go-paste/routes/paste.go.db

195 lines
4.0 KiB
Plaintext

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)
}