232 lines
5.6 KiB
Go
232 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
valid "github.com/asaskevich/govalidator"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/twharmon/gouid"
|
|
"golang.org/x/crypto/blake2b"
|
|
"strings"
|
|
"tcp.ac/termbin"
|
|
)
|
|
|
|
func init() {
|
|
termbin.UseChannel = true
|
|
}
|
|
|
|
func incoming() {
|
|
var msg termbin.Message
|
|
|
|
select {
|
|
case msg = <-termbin.Msg:
|
|
switch msg.Type {
|
|
case "ERROR":
|
|
log.Error().
|
|
Str("RemoteAddr", msg.RAddr).
|
|
Int("Size", msg.Size).
|
|
Msg(msg.Content)
|
|
case "INCOMING_DATA":
|
|
log.Debug().
|
|
Str("RemoteAddr", msg.RAddr).
|
|
Int("Size", msg.Size).
|
|
Msg("termbin_data")
|
|
case "FINISH":
|
|
log.Debug().
|
|
Str("RemoteAddr", msg.RAddr).
|
|
Int("Size", msg.Size).
|
|
Msg(msg.Content)
|
|
|
|
case "DEBUG":
|
|
log.Debug().
|
|
Str("RemoteAddr", msg.RAddr).
|
|
Int("Size", msg.Size).
|
|
Msg(msg.Content)
|
|
|
|
case "FINAL":
|
|
log.Debug().
|
|
Str("RemoteAddr", msg.RAddr).
|
|
Int("Size", msg.Size).
|
|
Msg(msg.Content)
|
|
|
|
termPost(msg.Bytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
func termPost(b []byte) {
|
|
slog := log.With().Str("caller", "termPost").Logger()
|
|
Hashr, _ := blake2b.New(64, nil)
|
|
Hashr.Write(b)
|
|
hash := Hashr.Sum(nil)
|
|
if ogTxt, _ := hashDB.Get(hash); ogTxt != nil {
|
|
if txtDB.Has(ogTxt) {
|
|
slog.Debug().Str("ogUid", string(ogTxt)).Msg("duplicate file found! returning original URL")
|
|
post := &Post{
|
|
Type: "t",
|
|
Uid: string(ogTxt),
|
|
Key: "",
|
|
Priv: false,
|
|
}
|
|
termbin.Reply <- termbin.Message{Type: "URL", Content: post.URLString()}
|
|
return
|
|
}
|
|
}
|
|
|
|
// generate new uid and delete key
|
|
uid := gouid.String(uidSize, gouid.MixedCaseAlphaNum)
|
|
key := gouid.String(keySize, gouid.MixedCaseAlphaNum)
|
|
|
|
// lets make sure that we don't clash even though its highly unlikely
|
|
for uidRef, _ := txtDB.Get([]byte(uid)); uidRef != nil; {
|
|
slog.Info().Msg(" uid already exists! generating new...")
|
|
uid = gouid.String(uidSize, gouid.MixedCaseAlphaNum)
|
|
}
|
|
for keyRef, _ := keyDB.Get([]byte(key)); keyRef != nil; {
|
|
slog.Info().Msg(" delete key already exists! generating new...")
|
|
key = gouid.String(keySize, gouid.MixedCaseAlphaNum)
|
|
}
|
|
|
|
hashDB.Put(hash, []byte(uid))
|
|
|
|
uid = gouid.String(uidSize, gouid.MixedCaseAlphaNum)
|
|
key = gouid.String(keySize, gouid.MixedCaseAlphaNum)
|
|
|
|
for uidRef, _ := txtDB.Get([]byte(uid)); uidRef != nil; {
|
|
slog.Info().Msg(" uid already exists! generating new...")
|
|
uid = gouid.String(uidSize, gouid.MixedCaseAlphaNum)
|
|
}
|
|
for keyRef, _ := keyDB.Get([]byte(key)); keyRef != nil; {
|
|
slog.Info().Msg(" delete key already exists! generating new...")
|
|
key = gouid.String(keySize, gouid.MixedCaseAlphaNum)
|
|
}
|
|
|
|
hashDB.Put([]byte(hash), []byte(uid))
|
|
|
|
err = txtDB.Put([]byte(uid), b)
|
|
if err != nil {
|
|
slog.Error().Err(err).Msg("failed to save text!")
|
|
termbin.Reply <- termbin.Message{Type: "ERROR", Content: "internal server error"}
|
|
return
|
|
}
|
|
err = keyDB.Put([]byte(key), []byte("t."+uid))
|
|
if err != nil {
|
|
slog.Error().Msg("failed to save delete key!")
|
|
termbin.Reply <- termbin.Message{Type: "ERROR", Content: "internal server error"}
|
|
return
|
|
}
|
|
|
|
slog.Debug().Str("uid", uid).Msg("saved to database successfully, sending to Serve")
|
|
|
|
post := &Post{
|
|
Type: "t",
|
|
Uid: uid,
|
|
Key: key,
|
|
Priv: false,
|
|
}
|
|
|
|
termbin.Reply <- termbin.Message{Type: "URL", Content: post.URLString()}
|
|
}
|
|
|
|
func txtView(c *gin.Context) {
|
|
slog := log.With().Str("caller", "txtView").Logger()
|
|
|
|
raddr, _ := c.RemoteIP()
|
|
if termbin.Rater.Check(&termbin.TermbinSource{Actual: raddr}) {
|
|
errThrow(c, 429, "ratelimited", "too many requests")
|
|
return
|
|
}
|
|
|
|
sUid := strings.Split(c.Param("uid"), ".")
|
|
rUid := sUid[0]
|
|
fExt = ""
|
|
|
|
if len(sUid) > 1 {
|
|
fExt = strings.ToLower(sUid[1])
|
|
if fExt != "txt" {
|
|
slog.Error().Msg("bad file extension")
|
|
errThrow(c, 400, "400", "400")
|
|
return
|
|
}
|
|
}
|
|
|
|
// if it doesn't match the key size or it isn't alphanumeric - throw it out
|
|
if !valid.IsAlphanumeric(rUid) || len(rUid) != uidSize {
|
|
slog.Error().Msg("request discarded as invalid") // these limits should be variables eventually
|
|
errThrow(c, 400, "400", "400")
|
|
return
|
|
}
|
|
|
|
// query bitcask for the id
|
|
fBytes, _ := txtDB.Get([]byte(rUid))
|
|
if fBytes == nil {
|
|
slog.Error().Str("rUid", rUid).Msg("no corresponding file for this id")
|
|
errThrow(c, 404, "404", "file not found")
|
|
return
|
|
}
|
|
|
|
file, _ := termbin.Deflate(fBytes)
|
|
c.Data(200, "text/plain", file)
|
|
|
|
slog.Info().Str("rUid", rUid).Msg("success")
|
|
}
|
|
|
|
func txtDel(c *gin.Context) {
|
|
slog := log.With().
|
|
Str("caller", "txtDel").Logger()
|
|
slog.Debug().Msg("new_request")
|
|
if !validateKey(c.Param("key")) {
|
|
errThrow(c, 400, "400", "400")
|
|
return
|
|
}
|
|
|
|
rKey := c.Param("key")
|
|
|
|
targetTxt, _ := keyDB.Get([]byte(rKey))
|
|
if targetTxt == nil || !strings.Contains(string(targetTxt), "t.") {
|
|
slog.Warn().Str("rkey", rKey).Msg("no txt delete entry found with provided key")
|
|
errThrow(c, 400, "400", "400")
|
|
return
|
|
}
|
|
|
|
t := strings.Split(string(targetTxt), ".")[1]
|
|
|
|
if !txtDB.Has([]byte(t)) {
|
|
slog.Warn().Str("rkey", rKey).Msg("corresponding image not found in database!")
|
|
errThrow(c, 500, "500", "500") // this shouldn't happen...?
|
|
return
|
|
}
|
|
if err := txtDB.Delete([]byte(t)); err != nil {
|
|
slog.Error().Str("rkey", t).Msg("delete failed!")
|
|
errThrow(c, 500, "500", "500")
|
|
return
|
|
}
|
|
|
|
if txtDB.Has([]byte(t)) {
|
|
slog.Error().Str("rkey", t).Msg("delete failed!?")
|
|
errThrow(c, 500, "500", "500")
|
|
return
|
|
}
|
|
|
|
slog.Info().Str("rkey", t).Msg("Text file deleted successfully")
|
|
slog.Debug().Str("rkey", t).Msg("Removing delete key entry")
|
|
err = keyDB.Delete([]byte(rKey))
|
|
if err != nil {
|
|
slog.Error().Str("rkey", t).Msg("Couldn't delete key")
|
|
}
|
|
c.JSON(200, "DELETE_SUCCESS")
|
|
}
|
|
|
|
//func serveTermbin() {
|
|
func serveTermbin() {
|
|
go func() {
|
|
for {
|
|
incoming()
|
|
}
|
|
}()
|
|
err := termbin.Listen("", txtPort)
|
|
if err != nil {
|
|
println(err.Error())
|
|
return
|
|
}
|
|
}
|