func DelGob(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: DelGob called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) params := r.URL.Query() token := params.Get(":token") if !validToken(token) { returnHTTPError(w, "DelGob", token+" not found", http.StatusNotFound) return } uid, err := store.TokenToUID(token) if err != nil { gslog.Error("HANDLER: delete gob failed with error: %s", err.Error()) returnHTTPError(w, "DelGob", "failed to delete gob", http.StatusInternalServerError) return } if uid == "" { returnHTTPError(w, "DelGob", token+" not found", http.StatusNotFound) return } err = store.DelGob(uid) if err != nil { gslog.Error("HANDLER: delete gob failed with error: %s", err.Error()) returnHTTPError(w, "DelGob", "failed to delete gob", http.StatusInternalServerError) return } pageType := getPageType(r) pageBytes, err := templ.GetMessPage(pageType, "successfully deleted "+uid) w.Write(pageBytes) }
func GetHorde(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: GetHorde called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) params := r.URL.Query() hordeName := params.Get(":horde") if !validHordeName(hordeName) { returnHTTPError(w, "GetHorde", hordeName+" not found", http.StatusNotFound) return } horde, err := store.GetHorde(hordeName) if err != nil { gslog.Error("HANDLER: failed to get horde with error: %s", err.Error()) returnHTTPError(w, "GetHorde", "failed to get horde", http.StatusInternalServerError) return } if len(horde) == 0 { returnHTTPError(w, "GetHorde", hordeName+" not found", http.StatusNotFound) return } pageType := getPageType(r) pageBytes, err := templ.GetHordePage(getScheme(r), pageType, hordeName, horde) if err != nil { gslog.Error("HANDLER: failed to get horde with error: %s", err.Error()) returnHTTPError(w, "GetHorde", "failed to get horde", http.StatusInternalServerError) return } w.Write(pageBytes) }
func PostGob(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: PostGob called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) gobData := getGobData(w, r) if len(gobData) == 0 { returnHTTPError(w, "PostGob", "gob empty", http.StatusBadRequest) return } ip := getIpAddress(r) uid, token, err := store.PutGob(gobData, ip) gslog.Debug("HANDLER: PostGob uid: %s, ip: %s, token: %s", uid, ip, token) if err != nil { gslog.Error("HANDLER: post gob failed with error: %s", err.Error()) returnHTTPError(w, "PostGob", "failed to save gob", http.StatusInternalServerError) return } pageType := getPageType(r) pageBytes, err := templ.GetURLPage(getScheme(r), pageType, uid, token) if err != nil { gslog.Error("HANDLER: post gob failed with error: %s", err.Error()) returnHTTPError(w, "GetHorde", "failed to save gob", http.StatusInternalServerError) return } w.Write(pageBytes) }
// setTTL sets the expire time for the uid based on the size. Expects to be // run in a goroutine, so it does not return an error. It instead logs it. // TODO: should I be passing the client in? func (redisStore *RedisStore) setTTLRoutine(client *pool.Client, gobInfo *storage.GobInfo, data []byte) { defer redisStore.Put(client) ttl := calculateTTL(data) if i, _ := client.Cmd("EXPIRE", gobKey(gobInfo.UID), ttl).Int(); i == 0 { gslog.Error("REDIS: could not set expire time for key '%s' to %d seconds", gobKey(gobInfo.UID), ttl) } if i, _ := client.Cmd("EXPIRE", gobInfoKey(gobInfo.UID), ttl).Int(); i == 0 { gslog.Error("REDIS: could not set expire time for key '%s' to %d seconds", gobInfoKey(gobInfo.UID), ttl) } if i, _ := client.Cmd("EXPIRE", tokenKey(gobInfo.Token), ttl).Int(); i == 0 { gslog.Error("REDIS: could not set expire time for key '%s' to %d seconds", tokenKey(gobInfo.Token), ttl) } }
func PostHordeGob(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: PostHordeGob called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) params := r.URL.Query() hordeName := params.Get(":horde") if !validHordeName(hordeName) { returnHTTPError(w, "PostHordeGob", "horde name can only contain up to "+strconv.Itoa(HORDE_MAX_LEN)+" alphanumeric characters", http.StatusNotFound) return } gobData := getGobData(w, r) if len(gobData) == 0 { returnHTTPError(w, "PostHordeGob", "gob empty", http.StatusBadRequest) return } ip := getIpAddress(r) uid, token, err := store.PutHordeGob(hordeName, gobData, ip) gslog.Debug("HANDLER: uid: %s, ip: %s", uid, ip) if err != nil { gslog.Error("HANDLER: put horde gob failed with error: %s", err.Error()) returnHTTPError(w, "PostHordeGob", "failed to save gob", http.StatusInternalServerError) return } pageType := getPageType(r) pageBytes, err := templ.GetURLPage(getScheme(r), pageType, uid, token) w.Write(pageBytes) }
func (redisStore *RedisStore) deleteExpireRoutine(client *pool.Client, key string) { err := deleteExpire(client, key) if err != nil { gslog.Error("REDIS: could not set expire time for deleted key '%s' with error: %s", key) return } redisStore.Put(client) }
func GetForm(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: GetForm called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) pageBytes, err := templ.GetFormPage(getScheme(r)) if err != nil { gslog.Error("HANDLER: failed to get form with error: %s", err.Error()) returnHTTPError(w, "GetRoot", "failed to get form", http.StatusInternalServerError) return } w.Write(pageBytes) }
func GetGob(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: GetGob called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) params := r.URL.Query() uid := params.Get(":uid") if !validUID(uid) { returnHTTPError(w, "GetGob", uid+" not found", http.StatusNotFound) return } data, _, err := store.GetGob(uid) if err != nil { gslog.Error("HANDLER: failed to get gob with error: " + err.Error()) returnHTTPError(w, "GetGob", "failed to get gob", http.StatusInternalServerError) return } if len(data) == 0 { returnHTTPError(w, "GetGob", uid+" not found", http.StatusNotFound) return } // Firgure out language parameter lang := getLanguage(r) if lang == "" { gslog.Debug("HANDLER: GetGob writing data") w.Write(data) return } contentType := http.DetectContentType(data) // If data is a valid content type for syntax highlighting if textContentTypeReg.MatchString(contentType) { data, err = templ.GetGobPage(lang, data) if err != nil { gslog.Error("HANDLER: failed to get gob page with error: %s", err.Error()) returnHTTPError(w, "GetGob", "failed to get gob", http.StatusInternalServerError) return } } w.Write(data) }
func AppendGob(w http.ResponseWriter, r *http.Request) { gslog.Debug("HANDLER: AppendGob called with header: %+v, host: %s, requestURI: %s, remoteAddr: %s", r.Header, r.Host, r.RequestURI, r.RemoteAddr) params := r.URL.Query() token := params.Get(":token") if !validToken(token) { returnHTTPError(w, "AppendGob", token+" not found", http.StatusNotFound) return } gobData := getGobData(w, r) if len(gobData) == 0 { returnHTTPError(w, "AppendGob", "gob empty", http.StatusBadRequest) return } ip := getIpAddress(r) uid, err := store.TokenToUID(token) gslog.Debug("HANDLER: AppendGob uid: %s, ip: %s, token: %s", uid, ip, token) if err != nil { gslog.Error("HANDLER: append gob failed with error: %s", err.Error()) returnHTTPError(w, "AppendGob", "failed to append gob", http.StatusInternalServerError) return } if uid == "" { returnHTTPError(w, "AppendGob", token+" not found", http.StatusNotFound) return } err = store.AppendGob(uid, gobData) if err != nil { gslog.Error("HANDLER: append gob failed with error: %s", err.Error()) returnHTTPError(w, "AppendGob", "failed to append gob", http.StatusInternalServerError) return } pageType := getPageType(r) // TODO: Should I tell them what gob they appended? Maybe a security flaw pageBytes, err := templ.GetMessPage(pageType, "successfully appended "+uid) w.Write(pageBytes) }
// deleteExpire modifies the key so that it is inaccessble via normal methods // and sets the TTL to a week func deleteExpire(client *pool.Client, key string) error { // Make gob inaccessble using normal key reply := client.Cmd("RENAME", key, deletedKey(key)) if reply.Err != nil { return reply.Err } reply = client.Cmd("EXPIRE", deletedKey(key), DEL_TTL) if reply.Err != nil { return reply.Err } if i, _ := reply.Int(); i == 0 { gslog.Error("REDIS: could not set expire time for deleted key '%s'", key) } return nil }
func GetGobPage(language string, data []byte) ([]byte, error) { if language == "markdown" { p := &MDPage{ Title: "gob: " + language + " syntax highlighted", Language: language, Data: htmlTemplate.HTML(string(data)), } return executeTemplate("HTML", "mdPage", p) } pygments.Binary(pygmentizePath) opts := pygments.Options{ "linenos": "table", "encoding": "utf-8", } code, err := pygments.Highlight(string(data), language, "html", opts) if err != nil { gslog.Error("Failed to highlight code: " + err.Error()) // Syntax highlighting has failed, so just display the raw data return data, nil } p := &GobPage{Title: "gob: " + language + " syntax highlighted", Language: language, Data: htmlTemplate.HTML(code)} return executeTemplate("HTML", "gobPage", p) }
func main() { //if conf.ShowVers { // println("Commit: " + buildCommit) // println("Date: " + buildDate) // os.Exit(0) //} gslog.Info("Goblin started [build commit: %s, build date: %s]", buildCommit, buildDate) if err := conf.Parse(); err != nil { gslog.Fatal("MAIN: failed to parse conf with error: %s", err.Error()) } gslog.SetMinimumLevel(conf.GetStr("loglevel")) if logFile := conf.GetStr("logfile"); logFile != "" { gslog.SetLogFile(logFile) } storeType, storeConf := conf.GetStr("storetype"), conf.GetStr("storeconf") uidLen, tokenLen := conf.GetInt("uidlength"), conf.GetInt("tokenlength") handler.Initialize(uidLen, tokenLen) if err := store.Initialize(storeType, storeConf, uidLen, tokenLen); err != nil { gslog.Fatal("MAIN: failed to initialize storage with error: %s", err.Error()) } htmlTemps, textTemps := conf.GetStr("htmltemplates"), conf.GetStr("texttemplates") domain := conf.GetStr("domain") pygmentizePath := conf.GetStr("pygmentizepath") if err := templ.Initialize(htmlTemps, textTemps, domain, pygmentizePath); err != nil { gslog.Fatal("MAIN: failed to initialize templates with error: %s", err.Error()) } // Setup route handlers mux := pat.New() mux.Get("/", http.HandlerFunc(handler.GetRoot)) mux.Get("/:uid", http.HandlerFunc(handler.GetGob)) mux.Get("/delete/:token", http.HandlerFunc(handler.DelGob)) mux.Post("/append/:token", http.HandlerFunc(handler.AppendGob)) mux.Get("/horde/:horde", http.HandlerFunc(handler.GetHorde)) mux.Get("/new/gob", http.HandlerFunc(handler.GetForm)) mux.Post("/", http.HandlerFunc(handler.PostGob)) // TODO: Should I post to /horde/:horde mux.Post("/:horde", http.HandlerFunc(handler.PostHordeGob)) http.Handle("/", mux) // Mandatory root-based resources staticPath := conf.GetStr("staticpath") serveSingle("/browserconfig.xml", filepath.Join(staticPath, "browserconfig.xml")) serveSingle("/robots.txt", filepath.Join(staticPath, "robots.txt")) serveSingle("/sitemap.xml", filepath.Join(staticPath, "sitemap.xml")) serveSingle("/apple-touch-icon-114x114.png", filepath.Join(staticPath, "img/apple-touch-icon-114x114.png")) serveSingle("/apple-touch-icon-120x120.png", filepath.Join(staticPath, "img/apple-touch-icon-120x120.png")) serveSingle("/apple-touch-icon-144x144.png", filepath.Join(staticPath, "img/apple-touch-icon-144x144.png")) serveSingle("/apple-touch-icon-152x152.png", filepath.Join(staticPath, "img/apple-touch-icon-152x152.png")) serveSingle("/apple-touch-icon-57x57.png", filepath.Join(staticPath, "img/apple-touch-icon-57x57.png")) serveSingle("/apple-touch-icon-60x60.png", filepath.Join(staticPath, "img/apple-touch-icon-60x60.png")) serveSingle("/apple-touch-icon-72x72.png", filepath.Join(staticPath, "img/apple-touch-icon-72x72.png")) serveSingle("/apple-touch-icon-76x76.png", filepath.Join(staticPath, "img/apple-touch-icon-76x76.png")) serveSingle("/apple-touch-icon.png", filepath.Join(staticPath, "img/apple-touch-icon.png")) serveSingle("/apple-touch-icon-precomposed.png", filepath.Join(staticPath, "img/apple-touch-icon-precomposed.png")) serveSingle("/favicon-160x160.png", filepath.Join(staticPath, "img/favicon-160x160.png")) serveSingle("/favicon-16x16.png", filepath.Join(staticPath, "img/favicon-16x16.png")) serveSingle("/favicon-196x196.png", filepath.Join(staticPath, "img/favicon-196x196.png")) serveSingle("/favicon-32x32.png", filepath.Join(staticPath, "img/favicon-32x32.png")) serveSingle("/favicon-96x96.png", filepath.Join(staticPath, "img/favicon-96x96.png")) serveSingle("/favicon.ico", filepath.Join(staticPath, "img/favicon.ico")) serveSingle("/mstile-144x144.png", filepath.Join(staticPath, "img/mstile-144x144.png")) serveSingle("/mstile-150x150.png", filepath.Join(staticPath, "img/mstile-150x150.png")) serveSingle("/mstile-310x150.png", filepath.Join(staticPath, "img/mstile-310x150.png")) serveSingle("/mstile-310x310.png", filepath.Join(staticPath, "img/mstile-310x310.png")) serveSingle("/mstile-70x70.png", filepath.Join(staticPath, "img/mstile-70x70.png")) // Normal static resources http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(staticPath)))) listenOn := conf.GetStr("listen") gslog.Info("MAIN: Listening on %s...", listenOn) c := make(chan error) go listenAndServer(listenOn, c) // Set up listening for os signals shutdownCh := make(chan os.Signal, 5) // TODO: What signals for Windows if any? signal.Notify(shutdownCh, syscall.SIGINT, syscall.SIGKILL) // Set up listening for os signals reloadCh := make(chan os.Signal, 5) signal.Notify(reloadCh, syscall.SIGUSR2) for { select { case <-reloadCh: gslog.Info("MAIN: reloading") if err := conf.Parse(); err != nil { gslog.Error("MAIN: failed to parse conf with error: %s", err.Error()) break } gslog.SetMinimumLevel(conf.GetStr("loglevel")) if logFile := conf.GetStr("logfile"); logFile != "" { gslog.SetLogFile(logFile) } storeConf = conf.GetStr("storeconf") uidLen, tokenLen = conf.GetInt("uidlength"), conf.GetInt("tokenlength") handler.Initialize(uidLen, tokenLen) store.Configure(storeConf, uidLen, tokenLen) htmlTemps, textTemps = conf.GetStr("htmltemplates"), conf.GetStr("texttemplates") domain = conf.GetStr("domain") pygmentizePath := conf.GetStr("pygmentizepath") if err := templ.Reload(htmlTemps, textTemps, domain, pygmentizePath); err != nil { gslog.Error("MAIN: failed to reload templates with error: %s", err.Error()) } case <-shutdownCh: gslog.Info("MAIN: Syscall recieved, shutting down...") gslog.Flush() os.Exit(0) case err := <-c: gslog.Error("MAIN: ListenAndServe: %s", err) gslog.Fatal("MAIN: Failed to start server, exiting...") } } }