package routes import ( "sour.is/x/httpsrv" "os" "golang.org/x/sys/unix" "log" "net/http" "sour.is/x/ident" "crypto/rand" "encoding/base64" "crypto/sha256" "io/ioutil" "io" "bufio" "github.com/gorilla/mux" "strings" "time" "strconv" ) var store string var randBytes int func init() { httpsrv.RegisterModule("paste", SetConfig) httpsrv.IdentRegister("paste", httpsrv.IdentRoutes{ { "Paste", "GET", "/paste/rng", GetRandom, }, { "Paste", "GET", "/paste/{id}", GetPaste, }, { "Paste", "GET", "/paste/get/{id}", GetPaste, }, { "Paste", "POST", "/paste", PostPaste, }, { "Paste", "DELETE", "/paste/{id}", DeletePaste, }, { "Paste", "GET", "/api/rng", GetRandom, }, { "Paste", "GET", "/api/{id}", GetPaste, }, { "Paste", "GET", "/api/get/{id}", GetPaste, }, { "Paste", "POST", "/api", PostPaste, }, { "Paste", "DELETE", "/api/{id}", DeletePaste, }, }) } func SetConfig (config map[string]string) { store = "data/" if config["store"] != "" { store = config["store"] } if !chkStore(store) { log.Fatalf("[routes::Paste] Store location [%s] does not exist or is not writable.", store) } randBytes = 1024 if config["random"] != "" { randBytes, _ = strconv.Atoi(config["random"]) } } func chkStore(path string) bool { file, err := os.Stat(path) if err == nil { return true } if os.IsNotExist(err) { return false } if !file.IsDir() { return false } if unix.Access(path, unix.W_OK & unix.R_OK) != nil { return false } return true } func chkFile(path string) bool { file, err := os.Stat(path) if err == nil { return true } if os.IsNotExist(err) { return false } if file.IsDir() { return false } if unix.Access(path, unix.W_OK & unix.R_OK) != nil { return false } return true } func chkGone(path string) bool { file, err := os.Stat(path) if err != nil { return true } if file.Size() == 0 { return true } return false } func GetRandom(w http.ResponseWriter, r *http.Request, i ident.Ident) { w.WriteHeader(http.StatusOK) w.Header().Set("content-type","application/octet-stream") s := make([]byte, randBytes) rand.Read(s) w.Write(s) } func GetPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) { vars := mux.Vars(r) id := vars["id"] if !chkFile(store + id) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("ERR Not Found")) return } if chkGone(store + id) { w.WriteHeader(http.StatusGone) w.Write([]byte("ERR Gone")) return } head, err := os.Open(store + id) if err != nil { log.Fatal(err) } defer head.Close() keep := true scanner := bufio.NewScanner(head) for scanner.Scan() { txt := scanner.Text() log.Println(txt) if (txt == "") { break } if strings.HasPrefix(txt, "exp:") { now := time.Now().Unix() exp, err := strconv.ParseInt(strings.TrimSpace(strings.TrimPrefix(txt, "exp:")), 10, 64) if err != nil { log.Print(err) } if now > exp { log.Printf("%d > %d", now, exp) w.WriteHeader(http.StatusGone) w.Write([]byte("ERR Gone")) Delete(store + id) return } } if strings.HasPrefix(txt, "burn:") { burn := strings.TrimSpace(strings.TrimPrefix(txt, "burn:")) if burn == "true" { keep = false } } } file, _ := os.Open(store + id) defer file.Close() w.WriteHeader(http.StatusOK) scanner = bufio.NewScanner(file) for scanner.Scan() { w.Write(scanner.Bytes()) w.Write([]byte("\n")) } if !keep { Delete(store + id) } } func PostPaste(w http.ResponseWriter, r *http.Request, i ident.Ident) { body, _ := ioutil.ReadAll(io.LimitReader(r.Body, 1048576)) s256 := sha256.Sum256(body) id := base64.RawURLEncoding.EncodeToString(s256[12:]) ioutil.WriteFile(store + id, body, 0644) w.WriteHeader(http.StatusCreated) w.Write([]byte("OK " + id)) } func DeletePaste(w http.ResponseWriter, r *http.Request, i ident.Ident) { vars := mux.Vars(r) id := vars["id"] Delete(store + id) w.WriteHeader(http.StatusNoContent) w.Write([]byte("OK")) } func Delete(path string) { ioutil.WriteFile(path, []byte(""), 0644) }