From 4b1f4b72b04b8b6208216dd0c65c3982dadb48fa Mon Sep 17 00:00:00 2001 From: "Steffen (Daode) Nurpmeso" Date: Mon, 23 Nov 2015 14:33:57 +0100 Subject: [PATCH] enum havespecs (HAVE_*) -> content_info (CI_*), ++.. First try to allow the MIME parser to produce failures. TODO After v15.0 S-nail will do TODO TODO - Parse MIME structure into DOM. TODO - Analyze DOM according to desired action. TODO I.e., modify DOM structure (e.g., explode decrypted parts), TODO create filter chains for each part as necessary. TODO - Walk the final DOM according to action, directly address TODO those parts that should be addressed. TODO TODO - A [Dd]ecryption operation should fail when decryption TODO fails. We need atomic operations with rollback! --- cmd1.c | 8 +++--- fio.c | 4 +-- message.c | 5 ++-- mime_parse.c | 81 +++++++++++++++++++++++++++++++++++++++--------------------- nail.h | 25 +++++++++++++------ pop3.c | 4 +-- send.c | 61 ++++++++++++++++++++++++++++++++++----------- 7 files changed, 127 insertions(+), 61 deletions(-) diff --git a/cmd1.c b/cmd1.c index 288edc47..8b6de4f0 100644 --- a/cmd1.c +++ b/cmd1.c @@ -69,7 +69,7 @@ static int _headers(int msgspec); /* Show the requested messages */ static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe, - bool_t dodecode, char *cmd, ui64_t *tstats); + bool_t donotdecode, char *cmd, ui64_t *tstats); /* Pipe the requested messages */ static int _pipe1(char *str, int doign); @@ -985,7 +985,7 @@ jleave: static int _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe, - bool_t dodecode, char *cmd, ui64_t *tstats) + bool_t donotdecode, char *cmd, ui64_t *tstats) { struct n_sigman sm; ui64_t mstats[1]; @@ -998,7 +998,7 @@ _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe, NYD_ENTER; {/* C89.. */ enum sendaction const action = ((dopipe && ok_blook(piperaw)) - ? SEND_MBOX : dodecode + ? SEND_MBOX : donotdecode ? SEND_SHOW : doign ? SEND_TODISP : SEND_TODISP_ALL); bool_t const volatile formfeed = (dopipe && ok_blook(page)); @@ -1023,7 +1023,7 @@ _type1(int *msgvec, bool_t doign, bool_t dopage, bool_t dopipe, if (!dopage) { for (ip = msgvec; *ip && PTRCMP(ip - msgvec, <, msgCount); ++ip) { mp = message + *ip - 1; - if (!(mp->m_have & HAVE_BODY)) + if (!(mp->m_content_info & CI_HAVE_BODY)) if (get_body(mp) != OKAY) goto jcleanup_leave; nlines += mp->m_lines + 1; /* Message info XXX and PARTS... */ diff --git a/fio.c b/fio.c index ba9ab5bd..cefe479b 100644 --- a/fio.c +++ b/fio.c @@ -277,7 +277,7 @@ setptr(FILE *ibuf, off_t offset) if (fgetline(&linebuf, &linesize, &filesize, &cnt, ibuf, 0) == NULL) { self.m_xsize = self.m_size; self.m_xlines = self.m_lines; - self.m_have = HAVE_HEADER | HAVE_BODY; + self.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY; if (selfcnt > 0) message_append(&self); message_append_null(); @@ -316,7 +316,7 @@ setptr(FILE *ibuf, off_t offset) } self.m_xsize = self.m_size; self.m_xlines = self.m_lines; - self.m_have = HAVE_HEADER | HAVE_BODY; + self.m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY; if (selfcnt++ > 0) message_append(&self); msgCount++; diff --git a/message.c b/message.c index b25c6693..969135a4 100644 --- a/message.c +++ b/message.c @@ -1258,10 +1258,11 @@ setinput(struct mailbox *mp, struct message *m, enum needspec need){ switch(need){ case NEED_HEADER: - ok = (m->m_have & HAVE_HEADER) ? OKAY : a_message_get_header(m); + ok = (m->m_content_info & CI_HAVE_HEADER) ? OKAY + : a_message_get_header(m); break; case NEED_BODY: - ok = (m->m_have & HAVE_BODY) ? OKAY : get_body(m); + ok = (m->m_content_info & CI_HAVE_BODY) ? OKAY : get_body(m); break; case NEED_UNSPEC: ok = OKAY; diff --git a/mime_parse.c b/mime_parse.c index 3d24f99b..0a402137 100644 --- a/mime_parse.c +++ b/mime_parse.c @@ -39,6 +39,9 @@ # include "nail.h" #endif +/* Fetch plain */ +static char * _mime_parse_ct_plain_from_ct(char const *cth); + static bool_t _mime_parse_part(struct message *zmp, struct mimepart *ip, enum mime_parse_flags mpf, int level); @@ -50,32 +53,44 @@ static void _mime_parse_pkcs7(struct message *zmp, struct mimepart *ip, enum mime_parse_flags mpf, int level); #endif -static void _mime_parse_multipart(struct message *zmp, +static bool_t _mime_parse_multipart(struct message *zmp, struct mimepart *ip, enum mime_parse_flags mpf, int level); static void __mime_parse_new(struct mimepart *ip, struct mimepart **np, off_t offs, int *part); static void __mime_parse_end(struct mimepart **np, off_t xoffs, long lines); +static char * +_mime_parse_ct_plain_from_ct(char const *cth) +{ + char *rv_b, *rv; + NYD2_ENTER; + + rv_b = savestr(cth); + + if ((rv = strchr(rv_b, ';')) != NULL) + *rv = '\0'; + + rv = rv_b + strlen(rv_b); + while (rv > rv_b && blankchar(rv[-1])) + --rv; + *rv = '\0'; + NYD2_LEAVE; + return rv_b; +} + static bool_t _mime_parse_part(struct message *zmp, struct mimepart *ip, enum mime_parse_flags mpf, int level) { - char *cp_b, *cp; + char *cp; bool_t rv = FAL0; NYD_ENTER; ip->m_ct_type = hfield1("content-type", (struct message*)ip); - if (ip->m_ct_type != NULL) { - ip->m_ct_type_plain = cp_b = savestr(ip->m_ct_type); - if ((cp = strchr(cp_b, ';')) != NULL) - *cp = '\0'; - cp = cp_b + strlen(cp_b); - while (cp > cp_b && blankchar(cp[-1])) - --cp; - *cp = '\0'; - } else if (ip->m_parent != NULL && - ip->m_parent->m_mimecontent == MIME_DIGEST) + if (ip->m_ct_type != NULL) + ip->m_ct_type_plain = _mime_parse_ct_plain_from_ct(ip->m_ct_type); + else if (ip->m_parent != NULL && ip->m_parent->m_mimecontent == MIME_DIGEST) ip->m_ct_type_plain = "message/rfc822"; else ip->m_ct_type_plain = "text/plain"; @@ -117,9 +132,11 @@ _mime_parse_part(struct message *zmp, struct mimepart *ip, if (mpf & MIME_PARSE_DECRYPT) { #ifdef HAVE_SSL _mime_parse_pkcs7(zmp, ip, mpf, level); + if (ip->m_content_info & CI_ENCRYPTED_OK) + ip->m_content_info |= CI_EXPANDED; break; #else - n_err(_("No SSL support compiled in\n")); + n_err(_("No SSL / S/MIME support compiled in\n")); goto jleave; #endif } @@ -132,7 +149,8 @@ _mime_parse_part(struct message *zmp, struct mimepart *ip, case MIME_SIGNED: case MIME_ENCRYPTED: case MIME_MULTI: - _mime_parse_multipart(zmp, ip, mpf, level); + if (!_mime_parse_multipart(zmp, ip, mpf, level)) + goto jleave; break; case MIME_822: _mime_parse_rfc822(zmp, ip, mpf, level); @@ -176,7 +194,7 @@ _mime_parse_rfc822(struct message *zmp, struct mimepart *ip, np = csalloc(1, sizeof *np); np->m_flag = MNOFROM; - np->m_have = HAVE_HEADER | HAVE_BODY; + np->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY; np->m_block = mailx_blockof(offs); np->m_offset = mailx_offsetof(offs); np->m_size = np->m_xsize = cnt; @@ -185,7 +203,7 @@ _mime_parse_rfc822(struct message *zmp, struct mimepart *ip, np->m_parent = ip; ip->m_multipart = np; - if (ok_blook(rfc822_body_from_)) { + if (!(mpf & MIME_PARSE_SHALLOW) && ok_blook(rfc822_body_from_)) { substdate((struct message*)np); np->m_from = fakefrom((struct message*)np);/* TODO strip MNOFROM flag? */ } @@ -212,33 +230,33 @@ _mime_parse_pkcs7(struct message *zmp, struct mimepart *ip, if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) { np = csalloc(1, sizeof *np); np->m_flag = xmp->m_flag; - np->m_have = xmp->m_have; + np->m_content_info = xmp->m_content_info | CI_ENCRYPTED | CI_ENCRYPTED_OK; np->m_block = xmp->m_block; np->m_offset = xmp->m_offset; np->m_size = xmp->m_size; np->m_xsize = xmp->m_xsize; np->m_lines = xmp->m_lines; np->m_xlines = xmp->m_xlines; - np->m_partstring = ip->m_partstring; + + /* TODO using part "1" for decrypted content is a hack */ + if ((np->m_partstring = ip->m_partstring) == NULL) + ip->m_partstring = np->m_partstring = n_UNCONST("1"); if (_mime_parse_part(zmp, np, mpf, level + 1) == OKAY) { + ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_OK; np->m_parent = ip; ip->m_multipart = np; } - } + } else + ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_BAD; NYD_LEAVE; } #endif /* HAVE_SSL */ -static void +static bool_t _mime_parse_multipart(struct message *zmp, struct mimepart *ip, enum mime_parse_flags mpf, int level) { - /* TODO Instead of the recursive multiple run parse we have today, - * TODO the send/MIME layer rewrite must create a "tree" of parts with - * TODO a single-pass parse, then address each part directly as - * TODO necessary; since boundaries start with -- and the content - * TODO rather forms a stack this is pretty cheap indeed! */ struct mimepart *np = NULL; char *boundary, *line = NULL; size_t linesize = 0, linelen, cnt, boundlen; @@ -261,7 +279,11 @@ _mime_parse_multipart(struct message *zmp, struct mimepart *ip, break; offs = ftell(ibuf); + /* TODO using part "1" for decrypted content is a hack */ + if (ip->m_partstring == NULL) + ip->m_partstring = n_UNCONST("1"); __mime_parse_new(ip, &np, offs, NULL); + while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) { /* XXX linelen includes LF */ if (!((lines > 0 || part == 0) && linelen > boundlen && @@ -308,9 +330,12 @@ _mime_parse_multipart(struct message *zmp, struct mimepart *ip, for (np = ip->m_multipart; np != NULL; np = np->m_nextpart) if (np->m_mimecontent != MIME_DISCARD) _mime_parse_part(zmp, np, mpf, level + 1); - free(line); + jleave: + if (line != NULL) + free(line); NYD_LEAVE; + return (ip != NULL); } static void @@ -323,7 +348,7 @@ __mime_parse_new(struct mimepart *ip, struct mimepart **np, off_t offs, *np = csalloc(1, sizeof **np); (*np)->m_flag = MNOFROM; - (*np)->m_have = HAVE_HEADER | HAVE_BODY; + (*np)->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY; (*np)->m_block = mailx_blockof(offs); (*np)->m_offset = mailx_offsetof(offs); @@ -370,7 +395,7 @@ mime_parse_msg(struct message *mp, enum mime_parse_flags mpf) ip = csalloc(1, sizeof *ip); ip->m_flag = mp->m_flag; - ip->m_have = mp->m_have; + ip->m_content_info = mp->m_content_info; ip->m_block = mp->m_block; ip->m_offset = mp->m_offset; ip->m_size = mp->m_size; diff --git a/nail.h b/nail.h index b8393d6e..943feb8d 100644 --- a/nail.h +++ b/nail.h @@ -1033,7 +1033,8 @@ enum b64flags { enum mime_parse_flags { MIME_PARSE_NONE = 0, MIME_PARSE_DECRYPT = 1<<0, - MIME_PARSE_PARTS = 1<<1 + MIME_PARSE_PARTS = 1<<1, + MIME_PARSE_SHALLOW = 1<<2 }; enum mime_handler_flags { @@ -2069,10 +2070,18 @@ enum needspec { NEED_BODY /* need header and body of a message */ }; -enum havespec { - HAVE_NOTHING, /* nothing downloaded yet */ - HAVE_HEADER = 01, /* header is downloaded */ - HAVE_BODY = 02 /* entire message is downloaded */ +enum content_info { + CI_NOTHING, /* Nothing downloaded yet */ + CI_HAVE_HEADER = 1<<0, /* Header is downloaded */ + CI_HAVE_BODY = 1<<1, /* Entire message is downloaded */ + CI_MIME_ERRORS = 1<<2, /* Defective MIME structure */ + CI_EXPANDED = 1<<3, /* Container part (pk7m) exploded into X */ + CI_SIGNED = 1<<4, /* Has a signature.. */ + CI_SIGNED_OK = 1<<5, /* ..verified ok.. */ + CI_SIGNED_BAD = 1<<6, /* ..verified bad (missing key).. */ + CI_ENCRYPTED = 1<<7, /* Is encrypted.. */ + CI_ENCRYPTED_OK = 1<<8, /* ..decryption possible/ok.. */ + CI_ENCRYPTED_BAD = 1<<9 /* ..not possible/ok */ }; /* flag bits. Attention: Flags that are used in cache.c may not change */ @@ -2111,8 +2120,8 @@ enum mflag { #define visible(mp) (((mp)->m_flag & MMNDEL) == 0) struct mimepart { - enum mflag m_flag; /* flags */ - enum havespec m_have; /* downloaded parts of the part */ + enum mflag m_flag; + enum content_info m_content_info; #ifdef HAVE_SPAM ui32_t m_spamscore; /* Spam score as int, 24:8 bits */ #endif @@ -2140,7 +2149,7 @@ struct mimepart { struct message { enum mflag m_flag; /* flags */ - enum havespec m_have; /* downloaded parts of the message */ + enum content_info m_content_info; #ifdef HAVE_SPAM ui32_t m_spamscore; /* Spam score as int, 24:8 bits */ #endif diff --git a/pop3.c b/pop3.c index 741c3bf7..12c51ecf 100644 --- a/pop3.c +++ b/pop3.c @@ -667,10 +667,10 @@ jretry: switch (need) { case NEED_HEADER: - m->m_have |= HAVE_HEADER; + m->m_content_info |= CI_HAVE_HEADER; break; case NEED_BODY: - m->m_have |= HAVE_HEADER | HAVE_BODY; + m->m_content_info |= CI_HAVE_HEADER | CI_HAVE_BODY; m->m_xlines = m->m_lines; m->m_xsize = m->m_size; break; diff --git a/send.c b/send.c index 3b841a24..140adef6 100644 --- a/send.c +++ b/send.c @@ -107,8 +107,11 @@ _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */ /* Take care of "99.99", i.e., 5 */ if ((cp = mpp->m_partstring) == NULL || cp[0] == '\0') cp = "?"; - if (level || (cp[0] != '1' && cp[1] == '\0')) + if (level || (cp[0] != '1' && cp[1] == '\0') || (cp[0] == '1' && /* TODO */ + cp[1] == '.' && cp[2] != '1')) /* TODO code should not look like so */ _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + + /* Part id, content-type, encoding, charset */ if (cpre != NULL) _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL); _out("[-- #", 5, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL); @@ -151,25 +154,50 @@ _print_part_info(FILE *obuf, struct mimepart const *mpp, /* TODO strtofmt.. */ _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL); _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL,NULL); - if (n_ignore_is_ign(doitp, "content-disposition", 19) && - mpp->m_filename != NULL && *mpp->m_filename != '\0') { - makeprint(n_str_add_cp(&ti, mpp->m_filename), &to); - free(ti.s); - to.l = delctrl(to.s, to.l); - + /* */ + if (mpp->m_content_info & CI_MIME_ERRORS) { if (cpre != NULL) _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + + ti.l = strlen(ti.s = n_UNCONST(_("Defective MIME structure"))); + makeprint(&ti, &to); + ti.s = NULL; /* Not allocated! */ + to.l = delctrl(to.s, to.l); _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + free(to.s); + _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); if (csuf != NULL) _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + } + + /* Filename */ + if (n_ignore_is_ign(doitp, "content-disposition", 19) && + mpp->m_filename != NULL && *mpp->m_filename != '\0') { + if (cpre != NULL) + _out(cpre->s, cpre->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, + NULL, NULL); + _out("[-- ", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + ti.l = 0; + makeprint(n_str_add_cp(&ti, mpp->m_filename), &to); + to.l = delctrl(to.s, to.l); + _out(to.s, to.l, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); free(to.s); + + _out(" --]", 4, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); + if (csuf != NULL) + _out(csuf->s, csuf->l, obuf, CONV_NONE, SEND_MBOX, qf, stats, + NULL, NULL); + _out("\n", 1, obuf, CONV_NONE, SEND_MBOX, qf, stats, NULL, NULL); } + + if (ti.s != NULL) + free(ti.s); NYD2_LEAVE; } @@ -362,9 +390,13 @@ sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf, n_UNINIT(term_infd, 0); n_UNINIT(cnt, 0); - if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart && - action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) - goto jskip; + quoteflt_reset(qf, obuf); + + if (ip->m_mimecontent == MIME_PKCS7) { + if (ip->m_multipart && + action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) + goto jheaders_skip; + } dostat = 0; if (level == 0) { @@ -383,7 +415,7 @@ sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf, cnt = ip->m_size; if (ip->m_mimecontent == MIME_DISCARD) - goto jskip; + goto jheaders_skip; if (!(ip->m_flag & MNOFROM)) while (cnt && (c = getc(ibuf)) != EOF) { @@ -397,7 +429,6 @@ sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf, ? CONV_FROMHDR : CONV_NONE; /* Work the headers */ - quoteflt_reset(qf, obuf); /* C99 */{ enum { HPS_NONE = 0, @@ -571,7 +602,7 @@ sendpart(struct message *zmp, struct mimepart *ip, FILE * volatile obuf, line = NULL; tmpname = NULL; -jskip: +jheaders_skip: memset(&mh, 0, sizeof mh); switch (ip->m_mimecontent) { @@ -960,7 +991,7 @@ jpipe_close: action == SEND_RFC822 || action == SEND_SHOW) convert = CONV_NONE; #ifdef HAVE_ICONV - if ((action == SEND_TODISP || action == SEND_TODISP_ALL || + else if ((action == SEND_TODISP || action == SEND_TODISP_ALL || action == SEND_QUOTE || action == SEND_QUOTE_ALL || action == SEND_TOSRCH) && (ip->m_mimecontent == MIME_TEXT_PLAIN || @@ -1466,7 +1497,7 @@ sendmp(struct message *mp, FILE *obuf, struct n_ignore const *doitp, mpf = MIME_PARSE_NONE; if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW) - mpf |= MIME_PARSE_DECRYPT | MIME_PARSE_PARTS; + mpf |= MIME_PARSE_PARTS | MIME_PARSE_DECRYPT; if ((ip = mime_parse_msg(mp, mpf)) == NULL) goto jleave; -- 2.11.4.GIT