X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=main.go;h=e3740f9a2ebac73e13d4f5a84fff63d64aee3d79;hb=0e2d904b168b0779e9131b31410daebd73764e28;hp=716c48eb3a91cf2fd0262e16088f3e76393ef75e;hpb=a98995541cd1445c34f677b7e14dc452365ffd4c;p=uploader.git diff --git a/main.go b/main.go index 716c48e..e3740f9 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,6 @@ /* uploader -- simplest form file uploader -Copyright (C) 2018-2020 Sergey Matveev +Copyright (C) 2018-2021 Sergey Matveev 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 @@ -19,6 +19,7 @@ package main import ( "bufio" + "bytes" "encoding/base64" "encoding/hex" "flag" @@ -32,16 +33,19 @@ import ( "net/http" "os" "os/exec" + "strconv" "strings" "time" + "go.cypherpunks.ru/recfile" + "go.cypherpunks.ru/tai64n" "golang.org/x/crypto/blake2b" "golang.org/x/net/netutil" ) const ( WriteBufSize = 1 << 20 - FileFieldName = "fileupload" + FileFieldName = "file" CommentFieldName = "comment" SendmailCmd = "/usr/sbin/sendmail" @@ -50,9 +54,15 @@ const ( var ( Index = template.Must(template.New("index").Parse(` Upload +Example command line usage: +
+$ curl -F file=@somedata.tar.gpg [-F comment="optional comment"] http://.../upload/
+$ b2sum -a blake2b somedata.tar.gpg # to verify checksum
+
-
- +
+
+

`)) @@ -60,10 +70,18 @@ var ( NotifyToAddr *string ) -func notify(filename, timestamp string, size int64, comment string) { +func notify(tai, filename string, size int64, comment string) { if *NotifyToAddr == "" { return } + var rec bytes.Buffer + w := recfile.NewWriter(&rec) + w.WriteFields( + recfile.Field{Name: "TAI64N", Value: tai}, + recfile.Field{Name: "Size", Value: strconv.FormatInt(size, 10)}, + recfile.Field{Name: "Filename", Value: filename}, + ) + w.WriteFieldMultiline("Comment", strings.Split(comment, "\n")) cmd := exec.Command(SendmailCmd, *NotifyToAddr) cmd.Stdin = io.MultiReader( strings.NewReader(fmt.Sprintf( @@ -77,16 +95,9 @@ Content-Transfer-Encoding: base64 `, *NotifyFromAddr, *NotifyToAddr, - mime.BEncoding.Encode("UTF-8", fmt.Sprintf( - "%s (%d KiB)", filename, size/1024, - )), + mime.BEncoding.Encode("UTF-8", fmt.Sprintf("%s (%d KiB)", filename, size/1024)), )), - strings.NewReader(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf( - "Timestamp: %s\nSize: %d bytes\nComment: %s\n", - timestamp, - size, - comment, - )))), + strings.NewReader(base64.StdEncoding.EncodeToString(rec.Bytes())), ) cmd.Run() } @@ -117,61 +128,81 @@ func upload(w http.ResponseWriter, r *http.Request) { if err != nil { panic(err) } - fn := time.Now().Format(time.RFC3339Nano) + t := time.Now() + ts := new(tai64n.TAI64N) + tai64n.FromTime(t, ts) + tai := ts.Encode()[1:] fnOrig := p.FileName() - fd, err := os.OpenFile(fn+".part", os.O_WRONLY|os.O_CREATE, 0600) + fd, err := os.OpenFile(tai+".part", os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) if err != nil { - log.Println(r.RemoteAddr, fn, fnOrig, err) + log.Println(r.RemoteAddr, tai, fnOrig, err) return } fdBuf := bufio.NewWriterSize(fd, WriteBufSize) mw := io.MultiWriter(fdBuf, h) n, err := io.Copy(mw, p) if err != nil { - log.Println(r.RemoteAddr, fn, fnOrig, err) + log.Println(r.RemoteAddr, tai, fnOrig, err) fd.Close() return } if n == 0 { - log.Println(r.RemoteAddr, fn, fnOrig, "empty") - os.Remove(fn + ".part") + log.Println(r.RemoteAddr, tai, fnOrig, "empty") + os.Remove(tai + ".part") fd.Close() fmt.Fprintf(w, "Empty file") return } if err = fdBuf.Flush(); err != nil { - log.Println(r.RemoteAddr, fn, fnOrig, err) + log.Println(r.RemoteAddr, tai, fnOrig, err) + fd.Close() + return + } + if err = fd.Sync(); err != nil { + log.Println(r.RemoteAddr, tai, fnOrig, err) fd.Close() return } fd.Close() sum := hex.EncodeToString(h.Sum(nil)) - if err = os.Rename(fn+".part", fn); err != nil { - log.Println(r.RemoteAddr, fn, fnOrig, n, sum, err) + if err = os.Rename(tai+".part", tai); err != nil { + log.Println(r.RemoteAddr, tai, fnOrig, n, sum, err) + return + } + var rec bytes.Buffer + wr := recfile.NewWriter(&rec) + if _, err = wr.WriteFields( + recfile.Field{Name: "TAI64N", Value: tai}, + recfile.Field{Name: "Size", Value: strconv.FormatInt(n, 10)}, + recfile.Field{Name: "Checksum", Value: sum}, + ); err != nil { + log.Println(r.RemoteAddr, tai, fnOrig, n, sum, err) return } - fmt.Fprintf(w, "Timestamp: %s\nBytes: %d\nBLAKE2b: %s\n", fn, n, sum) - log.Println(r.RemoteAddr, fn, fnOrig, n, sum) + io.Copy(w, &rec) + log.Println(r.RemoteAddr, tai, fnOrig, n, sum) p, err = mr.NextPart() if err != nil || p.FormName() != CommentFieldName { - go notify(fnOrig, fn, n, "") + go notify(fnOrig, tai, n, "") return } comment, err := ioutil.ReadAll(p) if err != nil || len(comment) == 0 { - go notify(fnOrig, fn, n, "") + go notify(tai, fnOrig, n, "") return } - ioutil.WriteFile(fn+".txt", comment, os.FileMode(0600)) - go notify(fnOrig, fn, n, string(comment)) + ioutil.WriteFile(tai+".txt", comment, os.FileMode(0666)) + go notify(tai, fnOrig, n, string(comment)) } func main() { bind := flag.String("bind", "[::]:8086", "Address to bind to") conns := flag.Int("conns", 2, "Maximal number of connections") - NotifyFromAddr = flag.String("notify-from", "uploader@example.com", "Address notifications are send to") - NotifyToAddr = flag.String("notify-to", "", "Address notifications are send from") + NotifyFromAddr = flag.String("notify-from", "uploader@example.com", "Address notifications are sent to") + NotifyToAddr = flag.String("notify-to", "", "Address notifications are sent from") flag.Parse() + log.SetFlags(log.Lshortfile) + log.SetOutput(os.Stdout) if len(*NotifyFromAddr) == 0 && len(*NotifyToAddr) > 0 { log.Fatalln("notify-from address can not be empty, if notify-to is set") }