aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2024-01-30 20:51:38 +0000
committerLaurent Bercot <ska@appnovation.com>2024-01-30 20:51:38 +0000
commit21d51f7e0a639a3224ffc45dc3c06854decf1d45 (patch)
tree87c4a36f66510e79d7ec8315361bb8bbbb5767f5 /src
downloadapaste-21d51f7e0a639a3224ffc45dc3c06854decf1d45.tar.gz
Initial commit
Signed-off-by: Laurent Bercot <ska@appnovation.com>
Diffstat (limited to 'src')
-rw-r--r--src/client/PROTOCOL.txt19
-rw-r--r--src/client/apaste.c101
-rw-r--r--src/client/apastec.c155
-rw-r--r--src/client/apastec.h17
-rw-r--r--src/client/deps-exe/apaste1
-rw-r--r--src/client/deps-exe/apastec4
-rw-r--r--src/client/send_file.c93
-rw-r--r--src/include-local/apaste-common.h9
-rw-r--r--src/server/apasted.c339
-rw-r--r--src/server/deps-exe/apasted3
10 files changed, 741 insertions, 0 deletions
diff --git a/src/client/PROTOCOL.txt b/src/client/PROTOCOL.txt
new file mode 100644
index 0000000..6e828ca
--- /dev/null
+++ b/src/client/PROTOCOL.txt
@@ -0,0 +1,19 @@
+
+apastec:
+
+banner \n
+nfiles (uint32) \n
+{
+ namelength (uint16) \n
+ name (namelength) \n
+ filelength (uint64) \n
+ file (filelength) \n
+}
+banner \n
+
+
+apasted:
+
+banner \n
+slug \n
+banner \n
diff --git a/src/client/apaste.c b/src/client/apaste.c
new file mode 100644
index 0000000..1d2a739
--- /dev/null
+++ b/src/client/apaste.c
@@ -0,0 +1,101 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/exec.h>
+
+#include <s6-networking/config.h>
+
+#include <apaste/config.h>
+
+#define USAGE "apaste [ -S | -s ] [ -C cadir ] [ -d server[:port] ] [ -r rtimeout ] [ -w wtimeout ] file..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char *const *argv)
+{
+ char const *cadir = APASTE_DEFAULT_CADIR ;
+ char const *server = APASTE_DEFAULT_SERVER ;
+ int dotls = APASTE_DEFAULT_TLS ;
+ uint32_t rt = 0 ;
+ uint32_t wt = 0 ;
+ uint16_t port = 0 ;
+ PROG = "apaste" ;
+
+ {
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, (char const *const *)argv, "SsC:d:r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'S' : dotls = 0 ; break ;
+ case 's' : dotls = 1 ; break ;
+ case 'C' : cadir = l.arg ; break ;
+ case 'd' :
+ {
+ char *colon = strchr(l.arg, ':') ;
+ server = l.arg ;
+ if (colon)
+ {
+ *colon = 0 ;
+ if (!uint160_scan(colon + 1, &port)) dieusage() ;
+ }
+ break ;
+ }
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+ if (!port) port = dotls ? APASTE_DEFAULT_TLSPORT : APASTE_DEFAULT_PORT ;
+ {
+ char const *newargv[12 + argc] ;
+ unsigned int m = 0 ;
+ char fmtr[UINT32_FMT] ;
+ char fmtw[UINT32_FMT] ;
+ char fmtp[UINT16_FMT] ;
+
+ fmtp[uint16_fmt(fmtp, port)] = 0 ;
+
+ newargv[m++] = dotls ? S6_NETWORKING_EXTBINPREFIX "s6-tlsclient" : S6_NETWORKING_EXTBINPREFIX "s6-tcpclient" ;
+ newargv[m++] = "-N" ;
+ newargv[m++] = "--" ;
+ newargv[m++] = server ;
+ newargv[m++] = fmtp ;
+ newargv[m++] = APASTE_BINPREFIX "apastec" ;
+ if (rt)
+ {
+ fmtr[uint32_fmt(fmtr, rt)] = 0 ;
+ newargv[m++] = "-r" ;
+ newargv[m++] = fmtr ;
+ }
+ if (rt)
+ {
+ fmtw[uint32_fmt(fmtw, wt)] = 0 ;
+ newargv[m++] = "-w" ;
+ newargv[m++] = fmtw ;
+ }
+ newargv[m++] = "--" ;
+ while (argc--) newargv[m++] = *argv++ ;
+ newargv[m++] = 0 ;
+
+ if (dotls)
+ {
+ size_t len = strlen(cadir) ;
+ char modif[7 + len] ;
+ memcpy(modif, "CADIR=", 6) ;
+ memcpy(modif + 6, cadir, len + 1) ;
+ xmexec_n(newargv, modif, len + 7, 1) ;
+ }
+ else xexec(newargv) ;
+ }
+}
diff --git a/src/client/apastec.c b/src/client/apastec.c
new file mode 100644
index 0000000..3237ceb
--- /dev/null
+++ b/src/client/apastec.c
@@ -0,0 +1,155 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+
+#include <apaste/config.h>
+#include "apaste-common.h"
+#include "apastec.h"
+
+#define USAGE "apastec [ -r rtimeout ] [ -w wtimeout ] file..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static void apaste_send (buffer *b, char const *file, tain const *deadline)
+{
+ int fd ;
+ struct stat st ;
+ size_t len = strlen(file) ;
+ if (!len) strerr_dief2x(100, "empty file", " names are invalid") ;
+ if (len > UINT16_MAX) strerr_dief2x(100, "file name too long: ", file) ;
+ if (len == 1 && file[0] == '-')
+ {
+ char tmp[sizeof(APASTE_TMPDIR) + 14] = APASTE_TMPDIR "/apaste:XXXXXX" ;
+ fd = mkstemp(tmp) ;
+ if (fd == -1) strerr_diefu2sys(111, "mkstemp ", tmp) ;
+ unlink_void(tmp) ;
+ if (fd_cat(0, fd) == -1) strerr_diefu2sys(111, "copy stdin to ", tmp) ;
+ if (lseek(fd, 0, SEEK_SET) == -1) strerr_diefu1sys(111, "lseek") ;
+ ndelay_on(fd) ;
+ }
+ else
+ {
+ if (file[len-1] == '/') strerr_dief2x(100, "directory", " names are invalid") ;
+ fd = open_read(file) ;
+ if (fd == -1) strerr_diefu2sys(111, "open ", file) ;
+ }
+
+ if (fstat(fd, &st) == -1) strerr_diefu2sys(111, "stat ", file) ;
+ if (!S_ISREG(st.st_mode)) strerr_dief3x(100, "file ", file, " is not a regular file") ;
+ {
+ char fmt[UINT64_FMT] ;
+ size_t m = uint16_fmt(fmt, len) ;
+ fmt[m++] = '\n' ;
+ if (buffer_timed_put_g(b, fmt, m, deadline) < m
+ || buffer_timed_put_g(b, file, len, deadline) < len
+ || buffer_timed_put_g(b, "\n", 1, deadline) < 1)
+ strerr_diefu1sys(111, "write to server") ;
+ m = uint64_fmt(fmt, st.st_size) ;
+ fmt[m++] = '\n' ;
+ if (buffer_timed_put_g(b, fmt, m, deadline) < m)
+ strerr_diefu1sys(111, "write to server") ;
+ }
+ send_file_g(b, fd, st.st_size, file, deadline) ;
+ fd_close(fd) ;
+ if (buffer_timed_put_g(b, "\n", 1, deadline) < 1)
+ strerr_diefu1sys(111, "write to server") ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ tain deadline ;
+ PROG = "apastec" ;
+ {
+ uint32_t rt = 0, wt = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "r:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (rt) tain_from_millisecs(&rtto, rt) ;
+ if (wt) tain_from_millisecs(&rtto, wt) ;
+ }
+ if (!argc) dieusage() ;
+ if (argc > UINT32_MAX) strerr_dief1x(100, "too many arguments") ;
+
+ if (!sig_altignore(SIGPIPE))
+ strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&deadline, &wtto) ;
+
+ {
+ char buf[BUFFER_OUTSIZE] ;
+ buffer b = BUFFER_INIT(&buffer_write, 7, buf, BUFFER_OUTSIZE) ;
+ buffer_putnoflush(&b, APASTEC_BANNER "\n", sizeof(APASTEC_BANNER)) ;
+ {
+ char fmt[UINT32_FMT] ;
+ size_t m = uint32_fmt(fmt, argc) ;
+ fmt[m++] = '\n' ;
+ buffer_putnoflush(&b, fmt, m) ;
+ }
+ {
+ int gotstdin = 0 ;
+ for (unsigned int i = 0 ; i < argc ; i++) if (argv[i][0] == '-' && !argv[i][1])
+ {
+ if (gotstdin) strerr_dief1x(100, "stdin specified more than once") ;
+ gotstdin = 1 ;
+ }
+ }
+ for (unsigned int i = 0 ; i < argc ; i++) apaste_send(&b, argv[i], &deadline) ;
+ if (buffer_timed_put_g(&b, APASTEC_BANNER "\n", sizeof(APASTEC_BANNER), &deadline) < sizeof(APASTEC_BANNER)
+ || !buffer_timed_flush_g(&b, &deadline))
+ strerr_diefu1sys(111, "write to apaste server") ;
+ }
+
+ fd_shutdown(7, 1) ;
+ fd_close(7) ;
+ tain_add_g(&deadline, &rtto) ;
+
+ {
+ char buf[BUFFER_INSIZE] ;
+ char banner[256] ;
+ char slug[256] ;
+ buffer b = BUFFER_INIT(&buffer_read, 6, buf, BUFFER_INSIZE) ;
+ size_t w = 0, sluglen = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ if (w != sizeof(APASTED_BANNER) || memcmp(banner, APASTED_BANNER, w-1))
+ strerr_dief1x(1, "server returned invalid data") ;
+ if (sanitize_read(timed_getlnmax_g(&b, slug, 256, &sluglen, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ if (w != sizeof(APASTED_BANNER) || memcmp(banner, APASTED_BANNER, w-1))
+ strerr_dief1x(1, "server returned invalid data") ;
+ if (buffer_putflush(buffer_1small, slug, sluglen) == -1)
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+
+ return 0 ;
+}
diff --git a/src/client/apastec.h b/src/client/apastec.h
new file mode 100644
index 0000000..84ab242
--- /dev/null
+++ b/src/client/apastec.h
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#ifndef APASTEC_H
+#define APASTEC_H
+
+#include <sys/types.h>
+
+#include <skalibs/buffer.h>
+#include <skalibs/tai.h>
+
+
+/* send_file.c */
+
+extern void send_file (buffer *, int, off_t, char const *, tain const *, tain *) ;
+#define send_file_g(b, fd, size, file, deadline) send_file(b, fd, size, file, (deadline), &STAMP)
+
+#endif
diff --git a/src/client/deps-exe/apaste b/src/client/deps-exe/apaste
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/client/deps-exe/apaste
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/client/deps-exe/apastec b/src/client/deps-exe/apastec
new file mode 100644
index 0000000..1e0ab64
--- /dev/null
+++ b/src/client/deps-exe/apastec
@@ -0,0 +1,4 @@
+send_file.o
+-lskarnet
+${SOCKET_LIB}
+${SYSCLOCK_LIB}
diff --git a/src/client/send_file.c b/src/client/send_file.c
new file mode 100644
index 0000000..4da8412
--- /dev/null
+++ b/src/client/send_file.c
@@ -0,0 +1,93 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifdef SKALIBS_HASSENDFILE
+
+#include <sys/types.h>
+#include <sys/sendfile.h>
+#include <limits.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+struct sendfile_s
+{
+ int fd ;
+ buffer *out ;
+ off_t pos ;
+ uint64_t n ;
+} ;
+
+static int s_getfd (void *p)
+{
+ struct sendfile_s *sf = p ;
+ return buffer_fd(sf->out) ;
+}
+
+static int s_isnonempty (void *p)
+{
+ struct sendfile_s *sf = p ;
+ return !!sf->n ;
+}
+
+static int s_flush (void *p)
+{
+ struct sendfile_s *sf = p ;
+ while (sf->n)
+ {
+ ssize_t r = sendfile(buffer_fd(sf->out), sf->fd, &sf->pos, sf->n > SSIZE_MAX ? SSIZE_MAX : sf->n) ;
+ if (r == -1) return 0 ;
+ sf->n -= r ;
+ }
+ return 1 ;
+}
+
+void send_file (buffer *b, int fd, off_t size, char const *fn, tain const *deadline, tain *stamp)
+{
+ struct sendfile_s sf = { .fd = fd, .out = b, .pos = 0, .n = size } ;
+ if (!buffer_timed_flush(b, deadline, stamp))
+ strerr_diefu1sys(111, "write to network") ;
+ if (!timed_flush(&sf, &s_getfd, &s_isnonempty, &s_flush, deadline, stamp))
+ strerr_diefu3sys(111, "sendfile ", fn, " to network") ;
+}
+
+#else
+
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <skalibs/uint64.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/unix-timed.h>
+
+void send_file (buffer *b, int fd, uint64_t n, char const *fn, tain const *deadline, tain *stamp)
+{
+ struct iovec v[2] ;
+ ssize_t r ;
+ if (!n) goto flushit ;
+ fillit:
+ buffer_wpeek(b, v) ;
+ r = allreadv(fd, v, 2) ;
+ if (r == -1) strerr_diefu2sys(111, "read from ", fn) ;
+ if (!r) strerr_diefu3x(111, "send ", fn, ": file was truncated") ;
+ if (r > n)
+ {
+ r = n ;
+ strerr_warnw2x("sending elongated file: ", fn) ;
+ }
+ buffer_wseek(b, r) ;
+ n -= r ;
+ flushit:
+ if (!buffer_timed_flush(b, deadline, stamp))
+ strerr_diefu1sys(111, "write to network") ;
+ if (n) goto fillit ;
+}
+
+#endif
diff --git a/src/include-local/apaste-common.h b/src/include-local/apaste-common.h
new file mode 100644
index 0000000..8a680d9
--- /dev/null
+++ b/src/include-local/apaste-common.h
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#ifndef APASTE_COMMON_H
+#define APASTE_COMMON_H
+
+#define APASTEC_BANNER "apastec protocol v1"
+#define APASTED_BANNER "apasted protocol v1"
+
+#endif
diff --git a/src/server/apasted.c b/src/server/apasted.c
new file mode 100644
index 0000000..b338154
--- /dev/null
+++ b/src/server/apasted.c
@@ -0,0 +1,339 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <skalibs/posixplz.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
+#include <skalibs/uint64.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/siovec.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/buffer.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/tai.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/sig.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/unix-timed.h>
+
+#include <apaste/config.h>
+#include "apaste-common.h"
+
+#define USAGE "apasted [ -r rtimeout ] [ -w wtimeout ] [ -d rootdir ] [ -p prefix ] [ -m maxfiles ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define FORBIDDEN " \t\r\n\"<>&;"
+
+#define INDEXSTART "\
+<html>\n\
+ <head>\n\
+ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n\
+ <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n\
+ <meta http-equiv=\"Content-Language\" content=\"en\" />\n\
+ <title>apaste results</title>\n\
+ <meta name=\"Description\" content=\"apaste results\" />\n\
+ </head>\n\
+<body>\n\
+<h3> apaste results </h3>\n\
+<ul>\n"
+
+static void cleanup (char const *dir)
+{
+ int e = errno ;
+ rm_rf(dir) ;
+ errno = e ;
+}
+
+static void prepare_index (buffer *ib, char const *dir, char *buf, size_t buflen)
+{
+ int fd ;
+ size_t dirlen = strlen(dir) ;
+ char fn[dirlen + 12] ;
+ memcpy(fn, dir, dirlen) ;
+ memcpy(fn + dirlen, "/index.html", 12) ;
+ fd = open_create(fn) ;
+ if (fd == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "open ", dir, "/index.html for writing") ;
+ }
+ buffer_init(ib, &buffer_write, fd, buf, buflen) ;
+ if (buffer_puts(ib, INDEXSTART) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "prepare index for ", dir) ;
+ }
+}
+
+static inline void add_index_entry (char const *dir, buffer *ib, char const *name, uint64_t filelen)
+{
+ char fmt[UINT64_FMT] ;
+ size_t m = uint64_fmt(fmt, filelen) ;
+ if (buffer_puts(ib, " <li> <a href=\"") == -1
+ || buffer_puts(ib, name) == -1
+ || buffer_puts(ib, ".txt\">") == -1
+ || buffer_puts(ib, name) == -1
+ || buffer_puts(ib, "</a> (") == -1
+ || buffer_put(ib, fmt, m) == -1
+ || buffer_puts(ib, " bytes) </li>\n") == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu5sys(111, "add ", name, " to ", dir, "/index.html") ;
+ }
+}
+
+static inline void finish_index (buffer *ib, char const *dir)
+{
+ if (buffer_putsflush(ib, "</ul>\n</body>\n</html>\n") == -1
+ || fsync(buffer_fd(ib)) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "finish index for ", dir) ;
+ }
+ fd_close(buffer_fd(ib)) ;
+}
+
+static inline void recv_one_file (char const *dir, char const *fn, buffer *b, int fd, uint64_t n, tain const *deadline)
+{
+ struct iovec v[2] ;
+ size_t len ;
+ while (n)
+ {
+ unsigned int j = 2 ;
+ if (buffer_len(b) < n)
+ {
+ if (sanitize_read(buffer_timed_fill_g(b, deadline)) <= 0)
+ {
+ cleanup(dir) ;
+ _exit(1) ;
+ }
+ }
+ buffer_rpeek(b, v) ;
+ len = siovec_len(v, 2) ;
+ if (len > n)
+ {
+ j = siovec_trunc(v, 2, n) ;
+ len = n ;
+ }
+ if (allwritev(fd, v, j) < len)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "write to ", fn) ;
+ }
+ buffer_rseek(b, len) ;
+ n -= len ;
+ }
+}
+
+static inline size_t add_unique (stralloc *sa, size_t *indices, uint32_t n, char const *bname)
+{
+ size_t blen = strlen(bname) ;
+ size_t pos = sa->len ;
+ static uint32_t suffix = 0 ;
+ if (!stralloc_readyplus(sa, blen + UINT32_FMT + 2)) return 0 ;
+ memcpy(sa->s + pos, bname, blen + 1) ;
+ for (;;)
+ {
+ uint32_t i = 0 ;
+ for (; i < n ; i++) if (!strcmp(sa->s + indices[i], sa->s + pos)) break ;
+ if (i == n) break ;
+ sa->s[pos + blen] = '.' ;
+ sa->s[pos + blen + 1 + uint32_fmt(sa->s + pos + blen + 1, suffix++)] = 0 ;
+ }
+
+ blen = strlen(sa->s + pos) ;
+ indices[n] = sa->len ;
+ sa->len += blen + 1 ;
+ return blen ;
+}
+
+static void read_one_file (char const *dir, buffer *b, buffer *ib, stralloc *sa, size_t *indices, uint32_t n, tain const *deadline)
+{
+ uint64_t filelen ;
+ size_t bnamelen ;
+ uint16_t namelen ;
+
+ {
+ size_t w = 0 ;
+ size_t m ;
+ char fmt[UINT16_FMT] ;
+ if (sanitize_read(timed_getlnmax_g(b, fmt, UINT16_FMT, &w, '\n', deadline)) <= 0) goto err ;
+ m = uint16_scan(fmt, &namelen) ;
+ if (!m || m+1 != w || fmt[m] != '\n') goto err ;
+ }
+
+ {
+ size_t w = 0 ;
+ char *bname ;
+ char name[namelen + 1] ;
+ if (sanitize_read(timed_getlnmax_g(b, name, namelen + 1, &w, '\n', deadline)) <= 0) goto err ;
+ if (!w || w != namelen + 1 || name[namelen] != '\n') goto err ;
+ name[namelen] = 0 ;
+ bname = strrchr(name, '/') ;
+ if (bname) bname++ ; else bname = name ;
+ if (!*bname) goto err ;
+
+ if (ib) /* sanitize the filename for inclusion in index.html */
+ {
+ size_t blen = strlen(bname) ;
+ if (byte_in(bname, blen, FORBIDDEN, sizeof(FORBIDDEN)) != blen) goto err ;
+ bnamelen = add_unique(sa, indices, n, bname) ;
+ if (!bnamelen)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "make ", bname, " unique") ;
+ }
+ }
+ else bnamelen = 5 ;
+ }
+
+ {
+ size_t w = 0 ;
+ size_t m ;
+ char fmt[UINT64_FMT] ;
+ if (sanitize_read(timed_getlnmax_g(b, fmt, UINT64_FMT, &w, '\n', deadline)) <= 0) goto err ;
+ m = uint64_scan(fmt, &filelen) ;
+ if (!m || m+1 != w || fmt[m] != '\n') goto err ;
+ }
+
+ {
+ int fd ;
+ size_t dirlen = strlen(dir) ;
+ char fn[dirlen + bnamelen + 6] ;
+ memcpy(fn, dir, dirlen) ;
+ fn[dirlen] = '/' ;
+ memcpy(fn + dirlen + 1, ib ? sa->s + indices[n] : "index", bnamelen) ;
+ memcpy(fn + dirlen + 1 + bnamelen, ".txt", 5) ;
+ fd = open_create(fn) ;
+ if (fd == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu3sys(111, "open ", fn, " for writing") ;
+ }
+ recv_one_file(dir, fn, b, fd, filelen, deadline) ;
+ if (fsync(fd) == -1)
+ {
+ cleanup(dir) ;
+ strerr_diefu2sys(111, "fsync ", fn) ;
+ }
+ fd_close(fd) ;
+ }
+
+ {
+ char c ;
+ if (sanitize_read(buffer_timed_get_g(b, &c, 1, deadline)) != 1) goto err ;
+ if (c != '\n') goto err ;
+ }
+
+ if (ib) add_index_entry(dir, ib, sa->s + indices[n], filelen) ;
+ return ;
+
+ err:
+ cleanup(dir) ;
+ _exit(1) ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ char const *prefix = "" ;
+ tain rtto = TAIN_INFINITE_RELATIVE, wtto = TAIN_INFINITE_RELATIVE ;
+ tain deadline ;
+ uint32_t maxfiles = 0 ;
+ uint32_t n ;
+ char buf[4097] ;
+ char dir[7] = "XXXXXX" ;
+ buffer b = BUFFER_INIT(&buffer_read, 0, buf, 4097) ;
+ PROG = "apasted" ;
+ {
+ char const *rootdir = 0 ;
+ uint32_t rt = 0, wt = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "r:w:d:p:m:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : if (!uint320_scan(l.arg, &rt)) dieusage() ; break ;
+ case 'w' : if (!uint320_scan(l.arg, &wt)) dieusage() ; break ;
+ case 'd' : rootdir = l.arg ; break ;
+ case 'p' : prefix = l.arg ; break ;
+ case 'm' : if (!uint320_scan(l.arg, &maxfiles)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (rt) tain_from_millisecs(&rtto, rt) ;
+ if (wt) tain_from_millisecs(&rtto, wt) ;
+ if (rootdir && chdir(rootdir) == -1)
+ strerr_diefu2sys(111, "chdir to ", rootdir) ;
+ if (strlen(prefix) > 4000) strerr_dief1x(100, "prefix too long") ;
+ }
+
+ if (!sig_altignore(SIGPIPE))
+ strerr_diefu1sys(111, "ignore SIGPIPE") ;
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&deadline, &rtto) ;
+
+ {
+ char banner[256] ;
+ size_t w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0) _exit(1) ;
+ if (w != sizeof(APASTEC_BANNER) || memcmp(banner, APASTEC_BANNER, w-1)) _exit(1) ;
+ }
+
+ {
+ char fmt[UINT32_FMT] ;
+ size_t w = 0 ;
+ size_t m ;
+ if (sanitize_read(timed_getlnmax_g(&b, fmt, UINT32_FMT, &w, '\n', &deadline)) <= 0)
+ strerr_diefu1sys(111, "read from apaste server") ;
+ m = uint32_scan(fmt, &n) ;
+ if (!m || m+1 != w) _exit(1) ;
+ if (!n || (maxfiles && n > maxfiles)) _exit(1) ;
+ }
+
+ if (!mkdtemp(dir)) strerr_diefu1sys(111, "mkdtemp") ;
+ if (n == 1) read_one_file(dir, &b, 0, 0, 0, 0, &deadline) ;
+ else
+ {
+ stralloc sa = STRALLOC_ZERO ;
+ size_t indices[n] ;
+ buffer ib ;
+ char ibuf[4096] ;
+ prepare_index(&ib, dir, ibuf, 4096) ;
+ for (uint32_t i = 0 ; i < n ; i++) read_one_file(dir, &b, &ib, &sa, indices, i, &deadline) ;
+ finish_index(&ib, dir) ;
+ }
+
+ {
+ char banner[256] ;
+ size_t w = 0 ;
+ if (sanitize_read(timed_getlnmax_g(&b, banner, 256, &w, '\n', &deadline)) <= 0
+ || w != sizeof(APASTEC_BANNER)
+ || memcmp(banner, APASTEC_BANNER, w-1)) goto err ;
+ }
+
+ buffer_init(&b, &buffer_write, 1, buf, 4097) ;
+ buffer_putsnoflush(&b, APASTED_BANNER "\n") ;
+ if (prefix[0]) buffer_putsnoflush(&b, prefix) ;
+ buffer_putnoflush(&b, dir, 6) ;
+ if (!strncmp(prefix, "http", 4)) buffer_putnoflush(&b, "/", 1) ;
+ buffer_putsnoflush(&b, "\n" APASTED_BANNER "\n") ;
+ tain_add_g(&deadline, &wtto) ;
+ if (!buffer_timed_flush_g(&b, &deadline)) goto err ;
+
+ return 0 ;
+
+ err:
+ cleanup(dir) ;
+ return 1 ;
+}
diff --git a/src/server/deps-exe/apasted b/src/server/deps-exe/apasted
new file mode 100644
index 0000000..720fe7d
--- /dev/null
+++ b/src/server/deps-exe/apasted
@@ -0,0 +1,3 @@
+-lskarnet
+${SOCKET_LIB}
+${SYSCLOCK_LIB}