-#!/bin/sh
+#!/bin/sh -e
-find . -type f -mtime +1 \
+exec find . -type f -mtime +1 \
-and -not -name index.html \
-and -not -name "asciinema-player-*" \
-delete
-#!/bin/sh
+#!/bin/sh -e
DST="gnutls-cli --logfile=/dev/null -p 2021 paster.example.com" paster $@
-#!/bin/sh
+#!/bin/sh -e
HOST=paster.example.com
DST=cat paster $@ |
tcpclient -DHR -l 0 $HOST 2021 tlsc -name $HOST sh -c "cat >&7 ; cat <&6"
-#!/bin/sh
+#!/bin/sh -e
DST=cat paster $@ |
tcpclient -DHR -l 0 paster.example.com 2020 sh -c "cat >&7 ; cat <&6"
#!/bin/sh -e
DST=${DST:-nc paster.example.com 2020}
+ext="0:,"
if [ -s "$1" ]; then
src="$1"
bn="${1##*/}"
_ext="${bn##*.}"
- [ "$bn" = "$_ext" ] || ext="1:e${#_ext}:$_ext"
+ [ "$bn" = "$_ext" ] || ext="${#_ext}:$_ext,"
else
src=`mktemp`
trap "rm -f $src" HUP PIPE INT QUIT TERM EXIT
cat > $src
- [ $# -eq 0 ] || ext="1:e${#1}:$1"
+ [ $# -eq 0 ] || ext="${#1}:$1,"
fi
-size=`perl -e 'print -s $ARGV[0]' $src`
-( echo -n "d${ext}1:v${size}:" ; cat $src ; echo -n e ) | $DST
+size=$(perl -e 'print -s $ARGV[0]' "$src")
+( echo -n "${ext}${size}:" ; cat "$src" ; echo -n , ) | $DST
set Port 2021
set GnuTLS 1
+set ext ""
set fn [lindex $argv 0]
set size 0
if {($argc > 0) && [file exists $fn]} {
- set ext [file extension $fn]
+ set ext [string trimleft [file extension $fn] .]
set fd [open $fn {RDONLY BINARY}]
set size [file size $fn]
} else {
set sock [socket $Host $Port]
chan configure $sock -encoding binary -translation binary
}
-puts -nonewline $sock "d"
-if {[info exists ext]} {
- set ext [string trimleft $ext .]
- puts -nonewline $sock [string cat "1:e" [string length $ext] ":$ext"]
-}
-puts -nonewline $sock "1:v$size:"
+
+puts -nonewline $sock [string cat [string length $ext] ":$ext,"]
+puts -nonewline $sock "$size:"
if {[info exists fd]} {
fcopy $fd $sock
close $fd
} {
puts -nonewline $sock $data
}
-puts -nonewline $sock "e"
+puts -nonewline $sock ","
flush $sock
while {[gets $sock line] >= 0} { puts $line }
close $sock
set -e
DST=${DST:-paster.example.com 2020}
+ext="0:,"
[[ -s "$1" ]] && {
src="$1"
bn=$src:t
e=${bn##*.}
- [[ $bn = $e ]] || ext="1:e${#e}:$e"
+ [[ $bn = $e ]] || ext="${#e}:$e,"
} || {
src=`mktemp`
trap "rm -f $src" HUP PIPE INT QUIT TERM EXIT
cat > $src
- [[ $# -eq 0 ]] || ext="1:e${#1}:$1"
+ [[ $# -eq 0 ]] || ext="${#1}:$1,"
}
zmodload -F zsh/stat b:zstat
size=`zstat +size $src`
zmodload zsh/net/tcp
ztcp ${=DST}
fd=$REPLY
-( print -n "d${ext}1:v${size}:" ; cat $src ; print -n e ) >&$fd
+( print -n "${ext}${size}:" ; cat $src ; print -n , ) >&$fd
cat <&$fd
ztcp -c $fd
with varying @code{Content-Types}. You can share images for example,
not only plaintext
-@item No excessive HTTP protocol: just send
- @url{https://en.wikipedia.org/wiki/Bencode, bencode}-ed dictionary
+@item No excessive HTTP protocol: just send two
+ @url{https://en.wikipedia.org/wiki/Netstring, netstring}-encoded strings
with the data over the TCP
@item Newline is appended for @file{.txt}/@file{.url} pastes, if it is missing
Install paster itself:
@example
-$ go get go.stargrave.org/paster
+$ go get go.stargrave.org/paster/v2
@end example
If you have got problems with your trust anchors, unwilling to
@node Protocol
@unnumbered Protocol
-Protocol is very simple: @url{https://en.wikipedia.org/wiki/Bencode, bencode}d
-dictionary is sent over TCP.
-
-@itemize
-@item @code{v} key contains the data you want to paste
-@item optional @code{e} key, holding the desired filename extension,
- without the leading dot, up to 9 characters long
-@end itemize
+Protocol is very simple: two
+@url{https://en.wikipedia.org/wiki/Netstring, netstring}s are sent over TCP.
+First one holds desired filename extension, without the leading dot, up to 9
+characters long. Second one is the paste data itself.
@example
-"hello world" => d1:v11:hello worlde
-"http://example.com/" and "url" extension => d1:e3:url1:v18:http://example.come
+ "hello world", no explicit extension => 0:,11:hello world,
+"http://example.com/", "url" extension => 3:url,18:http://example.com,
@end example
-module go.stargrave.org/paster
+module go.stargrave.org/paster/v2
-go 1.16
+go 1.17
+
+require go.cypherpunks.ru/netstring/v2 v2.4.0
--- /dev/null
+go.cypherpunks.ru/netstring/v2 v2.4.0 h1:qBOtHJj1hoCUpYkouuTurXl20R1IKnEkh+q7/J0TgZ4=
+go.cypherpunks.ru/netstring/v2 v2.4.0/go.mod h1:6YDx4gW414SmHdvSBMKbHaB2/7w9WZ04NQb7XIUV/pA=
"html/template"
"io"
"os"
- "strconv"
+
+ "go.cypherpunks.ru/netstring/v2"
)
+const MaxExtLen = 9
+
var (
//go:embed asciicast.tmpl
ASCIICastHTMLTmplRaw string
maxSize := flag.Uint64("max-size", 1<<20, "Maximal upload size")
asciicastPath := flag.String("asciicast-path", "", "Generate HTMLs for .cast asciicasts, specify \"asciinema-player-v2.6.1\"")
flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "Usage: paster [options] URL [...]\n")
+ fmt.Fprintf(os.Stderr, "Usage: paster [options] URL [URL ...]\n")
flag.PrintDefaults()
}
flag.Parse()
flag.Usage()
os.Exit(1)
}
- var fn string
- r := bufio.NewReader(os.Stdin)
- b, err := r.ReadByte()
+ r := netstring.NewReader(os.Stdin)
+ size, err := r.Next()
if err != nil {
fatal(err.Error())
}
- if b != 'd' {
- fatal("bad bencode: no dictionary start")
+ if size > MaxExtLen {
+ fatal("too long extension length")
}
- buf := make([]byte, 21)
- ext := ".txt"
- var size uint64
-AnotherKey:
- if _, err = io.ReadFull(r, buf[:3]); err != nil {
+ data, err := io.ReadAll(r)
+ if err != nil {
fatal(err.Error())
}
- switch s := string(buf[:3]); s {
- case "1:e":
- if _, err = io.ReadFull(r, buf[:2]); err != nil {
- fatal(err.Error())
- }
- 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 {
- fatal(err.Error())
- }
- ext = "." + string(buf[:extLen])
- goto AnotherKey
- case "1:v":
- n, err := r.Read(buf)
- if err != nil {
- fatal(err.Error())
- }
- i := bytes.IndexByte(buf[:n], ':')
- if i == -1 {
- fatal(`bad bencode: invalid "v" length`)
- }
- size, err = strconv.ParseUint(string(buf[:i]), 10, 64)
- if err != nil {
- fatal(err.Error())
- }
- if size == 0 {
- fatal("empty paste")
- }
- if size > *maxSize {
- fatal("too big")
- }
- buf = buf[i+1 : n]
- default:
- fatal("bad bencode: invalid key")
+ var ext string
+ if len(data) == 0 {
+ ext = ".txt"
+ } else {
+ ext = "." + string(data)
+ }
+ size, err = r.Next()
+ if err != nil {
+ fatal(err.Error())
+ }
+ if size == 0 {
+ fatal("empty paste")
+ }
+ if size > *maxSize {
+ fatal("too big")
}
rnd := make([]byte, 12)
if _, err = io.ReadFull(rand.Reader, rnd); err != nil {
fatal(err.Error())
}
- fn = "." + base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(rnd) + ext
+ 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 {
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 {
+ buf := make([]byte, 1)
+ _, err = io.CopyN(mw, r, int64(size-1))
+ if err != nil {
goto Failed
}
- if len(buf) == 0 {
- buf = append(buf, 0)
- } else {
- buf = buf[:1]
- }
- if _, err = mr.Read(buf); err != nil {
+ _, err = r.Read(buf)
+ if err != nil {
goto Failed
}
- if _, err = mw.Write(buf); err != nil {
+ _, err = mw.Write(buf)
+ if err != nil {
goto Failed
}
if (ext == ".txt" || ext == ".url") && buf[0] != '\n' {
- if err = bfd.WriteByte('\n'); err != nil {
+ err = bfd.WriteByte('\n')
+ if err != nil {
goto Failed
}
}
- if _, err = mr.Read(buf); err != nil {
- goto Failed
- }
- if buf[0] != 'e' {
- os.Remove(fn)
- fatal("bad bencode: no dictionary end")
- }
- if err = bfd.Flush(); err != nil {
+ err = bfd.Flush()
+ if err != nil {
goto Failed
}
- if err = fd.Close(); err != nil {
+ err = fd.Close()
+ if err != nil {
goto Failed
}
- if err = os.Rename(fn, fn[1:]); err != nil {
+ err = os.Rename(fn, fn[1:])
+ if err != nil {
goto Failed
}
for _, u := range flag.Args() {
fmt.Println(u + fn[1:])
}
- fmt.Println("SHA512/2:", hex.EncodeToString(h.Sum(nil)[:512/2/8]))
+ fmt.Println("SHA512/2:", hex.EncodeToString(h.Sum(nil)[:sha512.Size/2]))
if ext == ".cast" && *asciicastPath != "" {
if err = asciicastHTML(*asciicastPath, fn[1:]); err != nil {
goto Failed