-Install paster itself:
-
- $ go get go.stargrave.org/paster
-
-Add "paster" user and "pastes" directory, also accessible by HTTP service:
-
- # pw useradd paster -s /usr/sbin/nologin -w no -d /path/to/pastes
- # mkdir /path/to/pastes
- # echo "<html><body>Paste service</body></html>" > /path/to/pastes/index.html
- # chown -R paster:lighttpd pastes
- # chmod 2750 pastes
- # chmod 640 /path/to/pastes/index.html
-
-Create daemontools+ucspi-tcp service:
-
- # mkdir -p /var/service/.paster/log/main
- # cat > /var/service/.paster/run <<EOF
- #!/bin/sh -e
- cd /path/to/pastes
- umask 027
- exec setuidgid paster tcpserver -DHR -l 0 ::0 2020 \
- timeout 1m $GOPATH/bin/paster http://paster.example.com/ 2>&1
- EOF
- # cat > /var/service/.paster/log/run <<EOF
- #!/bin/sh -e
- exec setuidgid paster multilog t ./main
- EOF
- # chmod +x /var/service/.paster/run /var/service/.paster/log/run
- # chown paster /var/service/.paster/log/main
- # mv /var/service/.paster /var/service/paster
-
-Optionally prepare X.509 certificate for TLS enabled service:
-
- # umask 077
- # certtool --generate-privkey --bits 256 --ecc --outfile \
- paster.example.com.key.pem
- # tmpl=`mktemp`
- # cat > $tmpl <<EOF
- dn = "cn=paster.example.com"
- expiration_days = 365
- signing_key
- dns_name = "paster.example.com"
- EOF
- # certtool --generate-self-signed \
- --load-privkey paster.example.com.key.pem \
- --template $tmpl --outfile paster.example.com.pem
- # rm $tmpl
- # chown paster:paster paster.example.com*.pem
- # chmod 600 paster.example.com.key.pem
-
-and choose from plenty of UCSPI-friendly TLS wrappers:
-http://www.fehcom.de/ipnet/ucspi-ssl.html, https://github.com/younix/ucspi
-or likely go.cypherpunks.ru/ucspi/cmd/tlss:
-
- exec setuidgid paster tcpserver -DHR -l 0 ::0 2021 tlss \
- -key paster.example.com.key.pem -cert paster.example.com.pem \
- timeout 1m $GOPATH/bin/paster http://paster.example.com/ 2>&1
+Look for doc/install.texi.
+++ /dev/null
-Protocol is very simple: bencoded dictionary is sent over TCP.
-
-* "v" key contains the data you want to paste
-* optional "e" key, holding the desired filename extension, without the
- leading dot, up to 9 characters long
-
-"hello world" => d1:v11:hello worlde
-"http://example.com/" and "url" extension => d1:e3:url1:v18:http://example.come
go.stargrave.org/paster -- paste service daemon
-
-Are you tired of too complicated solutions even for the simple task like
-paste storage? I too. That is why that paster daemon is here.
-
-* It uses file system for storing the pastes
-* Pastes are shared through the external program (HTTP, FTP, Gopher, ...)
-* Paste files have an extension (.txt by default), that can be overriden
- for the given paste. So your HTTP service can answer with varying
- Content-Types. You can share images for example, not only plaintext
-* No excessive HTTP protocol: just send bencode-ed dictionary with the
- data over the TCP
-* Newline is appended for .txt/.url pastes, if it is missing
-* SHA512/2 checksum is sent back to you, for integrity checking
-* Intended to be run as a UCSPI-TCP service
- (https://cr.yp.to/ucspi-tcp.html) under daemontools-like supervisor
- (http://cr.yp.to/daemontools.html)
-* You can clear an old ones with simple: find pastes -ctime XXX -delete
-
-With contrib/paster you can send the paste very easily:
-
- $ paster
- something I want to send
- whatever whenever
- ^D
- http://paster.example.com/4KEOLWCZY5CBVWDNT5TA.txt
- 28d95ef0e8d6a4f4222d0e7eb2a23777aa99efb0794e535a0f4a55490705438f
-
- $ paster webp < some-image.webp
- http://paster.example.com/KO5O7SJTUGBORVGOZBSA.webp
- 7f53424fe50f1d70fa32763cde31335dc82fd63c975e8ab95f0bb4a6cd94fb1c
-
- $ paster some-other-image.webp
- http://paster.example.com/O6D2O3N5HPH63ZFIYF4A.webp
- https://paster.example.com/O6D2O3N5HPH63ZFIYF4A.webp
- ftp://paster.example.com/pub/pastes/O6D2O3N5HPH63ZFIYF4A.webp
- 2ffe10846ec637d29ab9145b98c3699653c01910bb6d9e00e41f7fe02c5882a8
-
- $ grep whatever ... | paster
- $ import png:- | paster png
- $ import -define webp:lossless=true webp:- | paster webp
- $ echo http://example.com | paster url
- $ paster < /proc/cpuinfo
-
paster is free software: see the file COPYING for copying conditions.
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>asciicast</title>
+ <link rel="stylesheet" type="text/css" href="{{.PlayerPath}}.css" />
+ </head>
+ <body>
+ <asciinema-player src="{{.Cast}}"></asciinema-player>
+ <script src="{{.PlayerPath}}.js"></script>
+ <noscript>
+ No JavaScript support.
+ Download <a href="{{.Cast}}">asciicast</a> and play it with
+ <a href="https://pypi.org/project/asciinema/">asciinema</a>.
+ </noscript>
+ </body>
+</html>
--- /dev/null
+#!/bin/sh
+
+find . -type f -mtime +1 \
+ -and -not -name index.html \
+ -and -not -name "asciinema-player-*" \
+ -delete
--- /dev/null
+/paster.html
--- /dev/null
+@node Asciicast
+@unnumbered Asciicast
+
+You can optionally automatically generate HTML pages for your
+@file{.cast}s, if you copy
+@url{https://github.com/asciinema/asciinema-player/releases, asciinema-player}
+bundle to your HTTP server and specify its base name for
+@option{-asciicast-path}:
+
+@example
+$ v=v2.6.1
+$ u=https://github.com/asciinema/asciinema-player/releases/download/$v/
+$ for ext in css js ; do
+ wget -O /path/to/www/asciinema-player-$v.$ext $u/asciinema-player.$ext
+done
+
+use $GOPATH/bin/paster -asciicast-path asciinema-player-v2.6.1
+@end example
+
+After that, additional @file{.cast.html} file will be generated during
+upload each time, with the links to the @file{.cast} and the player. You
+will also get those HTML URLs in the output.
--- /dev/null
+@node Examples
+@unnumbered Examples
+
+With @command{contrib/paster} you can send the paste very easily:
+
+@itemize
+
+@item
+@example
+$ paster
+something I want to send
+whatever whenever
+^D
+http://paster.example.com/4KEOLWCZY5CBVWDNT5TA.txt
+SHA512/2: 28d95ef0e8d6a4f4222d0e7eb2a23777aa99efb0794e535a0f4a55490705438f
+@end example
+
+@item With overriden file extension
+@example
+$ paster webp < some-image.webp
+http://paster.example.com/KO5O7SJTUGBORVGOZBSA.webp
+SHA512/2: 7f53424fe50f1d70fa32763cde31335dc82fd63c975e8ab95f0bb4a6cd94fb1c
+@end example
+
+@item With file extension taken from the specified path. Daemon is run
+ with multiple URLs specified.
+
+@example
+$ paster some-other-image.webp
+http://paster.example.com/O6D2O3N5HPH63ZFIYF4A.webp
+https://paster.example.com/O6D2O3N5HPH63ZFIYF4A.webp
+ftp://paster.example.com/pub/pastes/O6D2O3N5HPH63ZFIYF4A.webp
+SHA512/2: 2ffe10846ec637d29ab9145b98c3699653c01910bb6d9e00e41f7fe02c5882a8
+@end example
+
+@item
+@example
+$ grep whatever ... | paster
+$ import png:- | paster png
+$ import -define webp:lossless=true webp:- | paster webp
+$ echo http://example.com | paster url
+$ paster < /proc/cpuinfo
+@end example
+
+@item Pasting the @url{https://asciinema.org/, asciicast} requires
+@file{.cast} extension. @file{.cast.html} URLs will appear.
+
+@example
+$ paster path/to/ascii.cast
+http://paster.example.com/ST4LOKGUISMACAAFC4CA.cast
+SHA512/2: 171e39f35b58b39f0bc2f3def59955d6573756374584d5443afa16d31032fdf3
+http://paster.example.com/ST4LOKGUISMACAAFC4CA.cast.html
+@end example
+
+@end itemize
--- /dev/null
+@node Features
+@unnumbered Features
+
+@itemize
+
+@item It uses file system for storing the pastes
+
+@item Pastes are shared through the external program (HTTP, FTP, Gopher, ...)
+
+@item Paste files have an extension (@file{.txt} by default), that can
+ be overriden for the given paste. So your HTTP service can answer
+ 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
+ with the data over the TCP
+
+@item Newline is appended for @file{.txt}/@file{.url} pastes, if it is missing
+
+@item SHA512/2 checksum is sent back to you, for integrity checking
+
+@item Intended to be run as a @url{https://cr.yp.to/ucspi-tcp.html, UCSPI-TCP}
+ service @url{http://cr.yp.to/daemontools.html, daemontools}-like supervisor
+
+@item Can automatically generate HTML files with the links for automatic
+ @url{https://asciinema.org/, asciicasts} playback.
+
+@item You can clear an old ones with simple:
+ @code{find pastes -ctime XXX -delete}
+@end itemize
--- /dev/null
+\input texinfo
+@documentencoding UTF-8
+@settitle paster
+
+@copying
+Copyright @copyright{} 2021-2022 @email{stargrave@@stargrave.org, Sergey Matveev}
+@end copying
+
+@node Top
+@top paster
+
+Are you tired of too complicated solutions even for the simple task like
+paste storage? I too. That is why that @strong{paster daemon} is here.
+
+paster is
+@url{https://www.gnu.org/philosophy/pragmatic.html, copylefted}
+@url{https://www.gnu.org/philosophy/free-sw.html, free software}
+licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GNU GPLv3}.
+
+Please send questions, bug reports and patches to @url{paster@@stargrave.org}.
+
+@insertcopying
+
+@include features.texi
+@include examples.texi
+@include install.texi
+@include asciicast.texi
+@include protocol.texi
+
+@bye
--- /dev/null
+@node Install
+@unnumbered Install
+
+@itemize
+
+@item
+Install paster itself:
+
+@example
+$ go get go.stargrave.org/paster
+@end example
+
+If you have got problems with your trust anchors, unwilling to
+authenticate @code{go.stargrave.org}'s TLS connection, then clone the
+repository from @url{git://git.stargrave.org/paster.git} and build it
+as ordinary Go package with @code{go build}.
+
+@item
+Add @code{paster} user and @code{pastes} directory, also accessible by
+HTTP service (@url{http://www.godlighty.stargrave.org/, @code{godlighty}}
+user in current example):
+
+@example
+# pw useradd paster -s /usr/sbin/nologin -w no -d /path/to/pastes
+# mkdir /path/to/pastes
+# cat > /path/to/pastes/index.html <<EOF
+<!DOCTYPE html>
+<html>
+ <head><title>paster</title></head>
+ <body>Paste service.</body>
+</html>
+EOF
+# chown -R paster:godlighty pastes
+# chmod 2750 pastes
+# chmod 640 /path/to/pastes/index.html
+@end example
+
+@item
+Create @url{http://cr.yp.to/daemontools.html, daemontools} +
+@url{https://cr.yp.to/ucspi-tcp.html, UCSPI-TCP} service:
+
+@example
+# mkdir -p /var/service/.paster/log/main
+
+# cat > /var/service/.paster/run <<EOF
+#!/bin/sh -e
+cd /path/to/pastes
+umask 027
+exec setuidgid paster tcpserver -DHR -l 0 ::0 2020 \
+ timeout 1m $GOPATH/bin/paster http://paster.example.com/ 2>&1
+EOF
+
+# cat > /var/service/.paster/log/run <<EOF
+#!/bin/sh -e
+exec setuidgid paster multilog t ./main
+EOF
+
+# chmod +x /var/service/.paster/run /var/service/.paster/log/run
+# chown paster /var/service/.paster/log/main
+# mv /var/service/.paster /var/service/paster
+@end example
+
+@item
+Optionally prepare X.509 certificate for TLS enabled service:
+
+@example
+# umask 077
+# certtool --generate-privkey --bits 256 --ecc --outfile \
+ paster.example.com.key.pem
+
+# tmpl=`mktemp`
+# cat > $tmpl <<EOF
+dn = "cn=paster.example.com"
+expiration_days = 365
+signing_key
+dns_name = "paster.example.com"
+EOF
+
+# certtool --generate-self-signed \
+ --load-privkey paster.example.com.key.pem \
+ --template $tmpl --outfile paster.example.com.pem
+# rm $tmpl
+
+# chown paster:paster paster.example.com*.pem
+# chmod 600 paster.example.com.key.pem
+@end example
+
+and choose from plenty of UCSPI-friendly TLS wrappers:
+@url{http://www.fehcom.de/ipnet/ucspi-ssl.html},
+@url{https://github.com/younix/ucspi}
+or likely the @code{go.cypherpunks.ru/ucspi/cmd/tlss}:
+
+@example
+exec setuidgid paster tcpserver -DHR -l 0 ::0 2021 tlss \
+ -key paster.example.com.key.pem -cert paster.example.com.pem \
+ timeout 1m $GOPATH/bin/paster http://paster.example.com/ 2>&1
+@end example
+
+@item
+Be sure that your HTTP/whatever server uses proper @code{Content-Type}
+based on filename's extension (@code{text/plain} for @file{.txt},
+@code{image/jxl} for @file{.jxl} and so on).
+
+@end itemize
--- /dev/null
+@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
+
+@example
+"hello world" => d1:v11:hello worlde
+"http://example.com/" and "url" extension => d1:e3:url1:v18:http://example.come
+@end example
--- /dev/null
+body { background-color: #AEBECE }
+h1, h2, h3, h4 { text-align: center }
+h1, h2, h3, h4, strong { color: #900090 }
+pre { background-color: #CCCCCC }
+table, th, td { border: 1px solid black ; border-collapse: collapse }
--- /dev/null
+redo-ifchange *.texi
+html=paster.html
+rm -f $html/*.html
+${MAKEINFO:=makeinfo} --html \
+ --css-include style.css \
+ --set-customization-variable SECTION_NAME_IN_TITLE=1 \
+ --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \
+ --set-customization-variable FORMAT_MENU=menu \
+ --set-customization-variable SHOW_TITLE=0 \
+ --set-customization-variable DATE_IN_HEADER=1 \
+ --set-customization-variable CLOSE_QUOTE_SYMBOL=\" \
+ --set-customization-variable OPEN_QUOTE_SYMBOL=\" \
+ -o $html index.texi
+find $html -type d -exec chmod 755 {} +
+find $html -type f -exec chmod 644 {} +
"bytes"
"crypto/rand"
"crypto/sha512"
+ _ "embed"
"encoding/base32"
"encoding/hex"
"flag"
"fmt"
+ "html/template"
"io"
"os"
"strconv"
)
+var (
+ //go:embed asciicast.tmpl
+ ASCIICastHTMLTmplRaw string
+ ASCIICastHTMLTmpl = template.Must(template.New("asciicast").Parse(
+ ASCIICastHTMLTmplRaw,
+ ))
+)
+
func fatal(s string) {
fmt.Println(s)
os.Exit(1)
}
+func asciicastHTML(playerPath, cast string) error {
+ var buf bytes.Buffer
+ err := ASCIICastHTMLTmpl.Execute(&buf, struct {
+ PlayerPath string
+ Cast string
+ }{
+ PlayerPath: playerPath,
+ Cast: cast,
+ })
+ if err != nil {
+ return err
+ }
+ fn := cast + ".html"
+ fd, err := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
+ if err != nil {
+ return err
+ }
+ if _, err = fd.Write(buf.Bytes()); err != nil {
+ os.Remove(fn)
+ return err
+ }
+ return fd.Close()
+}
+
func main() {
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")
flag.PrintDefaults()
for _, u := range flag.Args() {
fmt.Println(u + fn[1:])
}
- fmt.Println(hex.EncodeToString(h.Sum(nil)[:512/2/8]))
+ fmt.Println("SHA512/2:", hex.EncodeToString(h.Sum(nil)[:512/2/8]))
+ if ext == ".cast" && *asciicastPath != "" {
+ if err = asciicastHTML(*asciicastPath, fn[1:]); err != nil {
+ goto Failed
+ }
+ for _, u := range flag.Args() {
+ fmt.Println(u + fn[1:] + ".html")
+ }
+ }
fmt.Fprintf(
os.Stderr,
"[%s]:%s %s %d\n",