INSTALL | 58 +----------------------------------------------------
PROTOCOL | 8 --------
README | 43 -------------------------------------------
asciicast.tmpl | 16 ++++++++++++++++
contrib/clean.sh | 6 ++++++
doc/.gitignore | 1 +
doc/asciicast.texi | 22 ++++++++++++++++++++++
doc/examples.texi | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
doc/features.texi | 31 +++++++++++++++++++++++++++++++
doc/index.texi | 30 ++++++++++++++++++++++++++++++
doc/install.texi | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++
doc/protocol.texi | 16 ++++++++++++++++
doc/style.css | 5 +++++
doc/www.do | 15 +++++++++++++++
main.go | 45 ++++++++++++++++++++++++++++++++++++++++++++-
diff --git a/INSTALL b/INSTALL
index 28a9b0b0688036154d7e82836091032573c790bb..17039284251e86bf5f674148c2a0923684f18ed2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,57 +1 @@
-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 "
Paste service" > /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 <&1
- EOF
- # cat > /var/service/.paster/log/run < $tmpl <&1
+Look for doc/install.texi.
diff --git a/PROTOCOL b/PROTOCOL
deleted file mode 100644
index 96ee59cf06d5c3e45c2057c57d3808df2737eed3..0000000000000000000000000000000000000000
--- a/PROTOCOL
+++ /dev/null
@@ -1,8 +0,0 @@
-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
diff --git a/README b/README
index d5264162b44ed4442dac84c6b92bb9e9c09a680a..e6a5978c37e656efccef0b6b598d8dbcf1f676dc 100644
--- a/README
+++ b/README
@@ -1,45 +1,2 @@
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.
diff --git a/asciicast.tmpl b/asciicast.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..22380336355304c6d4cbe9ef39fbbfb8d34d276b
--- /dev/null
+++ b/asciicast.tmpl
@@ -0,0 +1,16 @@
+
+
+
+ asciicast
+
+
+
+
+
+
+
+
diff --git a/contrib/clean.sh b/contrib/clean.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a9148a9ae7c187bbbe66a2241474fd7326f41918
--- /dev/null
+++ b/contrib/clean.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+find . -type f -mtime +1 \
+ -and -not -name index.html \
+ -and -not -name "asciinema-player-*" \
+ -delete
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2129d45230504f4a0040f937e615d7c1ac614d36
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1 @@
+/paster.html
diff --git a/doc/asciicast.texi b/doc/asciicast.texi
new file mode 100644
index 0000000000000000000000000000000000000000..2dc6be2d600a2e9405ba72a4400ec4f9195a978a
--- /dev/null
+++ b/doc/asciicast.texi
@@ -0,0 +1,22 @@
+@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.
diff --git a/doc/examples.texi b/doc/examples.texi
new file mode 100644
index 0000000000000000000000000000000000000000..1de5e34ed2bebc8f817533cb1ab9843073351ad6
--- /dev/null
+++ b/doc/examples.texi
@@ -0,0 +1,55 @@
+@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
diff --git a/doc/features.texi b/doc/features.texi
new file mode 100644
index 0000000000000000000000000000000000000000..56bcfa6774c240b431015d1d75cf900efc62a706
--- /dev/null
+++ b/doc/features.texi
@@ -0,0 +1,31 @@
+@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
diff --git a/doc/index.texi b/doc/index.texi
new file mode 100644
index 0000000000000000000000000000000000000000..03d3e0f41e62be9cbedcd79c773930468dd7a53c
--- /dev/null
+++ b/doc/index.texi
@@ -0,0 +1,30 @@
+\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
diff --git a/doc/install.texi b/doc/install.texi
new file mode 100644
index 0000000000000000000000000000000000000000..8083cb3443090a69573a8b7740b86e5100168d18
--- /dev/null
+++ b/doc/install.texi
@@ -0,0 +1,104 @@
+@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 <
+
+ paster
+ Paste service.
+
+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 <&1
+EOF
+
+# cat > /var/service/.paster/log/run < $tmpl <&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
diff --git a/doc/protocol.texi b/doc/protocol.texi
new file mode 100644
index 0000000000000000000000000000000000000000..12e0a4fab5f1cc080b837f805749b3d3de05c12d
--- /dev/null
+++ b/doc/protocol.texi
@@ -0,0 +1,16 @@
+@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
diff --git a/doc/style.css b/doc/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..44fa2e051c6a8f312534e54b730e28b0fc4a9a1e
--- /dev/null
+++ b/doc/style.css
@@ -0,0 +1,5 @@
+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 }
diff --git a/doc/www.do b/doc/www.do
new file mode 100644
index 0000000000000000000000000000000000000000..ea98526c89373433609b46e2e706fd236bbd0053
--- /dev/null
+++ b/doc/www.do
@@ -0,0 +1,15 @@
+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 {} +
diff --git a/main.go b/main.go
index 139b5366d31f9caf6c75b001de1f671ab365877b..89bcc6eabdb6010ae235c37b162fb06aae213f65 100644
--- a/main.go
+++ b/main.go
@@ -22,22 +22,57 @@ "bufio"
"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()
@@ -150,7 +185,15 @@ }
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",