summary refs log tree commit diff
path: root/show.c
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-07-16 22:19:20 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2016-07-16 22:19:20 +0200
commitd87ab6386c0f786dbde5d2fedcfb060d4a3f7707 (patch)
tree842a2fb433774b1a5b076adcb6b42c852ca8d5c4 /show.c
parent248ed48727433736fdb37feee4968d9f5e38ce41 (diff)
downloadmblaze-d87ab6386c0f786dbde5d2fedcfb060d4a3f7707.tar.gz
mblaze-d87ab6386c0f786dbde5d2fedcfb060d4a3f7707.tar.xz
mblaze-d87ab6386c0f786dbde5d2fedcfb060d4a3f7707.zip
show: rewrite with mime support, listing and extraction
Diffstat (limited to 'show.c')
-rw-r--r--show.c361
1 files changed, 346 insertions, 15 deletions
diff --git a/show.c b/show.c
index dda396d..cdb080e 100644
--- a/show.c
+++ b/show.c
@@ -10,54 +10,385 @@
 #include <errno.h>
 #include <time.h>
 #include <wchar.h>
+#include <unistd.h>
 
 #include "blaze822.h"
 
+static int rflag;
+static int qflag;
+static int Hflag;
+static int Lflag;
+static int tflag;
+static char defaulthflags[] = "from:subject:to:cc:date:";
+static char *hflag = defaulthflags;
+static char *xflag;
+static char *Oflag;
+
+struct message *filters;
+
+static int mimecount;
+
 void
 printhdr(char *hdr)
 {
 	int uc = 1;
 
-	while (*hdr) {
+	while (*hdr && *hdr != ':') {
 		putc(uc ? toupper(*hdr) : *hdr, stdout);
 		uc = (*hdr == '-');
 		hdr++;
 	}
-	putc(' ', stdout);
+
+	if (*hdr) {
+		printf("%s\n", hdr);
+	}
+}
+
+char *
+mimetype(char *ct)
+{
+	char *s;
+	for (s = ct; *s && *s != ';' && *s != ' ' && *s != '\t'; s++)
+		;
+
+	return strndup(ct, s-ct);
+}
+
+char *
+tlmimetype(char *ct)
+{
+	char *s;
+	for (s = ct; *s && *s != ';' && *s != ' ' && *s != '\t' && *s != '/'; s++)
+		;
+
+	return strndup(ct, s-ct);
+}
+
+typedef enum {
+	MIME_CONTINUE,
+	MIME_STOP,
+	MIME_PRUNE,
+} mime_action;
+
+typedef mime_action (*mime_callback)(int, char *, char *, size_t);
+
+mime_action
+render_mime(int depth, char *ct, char *body, size_t bodylen)
+{
+	char *mt = mimetype(ct);
+	char *tlmt = tlmimetype(ct);
+
+	char *filename, *fn, *fne;
+	if (blaze822_mime_parameter(ct, "name", &fn, &fne))
+		filename = strndup(fn, fne-fn);
+
+	mimecount++;
+
+	int i;
+	for (i = 0; i < depth+1; i++)
+		printf("--- ");
+	printf("%d: %s size=%zd", mimecount, mt, bodylen);
+	if (filename)
+		printf(" name=%s", filename);
+
+	char *cmd;
+	mime_action r = MIME_CONTINUE;
+
+	if (filters &&
+	    ((cmd = blaze822_chdr(filters, mt)) ||
+	    (cmd = blaze822_chdr(filters, tlmt)))) {
+		printf(" filter=\"%s\" ---\n", cmd);
+		FILE *p;
+		fflush(stdout);
+		p = popen(cmd, "w");
+		if (!p) {
+			perror("popen");
+			goto nofilter;
+		}
+		fwrite(body, bodylen, 1, p);
+		if (pclose(p) != 0) {
+			perror("pclose");
+			goto nofilter;
+		}
+		r = MIME_PRUNE;
+	} else {
+nofilter:
+		printf(" ---\n");
+
+		if (strncmp(ct, "text/", 5) == 0) {
+			fwrite(body, bodylen, 1, stdout);
+		} else if (strncmp(ct, "message/rfc822", 14) == 0) {
+			struct message *imsg = blaze822_mem(body, bodylen);
+			char *h = 0;
+			if (imsg) {
+				while ((h = blaze822_next_header(imsg, h)))
+					printf("%s\n", h);
+				printf("\n");
+			}
+		} else if (strncmp(ct, "multipart/", 10) == 0) {
+			;
+		} else {
+			printf("no filter or default handler\n");
+		}
+	}
+
+	free(mt);
+	free(tlmt);
+
+	return r;
+}
+
+mime_action
+list_mime(int depth, char *ct, char *body, size_t bodylen)
+{
+	(void) body;
+
+	char *mt = mimetype(ct);
+	char *fn, *fne;
+
+	printf("%*.s%d: %s size=%zd", depth*2, "", ++mimecount, mt, bodylen);
+	if (blaze822_mime_parameter(ct, "name", &fn, &fne)) {
+		printf(" name=");
+		fwrite(fn, 1, fne-fn, stdout);
+	}
+	printf("\n");
+
+	return MIME_CONTINUE;
+}
+
+mime_action
+walk_mime(struct message *msg, int depth, mime_callback visit)
+{
+	char *ct, *body, *bodychunk;
+	size_t bodylen;
+
+	mime_action r = MIME_CONTINUE;
+
+	if (blaze822_mime_body(msg, &ct, &body, &bodylen, &bodychunk)) {
+
+		mime_action r = visit(depth, ct, body, bodylen);
+
+		if (r == MIME_CONTINUE) {
+			if (strncmp(ct, "multipart/", 10) == 0) {
+				struct message *imsg = 0;
+				while (blaze822_multipart(msg, &imsg)) {
+					r = walk_mime(imsg, depth+1, visit);
+					if (r == MIME_STOP)
+						break;
+				}
+			} else if (strncmp(ct, "message/rfc822", 14) == 0) {
+				struct message *imsg = blaze822_mem(body, bodylen);
+				if (imsg)
+					walk_mime(imsg, depth+1, visit);
+			}
+		}
+
+		free(bodychunk);
+	}
+
+	return r;
 }
 
 void
-show(char *file)
+list(char *file)
 {
-	struct message *msg;
+	struct message *msg = blaze822_file(file);
+	if (!msg)
+		return;
+	mimecount = 0;
+	walk_mime(msg, 0, list_mime);
+}
+
+static int extract_argc;
+static char **extract_argv;
+static int extract_stdout;
+
+static int
+writefile(char *name, char *buf, ssize_t len)
+{
+	int fd = open(name, O_CREAT | O_EXCL | O_WRONLY, 0666);
+	if (fd == -1) {
+		perror("open");
+		return -1;
+	}
+	if (write(fd, buf, len) != len) {
+		// XXX partial write
+		perror("write");
+		return -1;
+	}
+	close(fd);
+	return 0;
+}
+
+mime_action
+extract_mime(int depth, char *ct, char *body, size_t bodylen)
+{
+	(void) body;
+	(void) depth;
+
+	char *filename = 0, *fn, *fne;
+
+	if (blaze822_mime_parameter(ct, "name", &fn, &fne))
+		filename = strndup(fn, fne-fn);
+
+	mimecount++;
 
-	msg = blaze822_file(file);
+	if (extract_argc == 0) {
+		if (extract_stdout) { // output all parts
+			fwrite(body, bodylen, 1, stdout);
+		} else { // extract all named attachments
+			if (filename) {
+				printf("%s\n", filename);
+				writefile(filename, body, bodylen);
+			}
+		}
+	} else {
+		int i;
+		for (i = 0; i < extract_argc; i++) {
+			char *a = extract_argv[i];
+			char *b;
+			errno = 0;
+			long d = strtol(a, &b, 10);
+			if (errno == 0 && !*b && d == mimecount) {
+				// extract by id
+				if (extract_stdout) {
+					fwrite(body, bodylen, 1, stdout);
+				} else {
+					char buf[255];
+					if (!filename) {
+						snprintf(buf, sizeof buf,
+						    "attachment%d", mimecount);
+						filename = buf;
+					}
+					printf("%s\n", filename);
+					writefile(filename, body, bodylen);
+				}
+			} else if (filename && strcmp(a, filename) == 0) {
+				// extract by name
+				if (extract_stdout) {
+					fwrite(body, bodylen, 1, stdout);
+				} else {
+					printf("%s\n", filename);
+					writefile(filename, body, bodylen);
+				}
+			}
+		}
+	}
+
+	free(filename);
+	return MIME_CONTINUE;
+}
+
+void
+extract(char *file, int argc, char **argv, int use_stdout)
+{
+	struct message *msg = blaze822_file(file);
 	if (!msg)
 		return;
+	mimecount = 0;
+	extract_argc = argc;
+	extract_argv = argv;
+	extract_stdout = use_stdout;
+	walk_mime(msg, 0, extract_mime);
+}
 
-	char fields[] = "\0from:\0subject:\0to:\0cc:\0date:\0\0";
+void
+show(char *file)
+{
+	struct message *msg;
 
-	// XXX custom field formatting
+	if (qflag)
+		msg = blaze822(file);
+	else
+		msg = blaze822_file(file);
+	if (!msg)
+		return;
 
-	char *f, *v;
-	for (f = fields; f[1]; f += strlen(f+1)+1) {
-		v = blaze822_hdr_(msg, f, strlen(f+1)+1);
-		if (v) {
-			printhdr(f+1);
-			printf("%s\n", v);
+	if (Hflag) {  // raw headers
+		size_t hl = blaze822_headerlen(msg);
+		char *header = malloc(hl);
+		if (!header)
+			return;
+		int fd = open(file, O_RDONLY);
+		if (fd == -1)
+			return;
+		hl = read(fd, header, hl);
+		fwrite(header, hl, 1, stdout);
+	} else if (Lflag) {  // all headers
+		char *h = 0;
+		while ((h = blaze822_next_header(msg, h))) {
+			char d[4096];
+			blaze822_decode_rfc2047(d, h, sizeof d, "UTF-8");
+			printhdr(d);
+		}
+	} else {  // selected headers
+		char *h = hflag;
+		char *v;
+		while (*h) {
+			char *n = strchr(h, ':');
+			if (n)
+				*n = 0;
+			v = blaze822_chdr(msg, h);
+			if (v) {
+				printhdr(h);
+				printf(": %s\n", v);
+			}
+			if (n) {
+				*n = ':';
+				h = n + 1;
+			} else {
+				break;
+			}
 		}
 	}
 
+	if (qflag)  // no body
+		goto done;
+
 	printf("\n");
 
-	fwrite(blaze822_body(msg), blaze822_bodylen(msg), 1, stdout);
+	if (rflag || !blaze822_check_mime(msg)) {  // raw body
+		fwrite(blaze822_body(msg), blaze822_bodylen(msg), 1, stdout);
+		goto done;
+	}
+
+	mimecount = 0;
+	walk_mime(msg, 0, render_mime);
+
+done:
 	blaze822_free(msg);
 }
 
 int
 main(int argc, char *argv[])
 {
-	blaze822_loop(argc-1, argv+1, show);
+	int c;
+	while ((c = getopt(argc, argv, "h:qrtHLx:O:")) != -1)
+		switch(c) {
+		case 'h': hflag = optarg; break;
+		case 'q': qflag = 1; break;
+		case 'r': rflag = 1; break;
+		case 'H': Hflag = 1; break;
+		case 'L': Lflag = 1; break;
+		case 't': tflag = 1; break;
+		case 'x': xflag = optarg; break;
+		case 'O': Oflag = optarg; break;
+                default:
+                        // XXX usage
+                        exit(1);
+                }
+
+	if (xflag) { // extract
+		extract(xflag, argc-optind, argv+optind, 0);
+	} else if (Oflag) { // extract to stdout
+		extract(Oflag, argc-optind, argv+optind, 1);
+	} else if (tflag) { // list
+		blaze822_loop(argc-optind, argv+optind, list);
+	} else { // show
+		if (!(qflag || rflag))
+			filters = blaze822("filters");
+		blaze822_loop(argc-optind, argv+optind, show);
+	}
 
 	return 0;
 }