Example #1
2
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")
	}
}
Example #2
0
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
}
Example #3
0
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()
}
Example #4
0
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()
}
Example #5
0
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)
}
Example #6
0
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)
}
Example #7
0
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])
		}
	}
}
Example #8
0
// 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)
}
Example #9
0
// 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)
}
Example #10
0
// 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)
	}
}
Example #11
0
// 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)
}
Example #12
0
// 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
	}
}