2017-04-14 16:15:07 -06:00
|
|
|
package routes
|
|
|
|
|
|
|
|
import (
|
2017-05-22 11:13:47 -06:00
|
|
|
"bufio"
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/base64"
|
2017-04-14 16:15:07 -06:00
|
|
|
"encoding/json"
|
|
|
|
"github.com/gorilla/mux"
|
2017-05-22 11:13:47 -06:00
|
|
|
"golang.org/x/sys/unix"
|
2017-04-14 16:15:07 -06:00
|
|
|
"io"
|
2017-05-22 11:13:47 -06:00
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
2017-04-14 16:15:07 -06:00
|
|
|
"os"
|
2017-05-22 11:13:47 -06:00
|
|
|
"sour.is/x/httpsrv"
|
|
|
|
"sour.is/x/log"
|
2017-04-14 16:15:07 -06:00
|
|
|
"strconv"
|
2017-04-24 09:20:46 -06:00
|
|
|
"strings"
|
|
|
|
"time"
|
2017-04-14 16:15:07 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
var store string
|
|
|
|
var randBytes int
|
|
|
|
|
|
|
|
func init() {
|
2017-04-24 09:20:46 -06:00
|
|
|
httpsrv.RegisterModule("paste", setConfig)
|
|
|
|
|
2017-04-14 16:15:07 -06:00
|
|
|
httpsrv.HttpRegister("paste", httpsrv.HttpRoutes{
|
2017-05-22 11:13:47 -06:00
|
|
|
{"getRandom", "GET", "/paste/rng", getRandom},
|
|
|
|
{"getPaste", "GET", "/paste/{id}", getPaste},
|
|
|
|
{"getPaste", "GET", "/paste/get/{id}", getPaste},
|
|
|
|
{"postPaste", "POST", "/paste", postPaste},
|
|
|
|
|
|
|
|
{"getRandom", "GET", "/api/rng", getRandom},
|
|
|
|
{"getPaste", "GET", "/api/{id}", getPaste},
|
|
|
|
{"getPaste", "GET", "/api/get/{id}", getPaste},
|
|
|
|
{"postPaste", "POST", "/api", postPaste},
|
2017-04-24 09:20:46 -06:00
|
|
|
})
|
|
|
|
|
|
|
|
httpsrv.AssetRegister("paste", httpsrv.AssetRoutes{
|
2017-05-22 11:13:47 -06:00
|
|
|
{"Assets", "/", httpsrv.FsHtml5(assetFS())},
|
2017-04-14 16:15:07 -06:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-05-22 11:13:47 -06:00
|
|
|
func setConfig(config map[string]string) {
|
2017-04-14 16:15:07 -06:00
|
|
|
|
|
|
|
store = "data/"
|
|
|
|
if config["store"] != "" {
|
|
|
|
store = config["store"]
|
|
|
|
}
|
|
|
|
|
|
|
|
if !chkStore(store) {
|
2017-04-24 09:20:46 -06:00
|
|
|
log.Criticalf("[routes::Paste] Store location [%s] does not exist or is not writable.", store)
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
2017-04-24 09:20:46 -06:00
|
|
|
log.Noticef("[paste::getPaste] Store location set to [%s]", store)
|
2017-04-14 16:15:07 -06:00
|
|
|
|
|
|
|
randBytes = 1024
|
|
|
|
if config["random"] != "" {
|
|
|
|
randBytes, _ = strconv.Atoi(config["random"])
|
2017-04-24 09:20:46 -06:00
|
|
|
log.Noticef("[paste:getRandom] set random size to %d bytes", randBytes)
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func chkStore(path string) bool {
|
|
|
|
file, err := os.Stat(path)
|
2017-05-22 11:13:47 -06:00
|
|
|
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
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func chkFile(path string) bool {
|
|
|
|
file, err := os.Stat(path)
|
2017-05-22 11:13:47 -06:00
|
|
|
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
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func chkGone(path string) bool {
|
|
|
|
file, err := os.Stat(path)
|
2017-05-22 11:13:47 -06:00
|
|
|
if err != nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if file.Size() == 0 {
|
|
|
|
return true
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
func getRandom(w http.ResponseWriter, r *http.Request) {
|
2017-04-14 16:15:07 -06:00
|
|
|
s := make([]byte, randBytes)
|
|
|
|
rand.Read(s)
|
|
|
|
|
2017-05-22 11:13:47 -06:00
|
|
|
w.Header().Set("content-type", "application/octet-stream")
|
2017-04-24 09:20:46 -06:00
|
|
|
w.WriteHeader(http.StatusOK)
|
2017-04-14 16:15:07 -06:00
|
|
|
w.Write(s)
|
|
|
|
}
|
2017-04-24 09:20:46 -06:00
|
|
|
|
|
|
|
func getPaste(w http.ResponseWriter, r *http.Request) {
|
2017-04-14 16:15:07 -06:00
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
id := vars["id"]
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
if !chkFile(store + id) {
|
2017-05-22 11:13:47 -06:00
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
|
|
w.Write([]byte("ERR Not Found"))
|
|
|
|
return
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
if chkGone(store + id) {
|
2017-05-22 11:13:47 -06:00
|
|
|
w.WriteHeader(http.StatusGone)
|
|
|
|
w.Write([]byte("ERR Gone"))
|
2017-04-14 16:15:07 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
head, err := os.Open(store + id)
|
|
|
|
if err != nil {
|
2017-05-22 11:13:47 -06:00
|
|
|
log.Fatal(err)
|
2017-04-24 09:20:46 -06:00
|
|
|
}
|
|
|
|
defer head.Close()
|
|
|
|
|
|
|
|
keep := true
|
|
|
|
scanner := bufio.NewScanner(head)
|
|
|
|
for scanner.Scan() {
|
|
|
|
txt := scanner.Text()
|
|
|
|
log.Debug(txt)
|
|
|
|
|
2017-05-22 11:13:47 -06:00
|
|
|
if txt == "" {
|
2017-04-24 09:20:46 -06:00
|
|
|
break
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
2017-04-24 09:20:46 -06:00
|
|
|
|
|
|
|
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.Warning(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Debugf("%d > %d", now, exp)
|
|
|
|
if now > exp {
|
|
|
|
w.WriteHeader(http.StatusGone)
|
|
|
|
w.Write([]byte("ERR Gone"))
|
|
|
|
|
|
|
|
deleteFile(store + id)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
if strings.HasPrefix(txt, "burn:") {
|
|
|
|
burn := strings.TrimSpace(strings.TrimPrefix(txt, "burn:"))
|
|
|
|
|
|
|
|
if burn == "true" {
|
|
|
|
keep = false
|
|
|
|
}
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
file, _ := os.Open(store + id)
|
|
|
|
defer file.Close()
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
w.WriteHeader(http.StatusOK)
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
scanner = bufio.NewScanner(file)
|
|
|
|
for scanner.Scan() {
|
|
|
|
w.Write(scanner.Bytes())
|
|
|
|
w.Write([]byte("\n"))
|
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
if !keep {
|
|
|
|
deleteFile(store + id)
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
2017-04-24 09:20:46 -06:00
|
|
|
}
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
func postPaste(w http.ResponseWriter, r *http.Request) {
|
|
|
|
defer r.Body.Close()
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1048576))
|
2017-04-14 16:15:07 -06:00
|
|
|
checkErr(err, w)
|
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
s256 := sha256.Sum256(body)
|
|
|
|
id := base64.RawURLEncoding.EncodeToString(s256[12:])
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-05-22 11:13:47 -06:00
|
|
|
ioutil.WriteFile(store+id, body, 0644)
|
2017-04-14 16:15:07 -06:00
|
|
|
|
2017-04-24 09:20:46 -06:00
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
|
|
w.Write([]byte("OK " + id))
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusCreated)
|
2017-04-14 16:15:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func checkErr(err error, w http.ResponseWriter) {
|
|
|
|
if err != nil {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2017-05-22 11:13:47 -06:00
|
|
|
json.NewEncoder(w).Encode(err)
|
2017-04-14 16:15:07 -06:00
|
|
|
panic(err)
|
|
|
|
}
|
2017-04-24 09:20:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func deleteFile(path string) {
|
|
|
|
ioutil.WriteFile(path, []byte(""), 0644)
|
2017-05-22 11:13:47 -06:00
|
|
|
}
|