func onCommand(bot *telebot.Bot, command string, args []string) { if command == "msg" && len(args) > 1 { user := config.GetUser(args[0]) if user.UID == config.NilUser.UID { log.Errorf("[Syscmds] Couldn't get an user with the name or UID %s", args[0]) } msg := connect(args[1:]) bot.SendMessage(user, "*[Sysadmin]* "+msg, util.Markdown) log.Infof("[Syscmds] Sent message %[1]s to %[2]s", msg, user.Name) } else if command == "broadcast" && len(args) > 0 { msg := connect(args) for _, user := range config.GetAllUsers() { bot.SendMessage(user, "*[Sysadmin Broadcast]* "+msg, util.Markdown) } log.Infof("[Syscmds] Broadcasted message %[1]s", msg) } else if command == "config" && len(args) > 0 { if strings.EqualFold(args[0], "save") { if !config.IndentConfig && len(args) > 1 && strings.EqualFold(args[0], "pretty") { config.IndentConfig = true config.Save() config.IndentConfig = false } else { config.Save() } } else if strings.EqualFold(args[0], "load") { config.Load() } } else if command == "stop" { Shutdown("Sysadmin") } }
func spam(id int, message string) error { resp, err := http.PostForm("http://ranssi.paivola.fi/story.php?id="+strconv.Itoa(id), url.Values{"comment": {message}}) if err != nil { log.Errorf("[Posts] Error posting message \"%[1]s\" to the Ranssi post with ID %[2]d:\n%[3]s", message, id, err) return errors.New("Failed to post message") } defer resp.Body.Close() _, err = ioutil.ReadAll(resp.Body) if err != nil { log.Errorf("[Posts] Failed to read response: %[1]s", err) return errors.New("Failed to read response") } return nil }
func updateNews() { if news != nil { err := news.Update() if err != nil { log.Errorf("[Posts] Failed to update front-page posts: %s", err) } } else { var err error news, err = rss.Fetch("http://ranssi.paivola.fi/rss.php") if err != nil { log.Errorf("[Posts] Failed to update front-page posts: %s", err) } } lastupdate = util.Timestamp() }
func init() { var err error news, err = rss.Fetch("http://ranssi.paivola.fi/rss.php") if err != nil { log.Errorf("[Posts] Failed to load front-page posts: %s", err) } lastupdate = util.Timestamp() }
func writeSuccess(w http.ResponseWriter, result string) { json, err := json.Marshal(Response{Result: result}) if err != nil { log.Errorf("Failed to marshal output json: %s", err) return } w.Write(json) }
func writeError(w http.ResponseWriter, errcode int, simple, errmsg string, args ...interface{}) { json, err := json.Marshal(Response{Error: simple, ErrorLong: fmt.Sprintf(errmsg, args...)}) if err != nil { log.Errorf("Failed to marshal output json: %s", err) return } w.WriteHeader(errcode) w.Write(json) }
func onCommand(command string, args []string) { if command == "remove" && len(args) > 1 { var err error if args[0] == "short" { err = data.DeleteShort(args[1]) } else if args[0] == "url" { err = data.DeleteURL(args[1]) } if err != nil { log.Errorf("Failed to delete: %s", err) } else { log.Infof("Successfully deleted all entries where %s=%s", args[0], args[1]) } } else if command == "set" && len(args) > 2 { err := data.InsertDirect(args[0], args[1], args[2]) if err != nil { log.Errorf("Failed to insert: %s", err) } else { log.Infof("Successfully inserted entry: (%s, %s, %s)", args[0], args[1], args[2]) } } }
// Get handles get requests func Get(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { w.Header().Add("Allow", "GET") w.WriteHeader(http.StatusMethodNotAllowed) return } path := r.URL.Path[1:] img, err := database.Query(path) if err == nil { date := time.Unix(img.Timestamp, 0).Format(config.DateFormat) r.URL.Path = r.URL.Path + "." + img.Format data.ImagePage{ ImageName: img.ImageName, ImageAddr: r.URL.String(), Uploader: img.Adder, Client: img.Client, Date: date, Index: strconv.Itoa(img.ID), }.Send(w) return } imgData, err := ioutil.ReadFile(config.ImageLocation + r.URL.Path) if err != nil { log.Errorf("Failed to read image at %[2]s requested by %[1]s: %[3]s", getIP(r), path, err) w.WriteHeader(http.StatusNotFound) return } w.WriteHeader(http.StatusOK) split := strings.Split(path, ".") if len(split) > 0 { img, err = database.Query(split[0]) if err == nil && len(img.Format) > 0 { w.Header().Set("Content-type", "image/"+img.Format) } else if len(split) > 1 { w.Header().Set("Content-type", "image/"+split[len(split)-1]) } } w.Write(imgData) }
// Delete handles delete requests func Delete(w http.ResponseWriter, r *http.Request) { var ip = getIP(r) if r.Method != "POST" { w.Header().Add("Allow", "POST") w.WriteHeader(http.StatusMethodNotAllowed) return } // Create a json decoder for the payload. decoder := json.NewDecoder(r.Body) var dfr DeleteForm // Decode the payload. err := decoder.Decode(&dfr) // Check if there was an error decoding. if err != nil || len(dfr.ImageName) == 0 || len(dfr.Username) == 0 || len(dfr.AuthToken) == 0 { log.Debugf("%[1]s sent an invalid delete request.", ip) w.WriteHeader(http.StatusBadRequest) return } err = auth.CheckAuthToken(dfr.Username, []byte(dfr.AuthToken)) // Check if the auth token was correct if err != nil { log.Debugf("%[1]s tried to authenticate as %[2]s with the wrong token.", ip, dfr.Username) output(w, GenericResponse{ Success: false, Status: "invalid-authtoken", StatusReadable: "The authentication token was incorrect. Please try logging in again.", }, http.StatusUnauthorized) return } data, err := database.Query(dfr.ImageName) if err != nil { log.Debugf("%[1]s@%[2]s attempted to delete an image that doesn't exist.", dfr.Username, ip, data.Adder) output(w, GenericResponse{Success: false, Status: "not-found", StatusReadable: "The image you requested to be deleted does not exist."}, http.StatusNotFound) return } if data.Adder != dfr.Username { log.Debugf("%[1]s@%[2]s attempted to delete an image uploaded by %[3]s.", dfr.Username, ip, data.Adder) output(w, GenericResponse{Success: false, Status: "no-permissions", StatusReadable: "The image you requested to be deleted was not uploaded by you."}, http.StatusForbidden) return } err = database.Remove(dfr.ImageName) if err != nil { log.Warnf("Error deleting %[4]s from the database (requested by %[1]s@%[2]s): %[3]s", dfr.Username, ip, err, dfr.ImageName) w.WriteHeader(http.StatusInternalServerError) return } err = os.Remove(config.ImageLocation + "/" + dfr.ImageName + "." + data.Format) if err != nil { // If the file just didn't exist, warn about the error. If the error was something else, cancel. if strings.HasSuffix(err.Error(), "no such file or directory") { log.Warnf("Error deleting %[3]s from the filesystem (requested by %[1]s@%[2]s): File not found", dfr.Username, ip, dfr.ImageName) } else { log.Errorf("Error deleting %[4]s from the filesystem (requested by %[1]s@%[2]s): %[3]s", dfr.Username, ip, err, dfr.ImageName) w.WriteHeader(http.StatusInternalServerError) return } } log.Debugf("%[1]s@%[2]s successfully deleted the image with the name %[3]s.", dfr.Username, ip, dfr.ImageName) output(w, GenericResponse{ Success: true, Status: "deleted", StatusReadable: "The image " + dfr.ImageName + " was successfully deleted.", }, http.StatusAccepted) }
// Insert handles insert requests func Insert(w http.ResponseWriter, r *http.Request) { var ip = getIP(r) if r.Method != "POST" { w.Header().Add("Allow", "POST") w.WriteHeader(http.StatusMethodNotAllowed) return } // Create a json decoder for the payload. decoder := json.NewDecoder(r.Body) var ifr InsertForm // Decode the payload. err := decoder.Decode(&ifr) // Check if there was an error decoding. if err != nil || len(ifr.Image) == 0 { log.Debugf("%[1]s sent an invalid insert request.", ip) w.WriteHeader(http.StatusBadRequest) return } // Fill out all non-necessary unfilled values. if len(ifr.ImageName) == 0 { ifr.ImageName = ImageName(5) } if len(ifr.ImageFormat) == 0 { ifr.ImageFormat = "png" } if len(ifr.Client) == 0 { ifr.Client = "Unknown Client" } if len(ifr.Username) == 0 || len(ifr.AuthToken) == 0 { // Username or authentication token not supplied. if config.RequireAuth { // The user is not logged in, but the config is set to require authentication, send error. log.Debugf("%[1]s tried to upload an image without authentication, even though authentication is required.", ip) output(w, GenericResponse{ Success: false, Status: "not-logged-in", StatusReadable: "This MIS server requires authentication. Please log in or register.", }, http.StatusUnauthorized) return } // The user is not logged in, but login is not required, set username to "anonymous" ifr.Username = "******" } else { // Username and authentication token supplied, check them. err = auth.CheckAuthToken(ifr.Username, []byte(ifr.AuthToken)) if err != nil { log.Debugf("%[1]s tried to authenticate as %[2]s with the wrong token.", ip, ifr.Username) output(w, GenericResponse{ Success: false, Status: "invalid-authtoken", StatusReadable: "Your authentication token was incorrect. Please try logging in again.", }, http.StatusUnauthorized) return } } // If the image already exists, make sure that the uploader is the owner of the image. var replace = false owner := database.GetOwner(ifr.ImageName) if len(owner) > 0 { if owner != ifr.Username || ifr.Username == "anonymous" { output(w, GenericResponse{ Success: false, Status: "already-exists", StatusReadable: "The requested image name is already in use by another user", }, http.StatusForbidden) log.Debugf("%[1]s@%[2]s attempted to override an image uploaded by %[3]s.", ifr.Username, ip, owner) return } replace = true } // Decode the base64 image from the JSON request. image, err := base64.StdEncoding.DecodeString(ifr.Image) if err != nil { output(w, GenericResponse{Success: false, Status: "invalid-image-encoding", StatusReadable: "The given image is not properly encoded in base64."}, http.StatusUnsupportedMediaType) log.Errorf("Error while decoding image from %[1]s@%[2]s: %[3]s", ifr.Username, ip, err) return } mimeType := http.DetectContentType(image) if !strings.HasPrefix(mimeType, "image/") { log.Debugf("%[1]s@%[2]s attempted to upload an image with an incorrect MIME type.", ifr.Username, ip, owner) output(w, GenericResponse{ Success: false, Status: "invalid-mime", StatusReadable: "The uploaded data is of an incorrect MIME type.", }, http.StatusUnsupportedMediaType) return } mimeType = mimeType[len("image/"):] // Write the image to disk. err = ioutil.WriteFile(config.ImageLocation+"/"+ifr.ImageName+"."+ifr.ImageFormat, image, 0644) if err != nil { log.Errorf("Error while saving image from %[1]s@%[2]s: %[3]s", ifr.Username, ip, err) w.WriteHeader(http.StatusInternalServerError) return } if !replace { // The image name has not been used. Insert it into the database. err = database.Insert(ifr.ImageName, ifr.ImageFormat, mimeType, ifr.Username, ip, ifr.Client, ifr.Hidden) if err != nil { log.Errorf("Error while inserting image from %[1]s@%[2]s into the database: %[3]s", ifr.Username, ip, err) output(w, GenericResponse{ Success: false, Status: "database-error", StatusReadable: "An internal server error occurred while attempting to save image information to the database.", }, http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError) return } log.Debugf("%[1]s@%[2]s successfully uploaded an image with the name %[3]s (new).", ifr.Username, ip, ifr.ImageName) output(w, GenericResponse{ Success: true, Status: "created", StatusReadable: "The image was successfully saved with the name " + ifr.ImageName, ImageName: ifr.ImageName, }, http.StatusCreated) } else { // The image name was in use. Update the data in the database. err = database.Update(ifr.ImageName, ifr.ImageFormat, mimeType, ip, ifr.Client, ifr.Hidden) if err != nil { log.Errorf("Error while updating data of image from %[1]s@%[2]s into the database: %[3]s", ifr.Username, ip, err) output(w, GenericResponse{ Success: false, Status: "database-error", StatusReadable: "An internal server error occurred while attempting to save image information to the database.", }, http.StatusInternalServerError) return } log.Debugf("%[1]s@%[2]s successfully uploaded an image with the name %[3]s (replaced).", ifr.Username, ip, ifr.ImageName) output(w, GenericResponse{ Success: true, Status: "replaced", StatusReadable: "The image was successfully saved with the name " + ifr.ImageName + ", replacing your previous image with the same name", ImageName: ifr.ImageName, }, http.StatusAccepted) } }
// Search handles search requests func Search(w http.ResponseWriter, r *http.Request) { var ip = getIP(r) if r.Method != "POST" { w.Header().Add("Allow", "POST") w.WriteHeader(http.StatusMethodNotAllowed) return } if !config.AllowSearch { log.Warnf("%[1]s attempted to execute a search, even though it's not allowed", ip) w.WriteHeader(http.StatusForbidden) return } // Create a json decoder for the payload. decoder := json.NewDecoder(r.Body) var sf SearchForm // Decode the payload. err := decoder.Decode(&sf) // Check if there was an error decoding. if err != nil || (len(sf.Format) == 0 && len(sf.Adder) == 0 && len(sf.Client) == 0 && sf.MinTime <= 0 && sf.MaxTime <= 0) { log.Debugf("%[1]s sent an invalid search request.", ip) w.WriteHeader(http.StatusBadRequest) return } if sf.MinTime <= 0 && sf.MaxTime > 0 { sf.MinTime = 1 } else if sf.MinTime > 0 && sf.MaxTime <= 0 { sf.MaxTime = time.Now().Unix() } var authenticated = false if len(sf.AuthToken) != 0 { err = auth.CheckAuthToken(sf.Adder, []byte(sf.AuthToken)) if err != nil { log.Debugf("%[1]s tried to authenticate as %[2]s with the wrong token.", ip, sf.Adder) output(w, SearchResponse{ Success: false, Status: "invalid-authtoken", StatusReadable: "The authentication token was incorrect. Please try logging in again.", }, http.StatusUnauthorized) return } authenticated = true } results, err := database.Search(sf.Format, sf.Adder, sf.Client, sf.MinTime, sf.MaxTime, authenticated) if err != nil { log.Errorf("Failed to execute search %[2]s by %[1]s: %[3]s", ip, sf.String(), err) w.WriteHeader(http.StatusInternalServerError) return } if authenticated { log.Debugf("%[3]s@%[1]s executed a search: %[2]s", ip, sf.String(), sf.Adder) } else { log.Debugf("%[1]s executed a search: %[2]s", ip, sf.String()) } output(w, SearchResponse{ Success: true, Status: "success", StatusReadable: fmt.Sprintf("Search completed with %d results", len(results)), Results: results, }, http.StatusOK) }
// Update the timetables from http://ranssi.paivola.fi/lj.php func Update() { // Get timetable page doc, err := util.HTTPGetAndParse("http://ranssi.paivola.fi/lj.php") // Check if there was an error if err != nil { // Print the error log.Errorf("[Timetables] Failed to update cache: %s", err) // Return return } // Find the timetable table header node ttnode := util.FindSpan("tr", "class", "header", doc) // Check if the node was found if ttnode != nil { dayentry := ttnode // Loop through the days in the timetable for day := 0; ; day++ { // Make sure the next day exists if dayentry.NextSibling == nil || dayentry.NextSibling.NextSibling == nil || dayentry.NextSibling.NextSibling.FirstChild == nil { break } // Get the first day node dayentry = dayentry.NextSibling.NextSibling var date util.Date // Get the date of this day dateraw := strings.Split(dayentry.FirstChild.NextSibling.LastChild.Data, ".") dateraw[0] = strings.Split(dateraw[0], "\n")[1] // Parse the day from the date dateday, err1 := strconv.Atoi(dateraw[0]) // Parse the month from the date datemonth, err2 := strconv.Atoi(dateraw[1]) // Parse the year from the date dateyear, err3 := strconv.Atoi(dateraw[2]) // If no errors came in parsing, create a Date struct from the parsed data // If there were errors, set the date to 1.1.1970 if err1 == nil && err2 == nil && err3 == nil { date = util.Date{Year: dateyear, Month: datemonth, Day: dateday} } else { date = util.Date{Year: 1970, Month: 1, Day: 1} } // Get the first lesson node in the day node entry := dayentry.FirstChild.NextSibling // Loop through the lessons on the day for lesson := 0; lesson < 9; lesson++ { // Make sure the next lesson exists if entry == nil || entry.NextSibling == nil || entry.NextSibling.NextSibling == nil { break } // Get the next lesson node entry = entry.NextSibling.NextSibling data := "tyhjää" // Check if the lesson contains anything if entry.FirstChild != nil { // Lesson data found; Try to parse it if entry.FirstChild.Type == html.TextNode { // Found lesson data directly under lesson node data = entry.FirstChild.Data } else if entry.FirstChild.Type == html.ElementNode { // Didn't find data directly under letimetable[day][lesson]sson node // Check for a child element node. if entry.FirstChild.FirstChild != nil { // Child element node found. Check if the child of that child is text. if entry.FirstChild.FirstChild.Type == html.TextNode { // Child of child is text, use it as the data. data = entry.FirstChild.FirstChild.Data } } } } // Uncomment to enable lesson parsing /*lsn := ParseLesson(data) if lsn != nil { if lsn.Course == 0 || lsn.Lesson == 0 { data = fmt.Sprintf(lang.LTranslate("english", "lesson-format.noncoursed"), lsn.Subject.Name, lsn.Subject.ShortName) } else { data = fmt.Sprintf(lang.LTranslate("english", "lesson-format.coursed"), lsn.Subject.Name, lsn.Subject.ShortName, lsn.Course, lsn.Lesson) } }*/ // Save the parsed data to the correct location. switch lesson { case 0: firstyear[day][0] = TimetableLesson{data, "Aamu", date, util.Time{Hours: 9, Minutes: 0}} case 1: firstyear[day][1] = TimetableLesson{data, "IP1", date, util.Time{Hours: 12, Minutes: 15}} case 2: firstyear[day][2] = TimetableLesson{data, "IP2", date, util.Time{Hours: 15, Minutes: 0}} case 3: firstyear[day][3] = TimetableLesson{data, "Ilta", date, util.Time{Hours: 19, Minutes: 0}} case 4: other[day] = TimetableLesson{data, "Muuta", date, util.Time{Hours: 0, Minutes: 0}} case 5: secondyear[day][0] = TimetableLesson{data, "Aamu", date, util.Time{Hours: 9, Minutes: 0}} case 6: secondyear[day][1] = TimetableLesson{data, "IP1", date, util.Time{Hours: 12, Minutes: 15}} case 7: secondyear[day][2] = TimetableLesson{data, "IP2", date, util.Time{Hours: 15, Minutes: 0}} case 8: secondyear[day][3] = TimetableLesson{data, "Ilta", date, util.Time{Hours: 19, Minutes: 0}} } } } lastupdate = util.Timestamp() } else { // Node not found, print error log.Errorf("[Timetables] Error updating: Failed to find timetable table header node!") lastupdate = 0 } }