diff --git a/Makefile b/Makefile index e286634..fa44127 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ PREFIX=/usr/local BINDIR=$(PREFIX)/bin MANDIR=$(PREFIX)/share/man -ALL = maddr magrep mdate mdeliver mdirs mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread +ALL = maddr magrep mdate mdeliver mdirs mexport mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread SCRIPT = mcolor mcom mless mquote all: $(ALL) @@ -15,6 +15,7 @@ magrep: magrep.o blaze822.o seq.o rfc2045.o rfc2047.o mymemmem.o mytimegm.o mdate: mdate.o mdeliver: mdeliver.o blaze822.o mymemmem.o mytimegm.o mdirs: mdirs.o +mexport: mexport.c blaze822.o seq.o mymemmem.o mytimegm.o mflag: mflag.o blaze822.o seq.o mymemmem.o mytimegm.o mgenmid: mgenmid.o blaze822.o seq.o mymemmem.o mytimegm.o mhdr: mhdr.o blaze822.o seq.o rfc2047.o mymemmem.o mytimegm.o diff --git a/README b/README index e85fab2..e819615 100644 --- a/README +++ b/README @@ -16,6 +16,7 @@ DESCRIPTION mcom(1) to write and send mail mdeliver(1) to deliver messages or import mailboxes mdirs(1) to find Maildirs + mexport(1) to export mailboxes mflag(1) to change flags (marks) of mail mgenmid(1) to generate Message-IDs mhdr(1) to extract mail headers diff --git a/man/mblaze.7 b/man/mblaze.7 index 6e30c6b..f87691a 100644 --- a/man/mblaze.7 +++ b/man/mblaze.7 @@ -26,6 +26,8 @@ to write and send mail to deliver messages or import mailboxes .It Xr mdirs 1 to find Maildirs +.It Xr mexport 1 +to export mailboxes .It Xr mflag 1 to change flags (marks) of mail .It Xr mgenmid 1 diff --git a/man/mdeliver.1 b/man/mdeliver.1 index 11ac172..0f48536 100644 --- a/man/mdeliver.1 +++ b/man/mdeliver.1 @@ -70,6 +70,7 @@ the flags of the new message file to be .Sh EXIT STATUS .Ex -std .Sh SEE ALSO +.Xr mexport 1 , .Xr maildir 5 , .Xr mbox 5 .Pp diff --git a/man/mexport.1 b/man/mexport.1 new file mode 100644 index 0000000..9b0f5ee --- /dev/null +++ b/man/mexport.1 @@ -0,0 +1,66 @@ +.Dd August 19, 2016 +.Dt MEXPORT 1 +.Os +.Sh NAME +.Nm mexport +.Nd export messages as mbox file +.Sh SYNOPSIS +.Nm +.Op Fl S +.Ar msgs\ ... +.Sh DESCRIPTION +.Nm +exports the given messages as a MBOXRD file to standard output. +See +.Xr mmsg 7 +for the message argument syntax. +.Pp +If no +.Ar msgs +are passed, +.Nm +reads file names from standard input, +or uses the mails in the current sequence when used interactively. +.Pp +.Nm +uses the +.Sq Li "Return-Path:" +(or +.Sq Li "X-Envelope-To:" ) +and the +.Sq Li "Date:" +from the message for the +.Sq Li "From " +line. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl S +Add +.Sq Li "Status:" +and +.Sq Li "X-Status:" +headers according to the Maildir flags. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr mdeliver 1 , +.Xr maildir 5 , +.Xr mbox 5 +.Pp +.Lk http://www.digitalpreservation.gov/formats/fdd/fdd000385.shtml "MBOXRD Email Format" +.Pp +.Lk https://cr.yp.to/proto/maildir.html "Using maildir format" +.Sh AUTHORS +.An Christian Neukirchen Aq Mt chneukirchen@gmail.com +.Sh LICENSE +.Nm +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/mexport.c b/mexport.c new file mode 100644 index 0000000..2e06890 --- /dev/null +++ b/mexport.c @@ -0,0 +1,139 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "blaze822.h" + +static int Sflag; + +static int status; + +void +export(char *file) +{ + struct message *msg; + + while (*file == ' ' || *file == '\t') + file++; + + msg = blaze822(file); + if (!msg) + return; + + char from[1024] = "nobody"; + + char *v; + if ((v = blaze822_hdr(msg, "return-path")) || + (v = blaze822_hdr(msg, "x-envelope-from"))) { + char *s = strchr(v, '<'); + char *e = strchr(s, '>'); + if (s && e) { + e++; + memcpy(from, s, e-s); + from[e-s] = 0; + } + } + + time_t date = -1; + if ((v = blaze822_hdr(msg, "date"))) { + date = blaze822_date(v); + } + + char *line = 0; + size_t linelen = 0; + + FILE *infile = fopen(file, "r"); + if (!infile) { + status = 1; + return; + } + + printf("From %s %s", from, ctime(&date)); + + int in_header = 1; + int final_nl = 0; + + while (1) { + errno = 0; + ssize_t rd = getline(&line, &linelen, infile); + if (rd == -1) { + if (errno == 0) + break; + // XXX print error? + status = 1; + return; + } + + if (in_header && line[0] == '\n' && !line[1]) { + if (Sflag) { + char *flags = strstr(file, ":2,"); + if (!flags) + flags = ""; + + fputs("Status: ", stdout); + if (strchr(flags, 'S')) + putchar('R'); + char *ee = strrchr(file, '/'); + if (!ee || + !(ee >= file + 3 && ee[-3] == 'n' && ee[-2] == 'e' && ee[-1] == 'w')) + putchar('O'); + putchar('\n'); + + fputs("X-Status: ", stdout); + if (strchr(flags, 'R')) putchar('A'); + if (strchr(flags, 'T')) putchar('D'); + if (strchr(flags, 'F')) putchar('F'); + putchar('\n'); + } + + in_header = 0; + } + + // MBOXRD: add first > to >>..>>From + char *s = line; + while (*s == '>') + s++; + if (strncmp("From ", s, 5) == 0) + putchar('>'); + + fputs(line, stdout); + final_nl = (line[rd-1] == '\n'); + } + + // ensure trailing newline + if (!final_nl) + putchar('\n'); + + fclose(infile); + + blaze822_free(msg); +} + +int +main(int argc, char *argv[]) +{ + int c; + while ((c = getopt(argc, argv, "S")) != -1) + switch(c) { + case 'S': Sflag = 1; break; + default: + fprintf(stderr, "Usage: mexport [-S] [msgs...]\n"); + exit(2); + } + + status = 0; + + if (argc == optind && isatty(0)) + blaze822_loop1(":", export); + else + blaze822_loop(argc-optind, argv+optind, export); + + return status; +}