X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/017a82124bf2ea39ced5aa4c8f969c18b3c2fb90..9322ba2854211794c27fae9468768b80b767c211:/uip/burst.c diff --git a/uip/burst.c b/uip/burst.c index 4f654fba..ab2a9e5d 100644 --- a/uip/burst.c +++ b/uip/burst.c @@ -2,63 +2,79 @@ /* * burst.c -- explode digests into individual messages * - * $Id$ + * This code is Copyright (c) 2002, by the authors of nmh. See the + * COPYRIGHT file in the root directory of the nmh distribution for + * complete copyright information. */ #include +#include +#include + +#define BURST_SWITCHES \ + X("inplace", 0, INPLSW) \ + X("noinplace", 0, NINPLSW) \ + X("mime", 0, MIMESW) \ + X("nomime", 0, NMIMESW) \ + X("automime", 0, AUTOMIMESW) \ + X("quiet", 0, QIETSW) \ + X("noquiet", 0, NQIETSW) \ + X("verbose", 0, VERBSW) \ + X("noverbose", 0, NVERBSW) \ + X("version", 0, VERSIONSW) \ + X("help", 0, HELPSW) \ + +#define X(sw, minchars, id) id, +DEFINE_SWITCH_ENUM(BURST); +#undef X + +#define X(sw, minchars, id) { sw, minchars, id }, +DEFINE_SWITCH_ARRAY(BURST, switches); +#undef X -static struct swit switches[] = { -#define INPLSW 0 - { "inplace", 0 }, -#define NINPLSW 1 - { "noinplace", 0 }, -#define QIETSW 2 - { "quiet", 0 }, -#define NQIETSW 3 - { "noquiet", 0 }, -#define VERBSW 4 - { "verbose", 0 }, -#define NVERBSW 5 - { "noverbose", 0 }, -#define VERSIONSW 6 - { "version", 0 }, -#define HELPSW 7 - { "help", 0 }, - { NULL, 0 } +struct smsg { + off_t s_start; + off_t s_stop; }; -static char delim3[] = "-------"; +/* + * For the MIME parsing routines + */ -struct smsg { - long s_start; - long s_stop; -}; +int debugsw = 0; /* * static prototypes */ -static int find_delim (int, struct smsg *); -static void burst (struct msgs **, int, struct smsg *, int, int, int); -static void cpybrst (FILE *, FILE *, char *, char *, int); +static int find_delim (int, struct smsg *, int *); +static void find_mime_parts (CT, struct smsg *, int *); +static void burst (struct msgs **, int, struct smsg *, int, int, int, + char *, int); +static void cpybrst (FILE *, FILE *, char *, char *, int, int); + +/* + * A macro to check to see if we have reached a message delimiter + * (an encapsulation boundary, EB, in RFC 934 parlance). + * + * According to RFC 934, an EB is simply a line which starts with + * a "-" and is NOT followed by a space. So even a single "-" on a line + * by itself would be an EB. + */ +#define CHECKDELIM(buffer) (buffer[0] == '-' && buffer[1] != ' ') int main (int argc, char **argv) { - int inplace = 0, quietsw = 0, verbosw = 0; - int msgp = 0, hi, msgnum, numburst; + int inplace = 0, quietsw = 0, verbosw = 0, mimesw = 1; + int hi, msgnum, numburst; char *cp, *maildir, *folder = NULL, buf[BUFSIZ]; - char **argp, **arguments, *msgs[MAXARGS]; + char **argp, **arguments; + struct msgs_array msgs = { 0, 0, NULL }; struct smsg *smsgs; struct msgs *mp; -#ifdef LOCALE - setlocale(LC_ALL, ""); -#endif - invo_name = r1bindex (argv[0], '/'); - - /* read user profile/context */ - context_read(); + if (nmh_init(argv[0], 1)) { return 1; } arguments = getarguments (invo_name, argc, argv, 1); argp = arguments; @@ -76,10 +92,10 @@ main (int argc, char **argv) snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name); print_help (buf, switches, 1); - done (1); + done (0); case VERSIONSW: print_version(invo_name); - done (1); + done (0); case INPLSW: inplace++; @@ -88,6 +104,16 @@ main (int argc, char **argv) inplace = 0; continue; + case MIMESW: + mimesw = 2; + continue; + case NMIMESW: + mimesw = 0; + continue; + case AUTOMIMESW: + mimesw = 1; + continue; + case QIETSW: quietsw++; continue; @@ -107,16 +133,16 @@ main (int argc, char **argv) if (folder) adios (NULL, "only one folder at a time!"); else - folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); + folder = pluspath (cp); } else { - msgs[msgp++] = cp; + app_msgarg(&msgs, cp); } } if (!context_find ("path")) free (path ("./", TFOLDER)); - if (!msgp) - msgs[msgp++] = "cur"; + if (!msgs.size) + app_msgarg(&msgs, "cur"); if (!folder) folder = getfolder (1); maildir = m_maildir (folder); @@ -125,7 +151,7 @@ main (int argc, char **argv) adios (maildir, "unable to change directory to"); /* read folder and create message structure */ - if (!(mp = folder_read (folder))) + if (!(mp = folder_read (folder, 1))) adios (NULL, "unable to read folder %s", folder); /* check for empty folder */ @@ -133,26 +159,24 @@ main (int argc, char **argv) adios (NULL, "no messages in %s", folder); /* parse all the message ranges/sequences and set SELECTED */ - for (msgnum = 0; msgnum < msgp; msgnum++) - if (!m_convert (mp, msgs[msgnum])) + for (msgnum = 0; msgnum < msgs.size; msgnum++) + if (!m_convert (mp, msgs.msgs[msgnum])) done (1); seq_setprev (mp); /* set the previous-sequence */ - smsgs = (struct smsg *) - calloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs)); - if (smsgs == NULL) - adios (NULL, "unable to allocate burst storage"); + smsgs = mh_xcalloc(MAXFOLDER + 2, sizeof *smsgs); hi = mp->hghmsg + 1; /* burst all the SELECTED messages */ for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) { if (is_selected (mp, msgnum)) { - if ((numburst = find_delim (msgnum, smsgs)) >= 1) { + if ((numburst = find_delim (msgnum, smsgs, &mimesw)) >= 1) { if (verbosw) printf ("%d message%s exploded from digest %d\n", numburst, numburst > 1 ? "s" : "", msgnum); - burst (&mp, msgnum, smsgs, numburst, inplace, verbosw); + burst (&mp, msgnum, smsgs, numburst, inplace, verbosw, + maildir, mimesw); } else { if (numburst == 0) { if (!quietsw) @@ -165,7 +189,7 @@ main (int argc, char **argv) } } - free ((char *) smsgs); + free(smsgs); context_replace (pfolder, folder); /* update current folder */ /* @@ -185,50 +209,100 @@ main (int argc, char **argv) seq_save (mp); /* synchronize message sequences */ context_save (); /* save the context file */ folder_free (mp); /* free folder/message structure */ - return done (0); + done (0); + return 1; } /* * Scan the message and find the beginning and * end of all the messages in the digest. + * + * If requested, see if the message is MIME-formatted and contains any + * message/rfc822 parts; if so, burst those parts. */ static int -find_delim (int msgnum, struct smsg *smsgs) +find_delim (int msgnum, struct smsg *smsgs, int *mimesw) { - int ld3, wasdlm, msgp; - long pos; + int wasdlm = 0, msgp; + off_t pos; char c, *msgnam; - int cc; char buffer[BUFSIZ]; FILE *in; + CT content; - ld3 = strlen (delim3); + msgnam = m_name (msgnum); - if ((in = fopen (msgnam = m_name (msgnum), "r")) == NULL) + /* + * If mimesw is 1 or 2, try to see if it's got proper MIME formatting. + */ + + if (*mimesw > 0) { + content = parse_mime(msgnam); + if (! content && *mimesw == 2) + return 0; + if (content) { + smsgs[0].s_start = 0; + smsgs[0].s_stop = content->c_begin - 1; + msgp = 1; + find_mime_parts(content, smsgs, &msgp); + free_content(content); + if (msgp == 1 && *mimesw == 2) { + adios (msgnam, "does not have any message/rfc822 parts"); + } else if (msgp > 1) { + *mimesw = 1; + return (msgp - 1); + } + } + } + + *mimesw = 0; + + if ((in = fopen (msgnam, "r")) == NULL) adios (msgnam, "unable to read message"); for (msgp = 0, pos = 0L; msgp <= MAXFOLDER;) { + /* + * We're either at the beginning of the whole message, or + * we're just past the delimiter of the last message. + * Swallow lines until we get to something that's not a newline + */ while (fgets (buffer, sizeof(buffer), in) && buffer[0] == '\n') pos += (long) strlen (buffer); if (feof (in)) break; - fseek (in, pos, SEEK_SET); + + /* + * Reset to the beginning of the last non-blank line, and save our + * starting position. This is where the encapsulated message + * starts. + */ + fseeko (in, pos, SEEK_SET); smsgs[msgp].s_start = pos; + /* + * Read in lines until we get to a message delimiter. + * + * Previously we checked to make sure the preceding line and + * next line was a newline. That actually does not comply with + * RFC 934, so make sure we break on a message delimiter even + * if the previous character was NOT a newline. + */ for (c = 0; fgets (buffer, sizeof(buffer), in); c = buffer[0]) { - if (strncmp (buffer, delim3, ld3) == 0 - && (msgp == 1 || c == '\n') - && ((cc = peekc (in)) == '\n' || cc == EOF)) + if ((wasdlm = CHECKDELIM(buffer))) break; else pos += (long) strlen (buffer); } - wasdlm = strncmp (buffer, delim3, ld3) == 0; - if (smsgs[msgp].s_start != pos) + /* + * Only count as a new message if we got the message delimiter. + * Swallow a blank line if it was right before the message delimiter. + */ + if (smsgs[msgp].s_start != pos && wasdlm) smsgs[msgp++].s_stop = (c == '\n' && wasdlm) ? pos - 1 : pos; + if (feof (in)) { #if 0 if (wasdlm) { @@ -242,7 +316,45 @@ find_delim (int msgnum, struct smsg *smsgs) } fclose (in); - return (msgp - 1); /* toss "End of XXX Digest" */ + return (msgp - 1); /* return the number of messages burst */ +} + + +/* + * Find any MIME content in the message that is a message/rfc822 and add + * it to the list of messages to burst. + */ + +static void +find_mime_parts (CT content, struct smsg *smsgs, int *msgp) +{ + struct multipart *m; + struct part *part; + + /* + * If we have a message/rfc822, then it's easy. + */ + + if (content->c_type == CT_MESSAGE && + content->c_subtype == MESSAGE_RFC822) { + smsgs[*msgp].s_start = content->c_begin; + smsgs[*msgp].s_stop = content->c_end; + (*msgp)++; + return; + } + + /* + * Otherwise, if we do have multiparts, try all of the sub-parts. + */ + + if (content->c_type == CT_MULTIPART) { + m = (struct multipart *) content->c_ctparams; + + for (part = m->mp_parts; part; part = part->mp_next) + find_mime_parts(part->mp_part, smsgs, msgp); + } + + return; } @@ -252,7 +364,7 @@ find_delim (int msgnum, struct smsg *smsgs) static void burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, - int inplace, int verbosw) + int inplace, int verbosw, char *maildir, int mimesw) { int i, j, mode; char *msgnam; @@ -264,7 +376,8 @@ burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, if ((in = fopen (msgnam = m_name (msgnum), "r")) == NULL) adios (msgnam, "unable to read message"); - mode = fstat (fileno(in), &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot(); + mode = + fstat (fileno(in), &st) != NOTOK ? (int) (st.st_mode & 0777) : m_gmprot(); mp = *mpp; /* @@ -292,6 +405,9 @@ burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, * If -inplace is given, renumber the messages after the * source message, to make room for each of the messages * contained within the digest. + * + * This is equivalent to refiling a message from the point + * of view of the external hooks. */ if (inplace) { for (i = mp->hghmsg; j > msgnum; i--, j--) { @@ -303,6 +419,11 @@ burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, if (rename (f2, f1) == NOTOK) admonish (f1, "unable to rename %s to", f2); + + (void)snprintf(f1, sizeof (f1), "%s/%d", maildir, i); + (void)snprintf(f2, sizeof (f2), "%s/%d", maildir, j); + ext_hook("ref-hook", f1, f2); + copy_msg_flags (mp, i, j); clear_msg_flags (mp, j); mp->msgflags |= SEQMOD; @@ -312,29 +433,59 @@ burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, unset_selected (mp, msgnum); - /* new hghmsg is hghmsg + numburst */ + /* new hghmsg is hghmsg + numburst + * + * At this point, there is an array of numburst smsgs, each element of + * which contains the starting and stopping offsets (seeks) of the message + * in the digest. The inplace flag is set if the original digest is replaced + * by a message containing the table of contents. smsgs[0] is that table of + * contents. Go through the message numbers in reverse order (high to low). + * + * Set f1 to the name of the destination message, f2 to the name of a scratch + * file. Extract a message from the digest to the scratch file. Move the + * original message to a backup file if the destination message number is the + * same as the number of the original message, which only happens if the + * inplace flag is set. Then move the scratch file to the destination message. + * + * Moving the original message to the backup file is equivalent to deleting the + * message from the point of view of the external hooks. And bursting each + * message is equivalent to adding a new message. + */ + i = inplace ? msgnum + numburst : mp->hghmsg; for (j = numburst; j >= (inplace ? 0 : 1); i--, j--) { + char *tempfile; + + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, &out)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + strncpy (f2, tempfile, sizeof(f2)); strncpy (f1, m_name (i), sizeof(f1)); - strncpy (f2, m_scratch ("", invo_name), sizeof(f2)); + if (verbosw && i != msgnum) printf ("message %d of digest %d becomes message %d\n", j, msgnum, i); - if ((out = fopen (f2, "w")) == NULL) - adios (f2, "unable to write message"); chmod (f2, mode); - fseek (in, smsgs[j].s_start, SEEK_SET); + fseeko (in, smsgs[j].s_start, SEEK_SET); cpybrst (in, out, msgnam, f2, - (int) (smsgs[j].s_stop - smsgs[j].s_start)); + (int) (smsgs[j].s_stop - smsgs[j].s_start), mimesw); fclose (out); if (i == msgnum) { strncpy (f3, m_backup (f1), sizeof(f3)); if (rename (f1, f3) == NOTOK) admonish (f3, "unable to rename %s to", f1); + + (void)snprintf(f3, sizeof (f3), "%s/%d", maildir, i); + ext_hook("del-hook", f3, NULL); } if (rename (f2, f1) == NOTOK) admonish (f1, "unable to rename %s to", f2); + + (void)snprintf(f3, sizeof (f3), "%s/%d", maildir, i); + ext_hook("add-hook", f3, NULL); + copy_msg_flags (mp, i, msgnum); mp->msgflags |= SEQMOD; } @@ -346,18 +497,19 @@ burst (struct msgs **mpp, int msgnum, struct smsg *smsgs, int numburst, #define S1 0 #define S2 1 #define S3 2 +#define S4 3 /* - * Copy a mesage which is being burst out of a digest. + * Copy a message which is being burst out of a digest. * It will remove any "dashstuffing" in the message. */ static void -cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len) +cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len, int mime) { - register int c, state; + int c, state; - for (state = S1; (c = fgetc (in)) != EOF && len > 0; len--) { + for (state = mime ? S4 : S1; (c = fgetc (in)) != EOF && len > 0; len--) { if (c == 0) continue; switch (state) { @@ -369,6 +521,7 @@ cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len) default: state = S2; + /* FALLTHRU */ case '\n': fputc (c, out); break; @@ -379,6 +532,7 @@ cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len) switch (c) { case '\n': state = S1; + /* FALLTHRU */ default: fputc (c, out); break; @@ -398,6 +552,10 @@ cpybrst (FILE *in, FILE *out, char *ifile, char *ofile, int len) break; } break; + + case S4: + fputc (c, out); + break; } }