]> Sergey Matveev's repositories - paster.git/blob - main.go
More paster examples
[paster.git] / main.go
1 /*
2 paster -- paste service
3 Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "bufio"
22         "bytes"
23         "crypto/rand"
24         "crypto/sha512"
25         "encoding/base32"
26         "encoding/hex"
27         "flag"
28         "fmt"
29         "io"
30         "os"
31         "strconv"
32         "time"
33 )
34
35 func fatal(s string) {
36         fmt.Println(s)
37         os.Exit(1)
38 }
39
40 func main() {
41         maxSecs := flag.Uint("max-secs", 60, "Maximal time of aliveness (0=disable)")
42         maxSize := flag.Uint64("max-size", 1<<20, "Maximal upload size")
43         flag.Usage = func() {
44                 fmt.Fprintf(os.Stderr, "Usage: paster [options] URL [...]\n")
45                 flag.PrintDefaults()
46         }
47         flag.Parse()
48         if len(flag.Args()) == 0 {
49                 flag.Usage()
50                 os.Exit(1)
51         }
52         var fn string
53         if *maxSecs > 0 {
54                 go func() {
55                         time.Sleep(time.Duration(*maxSecs) * time.Second)
56                         if fn != "" {
57                                 os.Remove(fn)
58                         }
59                         fatal("max aliveness time is reached")
60                 }()
61         }
62         r := bufio.NewReader(os.Stdin)
63         b, err := r.ReadByte()
64         if err != nil {
65                 fatal(err.Error())
66         }
67         if b != 'd' {
68                 fatal("bad bencode: no dictionary start")
69         }
70         buf := make([]byte, 21)
71         ext := ".txt"
72         var size uint64
73 AnotherKey:
74         if _, err = io.ReadFull(r, buf[:3]); err != nil {
75                 fatal(err.Error())
76         }
77         switch s := string(buf[:3]); s {
78         case "1:e":
79                 if _, err = io.ReadFull(r, buf[:2]); err != nil {
80                         fatal(err.Error())
81                 }
82                 if buf[1] != ':' {
83                         fatal(`bad bencode: invalid "e" format`)
84                 }
85                 extLen, err := strconv.Atoi(string(buf[:1]))
86                 if err != nil {
87                         fatal(err.Error())
88                 }
89                 if _, err = io.ReadFull(r, buf[:extLen]); err != nil {
90                         fatal(err.Error())
91                 }
92                 ext = "." + string(buf[:extLen])
93                 goto AnotherKey
94         case "1:v":
95                 n, err := r.Read(buf)
96                 if err != nil {
97                         fatal(err.Error())
98                 }
99                 i := bytes.IndexByte(buf[:n], ':')
100                 if i == -1 {
101                         fatal(`bad bencode: invalid "v" length`)
102                 }
103                 size, err = strconv.ParseUint(string(buf[:i]), 10, 64)
104                 if err != nil {
105                         fatal(err.Error())
106                 }
107                 if size == 0 {
108                         fatal("empty paste")
109                 }
110                 if size > *maxSize {
111                         fatal("too big")
112                 }
113                 buf = buf[i+1 : n]
114         default:
115                 fatal("bad bencode: invalid key")
116         }
117         rnd := make([]byte, 12)
118         if _, err = io.ReadFull(rand.Reader, rnd); err != nil {
119                 fatal(err.Error())
120         }
121         fn = "." + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(rnd) +
122                 ext
123         fd, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
124         if err != nil {
125                 fatal(err.Error())
126         }
127         h := sha512.New()
128         bfd := bufio.NewWriter(fd)
129         mr := io.MultiReader(bytes.NewReader(buf), r)
130         mw := io.MultiWriter(bfd, h)
131         if _, err = io.CopyN(mw, mr, int64(size-1)); err != nil {
132                 goto Failed
133         }
134         if _, err = mr.Read(buf[:1]); err != nil {
135                 goto Failed
136         }
137         if _, err = mw.Write(buf[:1]); err != nil {
138                 goto Failed
139         }
140         if (ext == ".txt" || ext == ".url") && buf[0] != '\n' {
141                 if err = bfd.WriteByte('\n'); err != nil {
142                         goto Failed
143                 }
144         }
145         if _, err = mr.Read(buf[:1]); err != nil {
146                 goto Failed
147         }
148         if buf[0] != 'e' {
149                 os.Remove(fn)
150                 fatal("bad bencode: no dictionary end")
151         }
152         if err = bfd.Flush(); err != nil {
153                 goto Failed
154         }
155         if err = fd.Close(); err != nil {
156                 goto Failed
157         }
158         if err = os.Rename(fn, fn[1:]); err != nil {
159                 goto Failed
160         }
161         for _, u := range flag.Args() {
162                 fmt.Println(u + fn[1:])
163         }
164         fmt.Println(hex.EncodeToString(h.Sum(nil)[:512/2/8]))
165         fmt.Fprintf(
166                 os.Stderr,
167                 "[%s]:%s %s %d\n",
168                 os.Getenv("TCPREMOTEIP"),
169                 os.Getenv("TCPREMOTEPORT"),
170                 fn[1:], size,
171         )
172         return
173 Failed:
174         os.Remove(fn)
175         fatal(err.Error())
176 }