aboutsummaryrefslogtreecommitdiffstats
path: root/src/client/apastec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/apastec.c')
-rw-r--r--src/client/apastec.c155
1 files changed, 155 insertions, 0 deletions
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 ;
+}