URI:
       scribo.c - scribo - Email-based phlog generator
  HTML git clone git://git.z3bra.org/scribo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       scribo.c (6581B)
       ---
            1 #include <ctype.h>
            2 #include <dirent.h>
            3 #include <limits.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <string.h>
            7 #include <time.h>
            8 #include <unistd.h>
            9 
           10 #include <sys/queue.h>
           11 #include <sys/types.h>
           12 
           13 #include "arg.h"
           14 #include "base64.h"
           15 #include "qp.h"
           16 #include "rfc5322.h"
           17 
           18 #include "config.h"
           19 
           20 /* header field */
           21 struct hdr {
           22         char name[BUFSIZ];
           23         char body[BUFSIZ];
           24         SLIST_ENTRY(hdr) entries;
           25 };
           26 
           27 /* header section */
           28 SLIST_HEAD(headers, hdr);
           29 
           30 void usage(char *);
           31 char * sanitize(const char *);
           32 FILE *pipeout(const char *, FILE *);
           33 char * header(struct headers *, char *);
           34 struct hdr * saveheader(struct headers *, char *);
           35 void freeheaders(struct headers *head);
           36 int parseheaders(FILE *, struct headers *);
           37 int verifyheaders(struct headers *);
           38 int write_8bit(FILE *, FILE *);
           39 int write_base64(FILE *, FILE *);
           40 int write_qp(FILE *, FILE *);
           41 int writeentry(FILE *, const char *, char *, struct headers *);
           42 
           43 
           44 void
           45 usage(char *pgm)
           46 {
           47         fprintf(stderr, "usage: %s [-h] [-a address] [-b basedir] [-d fmt] [-x cmd] [file]\n", pgm);
           48 }
           49 
           50 char *
           51 sanitize(const char *s)
           52 {
           53         static char tmp[PATH_MAX];
           54         const char *p;
           55         char *w;
           56 
           57         for (p = s, w = tmp; *p; p++) {
           58                 switch (*p) {
           59                 case '.':
           60                 case '-':
           61                 case '_':
           62                         *(w++) = *p;
           63                         break;
           64                 default:
           65                         if (isblank(*p)) *(w++) = '-';
           66                         if (isalnum(*p)) *(w++) = tolower(*p);
           67                 }
           68         }
           69 
           70         return tmp;
           71 }
           72 
           73 FILE *
           74 pipeout(const char *cmd, FILE *out)
           75 {
           76         int fd[2];
           77         char *sh;
           78 
           79         if (pipe(fd) < 0)
           80                 return NULL;
           81 
           82         if (!(sh = getenv("SHELL")))
           83                 sh = "/bin/sh";
           84 
           85         if (!fork()) {
           86                 close(fd[1]);
           87                 dup2(fd[0], STDIN_FILENO);
           88                 dup2(fileno(out), STDOUT_FILENO);
           89 
           90                 execlp(sh, sh, "-c", cmd, NULL);
           91                 return NULL; /* NOTREACHED */
           92         }
           93 
           94         fclose(out);
           95         close(fd[0]);
           96         return fdopen(fd[1], "w");
           97 }
           98 
           99 char *
          100 header(struct headers *head, char *key)
          101 {
          102         struct hdr *h;
          103         SLIST_FOREACH(h, head, entries) {
          104                 if (!strncmp(h->name, key, 997))
          105                         return h->body;
          106         }
          107 
          108         return NULL;
          109 }
          110 
          111 struct hdr *
          112 saveheader(struct headers *head, char *line)
          113 {
          114         struct hdr *h;
          115 
          116         if (!(h = malloc(sizeof(*h))))
          117                 return NULL;
          118 
          119         strlcpy(h->name, rfc5322_headername(line), sizeof(h->name));
          120         strlcpy(h->body, rfc5322_headerbody(line), sizeof(h->body));
          121         SLIST_INSERT_HEAD(head, h, entries);
          122 
          123         return h;
          124 }
          125 
          126 void
          127 freeheaders(struct headers *head)
          128 {
          129         struct hdr *h;
          130         while ((h = SLIST_FIRST(head))) {
          131                 SLIST_REMOVE_HEAD(head, entries);
          132                 free(h);
          133         }
          134 }
          135 
          136 int
          137 parseheaders(FILE *fp, struct headers *head)
          138 {
          139         char *buf = NULL;
          140         size_t bufsiz = 0;
          141         ssize_t len;
          142         struct hdr *h = NULL;
          143 
          144         SLIST_INIT(head);
          145 
          146         while ((len = getline(&buf, &bufsiz, fp)) > 0) {
          147                 /* a single newline mark the end of header section */
          148                 if (*buf == '\n' || !strncmp(buf, "\r\n", 2))
          149                         break;
          150 
          151                 if (isblank(*buf) && h)
          152                         rfc5322_unfold(h->body, buf, sizeof(h->body));
          153 
          154                 if (!isblank(*buf))
          155                         h = saveheader(head, buf);
          156         }
          157 
          158         if (len < 0) {
          159                 perror("getline");
          160                 free(buf);
          161                 return -1;
          162         }
          163 
          164         free(buf);
          165 
          166         return 0;
          167 }
          168 
          169 int
          170 verifyheaders(struct headers *head)
          171 {
          172         char *addr, *type;
          173 
          174         if (!head)
          175                 return -1;
          176 
          177         if (!header(head, "From")) {
          178                 fprintf(stderr, "Missing header: From\n");
          179                 return -1;
          180         }
          181 
          182         if (!header(head, "Date")) {
          183                 fprintf(stderr, "Missing header: Date\n");
          184                 return -1;
          185         }
          186 
          187         if (!header(head, "Subject")) {
          188                 fprintf(stderr, "Missing header: Subject\n");
          189                 return -1;
          190         }
          191 
          192 
          193         /* only accept plain text emails */
          194         type = header(head, "Content-Type");
          195         if (type && strncmp(type, "text/plain", 10)) {
          196                 fprintf(stderr, "Content-Type: %s is not supported\n", type);
          197                 return -1;
          198         }
          199 
          200         /* verify sender's address */
          201         addr = rfc5322_addr(header(head, "From"));
          202         if (author && strncmp(addr, author, strlen(author))) {
          203                 fprintf(stderr, "<%s> is not authorized to publish content\n", addr);
          204                 return -1;
          205         }
          206 
          207         return 0;
          208 }
          209 
          210 int
          211 write_8bit(FILE *in, FILE *out)
          212 {
          213         ssize_t len;
          214         char buf[BUFSIZ];
          215 
          216         while ((len = fread(buf, 1, sizeof(buf), in)))
          217                 fwrite(buf, 1, len, out);
          218 
          219         return 0;
          220 }
          221 
          222 int
          223 write_base64(FILE *in, FILE *out)
          224 {
          225         size_t n, bufsiz;
          226         ssize_t len;
          227         char *msg, *line, *b64;
          228 
          229         b64 = NULL;
          230         bufsiz = 0;
          231 
          232         line = NULL;
          233         n = 0;
          234 
          235         while ((len = getline(&line, &n, in)) > 0) {
          236                 b64 = realloc(b64, bufsiz + len);
          237                 if (!bufsiz)
          238                         memset(b64, 0, bufsiz + len);
          239                 strlcat(b64, line, bufsiz + len);
          240                 bufsiz += len;
          241         }
          242 
          243         len = base64_unfold(b64, bufsiz);
          244         len = base64_decode(&msg, (unsigned char *)b64, len);
          245 
          246         fwrite(msg, 1, len, out);
          247 
          248         free(b64);
          249         free(msg);
          250 
          251         return 0;
          252 }
          253 
          254 int
          255 write_qp(FILE *in, FILE *out)
          256 {
          257         size_t n, bufsiz;
          258         ssize_t len;
          259         char *msg, *line, *qp;
          260 
          261         qp = NULL;
          262         bufsiz = 0;
          263 
          264         line = NULL;
          265         n = 0;
          266 
          267         while ((len = getline(&line, &n, in)) > 0) {
          268                 qp = realloc(qp, bufsiz + len + 1);
          269                 if (!bufsiz)
          270                         memset(qp, 0, bufsiz + len + 1);
          271                 strlcat(qp, line, bufsiz + len + 1);
          272                 bufsiz += len + 1;
          273         }
          274 
          275         len = qp_decode(&msg, (unsigned char *)qp, bufsiz);
          276 
          277         fwrite(msg, 1, len, out);
          278 
          279         free(qp);
          280         free(msg);
          281 
          282         return 0;
          283 }
          284 
          285 int
          286 writeentry(FILE *in, const char *cmd, char *dir, struct headers *head)
          287 {
          288         FILE *out;
          289         struct tm tm = {.tm_isdst = -1};
          290         char stamp[BUFSIZ];
          291         char *subject, *date, *transfer;
          292         char entry[PATH_MAX];
          293 
          294         subject = header(head, "Subject");
          295         date = header(head, "Date");
          296         transfer = header(head, "Content-Transfer-Encoding");
          297 
          298         snprintf(entry, sizeof(entry), "%s/%s%s", dir, sanitize(subject), ext);
          299         out = fopen(entry, "w");
          300         if (!out) {
          301                 perror(entry);
          302                 return -1;
          303         }
          304 
          305         /* convert date to an appropriate format */
          306         strptime(date, "%a, %d %b %Y %T %z", &tm);
          307         strftime(stamp, sizeof(stamp), datefmt, &tm);
          308 
          309         fprintf(out, titlefmt, subject);
          310 
          311         /* pipe email body through the given command, if any */
          312         if (cmd && !(out = pipeout(cmd, out))) {
          313                 perror(cmd);
          314                 return -1;
          315         }
          316 
          317         if (transfer && !strncmp(transfer, "base64", 6))
          318                 write_base64(in, out);
          319         if (transfer && !strncmp(transfer, "quoted-printable", 16))
          320                 write_qp(in, out);
          321         else
          322                 write_8bit(in, out);
          323 
          324         fprintf(out, "\n%s\n", stamp);
          325         fclose(out);
          326 
          327         return 0;
          328 }
          329 
          330 int
          331 main(int argc, char *argv[])
          332 {
          333         FILE *in = stdin;
          334         char *argv0, *cmd;
          335         struct headers headers;
          336 
          337         cmd = NULL;
          338 
          339         ARGBEGIN {
          340         case 'a':
          341                 author = EARGF(usage(argv0));
          342                 break;
          343         case 'b':
          344                 basedir = EARGF(usage(argv0));
          345                 break;
          346         case 'd':
          347                 datefmt = EARGF(usage(argv0));
          348                 break;
          349         case 'x':
          350                 cmd = EARGF(usage(argv0));
          351                 break;
          352         default:
          353                 usage(argv0);
          354                 exit(1);
          355         } ARGEND;
          356 
          357         if (argc && !(in = fopen(*argv, "r"))) {
          358                 perror(*argv);
          359                 return -1;
          360         }
          361 
          362         if (chdir(basedir) < 0) {
          363                 perror(basedir);
          364                 return -1;
          365         }
          366 
          367         if (parseheaders(in, &headers) < 0)
          368                 return -1;
          369 
          370         if (verifyheaders(&headers) < 0)
          371                 return -1;
          372 
          373         if (writeentry(in, cmd, basedir, &headers) < 0)
          374                 return -1;
          375 
          376         fclose(in);
          377 
          378         freeheaders(&headers);
          379 
          380         return 0;
          381 }