1 // paster -- paste service
2 // Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
31 "go.cypherpunks.ru/netstring/v2"
32 "lukechampine.com/blake3"
38 //go:embed asciicast.tmpl
39 ASCIICastHTMLTmplRaw string
40 ASCIICastHTMLTmpl = template.Must(template.New("asciicast").Parse(
45 func fatal(s string) {
50 func asciicastHTML(playerPath, cast string) error {
52 err := ASCIICastHTMLTmpl.Execute(&buf, struct {
56 PlayerPath: playerPath,
63 fd, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
67 if _, err = fd.Write(buf.Bytes()); err != nil {
75 maxSize := flag.Uint64("max-size", 1<<20, "Maximal upload size")
76 asciicastPath := flag.String("asciicast-path", "", "Generate HTMLs for .cast asciicasts, specify \"asciinema-player-v2.6.1\"")
78 fmt.Fprintf(os.Stderr, "Usage: paster [options] URL [URL ...]\n")
82 if len(flag.Args()) == 0 {
86 r := netstring.NewReader(os.Stdin)
92 fatal("too long extension length")
94 data, err := io.ReadAll(r)
102 ext = "." + string(data)
114 rnd := make([]byte, 12)
115 if _, err = io.ReadFull(rand.Reader, rnd); err != nil {
118 fn := "." + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(rnd) + ext
119 fd, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
123 h := blake3.New(32, nil)
124 bfd := bufio.NewWriter(fd)
125 mw := io.MultiWriter(bfd, h)
126 buf := make([]byte, 1)
127 _, err = io.CopyN(mw, r, int64(size-1))
135 _, err = mw.Write(buf)
139 if (ext == ".txt" || ext == ".url") && buf[0] != '\n' {
140 err = bfd.WriteByte('\n')
153 err = os.Rename(fn, fn[1:])
157 for _, u := range flag.Args() {
158 fmt.Println(u + fn[1:])
160 fmt.Println("BLAKE3-256:", hex.EncodeToString(h.Sum(nil)))
161 if ext == ".cast" && *asciicastPath != "" {
162 if err = asciicastHTML(*asciicastPath, fn[1:]); err != nil {
165 for _, u := range flag.Args() {
166 fmt.Println(u + fn[1:] + ".html")
172 os.Getenv("TCPREMOTEIP"),
173 os.Getenv("TCPREMOTEPORT"),