/*
paster -- paste service
-Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2021-2022 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
"crypto/sha512"
"encoding/base32"
"encoding/hex"
- "errors"
"flag"
"fmt"
"io"
- "log"
"os"
"strconv"
- "time"
)
+func fatal(s string) {
+ fmt.Println(s)
+ os.Exit(1)
+}
+
func main() {
- maxSecs := flag.Uint("max-secs", 60, "Maximal time of aliveness (0=disable)")
maxSize := flag.Uint64("max-size", 1<<20, "Maximal upload size")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: paster [options] URL [...]\n")
flag.PrintDefaults()
}
flag.Parse()
- log.SetFlags(0)
- log.SetOutput(os.Stdout)
if len(flag.Args()) == 0 {
flag.Usage()
os.Exit(1)
}
- if *maxSecs > 0 {
- go func() {
- time.Sleep(time.Duration(*maxSecs) * time.Second)
- log.Fatalln(errors.New("max aliveness time is reached"))
- }()
- }
+ var fn string
r := bufio.NewReader(os.Stdin)
b, err := r.ReadByte()
if err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
if b != 'd' {
- log.Fatalln(errors.New("bad bencode: no dictionary start"))
+ fatal("bad bencode: no dictionary start")
}
buf := make([]byte, 21)
ext := ".txt"
var size uint64
AnotherKey:
if _, err = io.ReadFull(r, buf[:3]); err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
switch s := string(buf[:3]); s {
case "1:e":
if _, err = io.ReadFull(r, buf[:2]); err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
- extLen := 0
- switch s = string(buf[:2]); s {
- case "1:":
- extLen = 1
- case "2:":
- extLen = 2
- case "3:":
- extLen = 3
- case "4:":
- extLen = 4
- case "5:":
- extLen = 5
- case "6:":
- extLen = 6
- case "7:":
- extLen = 7
- case "8:":
- extLen = 8
- case "9:":
- extLen = 9
- default:
- log.Fatalln(errors.New("bad bencode: invalid \"e\" length"))
+ if buf[1] != ':' {
+ fatal(`bad bencode: invalid "e" format`)
+ }
+ extLen, err := strconv.Atoi(string(buf[:1]))
+ if err != nil {
+ fatal(err.Error())
}
if _, err = io.ReadFull(r, buf[:extLen]); err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
ext = "." + string(buf[:extLen])
goto AnotherKey
case "1:v":
n, err := r.Read(buf)
if err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
i := bytes.IndexByte(buf[:n], ':')
if i == -1 {
- log.Fatalln(errors.New("bad bencode: invalid \"v\" length"))
+ fatal(`bad bencode: invalid "v" length`)
}
size, err = strconv.ParseUint(string(buf[:i]), 10, 64)
if err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
if size == 0 {
- log.Fatalln(errors.New("empty paste"))
+ fatal("empty paste")
}
if size > *maxSize {
- log.Fatalln(errors.New("too big"))
+ fatal("too big")
}
buf = buf[i+1 : n]
default:
- log.Fatalln(errors.New("bad bencode: invalid key"))
+ fatal("bad bencode: invalid key")
}
rnd := make([]byte, 12)
if _, err = io.ReadFull(rand.Reader, rnd); err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
- fn := "." + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(rnd) +
+ fn = "." + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(rnd) +
ext
fd, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
if err != nil {
- log.Fatalln(err)
+ fatal(err.Error())
}
h := sha512.New()
bfd := bufio.NewWriter(fd)
mr := io.MultiReader(bytes.NewReader(buf), r)
mw := io.MultiWriter(bfd, h)
if _, err = io.CopyN(mw, mr, int64(size-1)); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if _, err = mr.Read(buf[:1]); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if _, err = mw.Write(buf[:1]); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if (ext == ".txt" || ext == ".url") && buf[0] != '\n' {
if err = bfd.WriteByte('\n'); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
}
if _, err = mr.Read(buf[:1]); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if buf[0] != 'e' {
os.Remove(fn)
- log.Fatalln(errors.New("bad bencode: no dictionary end"))
+ fatal("bad bencode: no dictionary end")
}
if err = bfd.Flush(); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if err = fd.Close(); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
if err = os.Rename(fn, fn[1:]); err != nil {
- os.Remove(fn)
- log.Fatalln(err)
+ goto Failed
}
for _, u := range flag.Args() {
fmt.Println(u + fn[1:])
os.Getenv("TCPREMOTEPORT"),
fn[1:], size,
)
+ return
+Failed:
+ os.Remove(fn)
+ fatal(err.Error())
}