char *rfc2047_decode_unicode(const char *text, const struct unicode_info *mychset, int options) { struct decode_unicode_s s; char *p=0; s.mychset=mychset; s.options=0; s.bufptr=0; s.bufsize=1; if (rfc2047_decode(text, &save_unicode, &s)) return (0); s.bufptr=p=malloc(s.bufsize); if (!s.bufptr) return (0); s.bufsize=0; if (rfc2047_decode(text, &save_unicode, &s)) { free(p); return (0); } save_unicode_text("", 1, (void *)&s); return (p); }
/* rfc2047 decode them in case of an error */ static void decode_descriptions (BODY *b) { BODY *t; for (t = b; t; t = t->next) { if (t->description) { rfc2047_decode (&t->description); } if (t->parts) decode_descriptions (t->parts); } }
/* Ok, the only reason for not merging this with mutt_copy_header() * below is to avoid creating a HEADER structure in message_handler(). * Also, this one will wrap headers much more aggressively than the other one. */ int mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, const char *prefix) { int from = 0; int this_is_from; int ignore = 0; char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */ char *nl; LIST *t; char **headers; int hdr_count; int x; char *this_one = NULL; size_t this_one_len = 0; int error; if (ftello (in) != off_start) fseeko (in, off_start, 0); buf[0] = '\n'; buf[1] = 0; if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0) { /* Without these flags to complicate things * we can do a more efficient line to line copying */ while (ftello (in) < off_end) { nl = strchr (buf, '\n'); if ((fgets (buf, sizeof (buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { ignore = 1; if (!from && mutt_strncmp ("From ", buf, 5) == 0) { if ((flags & CH_FROM) == 0) continue; from = 1; } else if (flags & (CH_NOQFROM) && ascii_strncasecmp (">From ", buf, 6) == 0) continue; else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && (ascii_strncasecmp ("Status:", buf, 7) == 0 || ascii_strncasecmp ("X-Status:", buf, 9) == 0)) continue; if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || ascii_strncasecmp ("Lines:", buf, 6) == 0)) continue; if ((flags & CH_UPDATE_REFS) && ascii_strncasecmp ("References:", buf, 11) == 0) continue; if ((flags & CH_UPDATE_IRT) && ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) continue; ignore = 0; } if (!ignore && fputs (buf, out) == EOF) return (-1); } return 0; } hdr_count = 1; x = 0; error = FALSE; /* We are going to read and collect the headers in an array * so we are able to do re-ordering. * First count the number of entries in the array */ if (flags & CH_REORDER) { for (t = HeaderOrderList; t; t = t->next) { dprint(3, (debugfile, "Reorder list: %s\n", t->data)); hdr_count++; } } dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not")); headers = safe_calloc (hdr_count, sizeof (char *)); /* Read all the headers into the array */ while (ftello (in) < off_end) { nl = strchr (buf, '\n'); /* Read a line */ if ((fgets (buf, sizeof (buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { /* Do we have anything pending? */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode (&this_one)) rfc2047_decode (&this_one); this_one_len = mutt_strlen (this_one); } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_strlen (headers[x]); safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ FREE (&this_one); } this_one = NULL; } ignore = 1; this_is_from = 0; if (!from && mutt_strncmp ("From ", buf, 5) == 0) { if ((flags & CH_FROM) == 0) continue; this_is_from = from = 1; } else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ /* note: CH_FROM takes precedence over header weeding. */ if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) && (flags & CH_WEED) && mutt_matches_ignore (buf, Ignore) && !mutt_matches_ignore (buf, UnIgnore)) continue; if ((flags & CH_WEED_DELIVERED) && ascii_strncasecmp ("Delivered-To:", buf, 13) == 0) continue; if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && (ascii_strncasecmp ("Status:", buf, 7) == 0 || ascii_strncasecmp ("X-Status:", buf, 9) == 0)) continue; if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || ascii_strncasecmp ("Lines:", buf, 6) == 0)) continue; if ((flags & CH_MIME) && ((ascii_strncasecmp ("content-", buf, 8) == 0 && (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 || ascii_strncasecmp ("type:", buf + 8, 5) == 0)) || ascii_strncasecmp ("mime-version:", buf, 13) == 0)) continue; if ((flags & CH_UPDATE_REFS) && ascii_strncasecmp ("References:", buf, 11) == 0) continue; if ((flags & CH_UPDATE_IRT) && ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) continue; /* Find x -- the array entry where this header is to be saved */ if (flags & CH_REORDER) { for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++) { if (!ascii_strncasecmp (buf, t->data, mutt_strlen (t->data))) { dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data, buf)); break; } } } ignore = 0; } /* If beginning of header */ if (!ignore) { dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count)); if (!this_one) { this_one = safe_strdup (buf); this_one_len = mutt_strlen (this_one); } else { int blen = mutt_strlen (buf); safe_realloc (&this_one, this_one_len + blen + sizeof (char)); strcat (this_one + this_one_len, buf); /* __STRCAT_CHECKED__ */ this_one_len += blen; } } } /* while (ftello (in) < off_end) */ /* Do we have anything pending? -- XXX, same code as in above in the loop. */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode (&this_one)) rfc2047_decode (&this_one); this_one_len = mutt_strlen (this_one); } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_strlen (headers[x]); safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ FREE (&this_one); } this_one = NULL; } /* Now output the headers in order */ for (x = 0; x < hdr_count; x++) { if (headers[x]) { #if 0 if (flags & CH_DECODE) rfc2047_decode (&headers[x]); #endif /* We couldn't do the prefixing when reading because RFC 2047 * decoding may have concatenated lines. */ if (flags & (CH_DECODE|CH_PREFIX)) { if (mutt_write_one_header (out, 0, headers[x], flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap) - (flags & CH_PAGER_BUILTIN ? SidebarWidth : 0), flags) == -1) { error = TRUE; break; } } else { if (fputs (headers[x], out) == EOF) { error = TRUE; break; } } } } /* Free in a separate loop to be sure that all headers are freed * in case of error. */ for (x = 0; x < hdr_count; x++) FREE (&headers[x]); FREE (&headers); if (error) return (-1); return (0); }
int mime_acl_check(uschar *acl, FILE *f, struct mime_boundary_context *context, uschar **user_msgptr, uschar **log_msgptr) { int rc = OK; uschar * header = NULL; struct mime_boundary_context nested_context; /* reserve a line buffer to work in */ header = store_get(MIME_MAX_HEADER_SIZE+1); /* Not actually used at the moment, but will be vital to fixing * some RFC 2046 nonconformance later... */ nested_context.parent = context; /* loop through parts */ while(1) { /* reset all per-part mime variables */ mime_vars_reset(); /* If boundary is null, we assume that *f is positioned on the start of headers (for example, at the very beginning of a message. If a boundary is given, we must first advance to it to reach the start of the next header block. */ /* NOTE -- there's an error here -- RFC2046 specifically says to * check for outer boundaries. This code doesn't do that, and * I haven't fixed this. * * (I have moved partway towards adding support, however, by adding * a "parent" field to my new boundary-context structure.) */ if (context) for (;;) { if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f)) { /* Hit EOF or read error. Ugh. */ DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n"); return rc; } /* boundary line must start with 2 dashes */ if ( Ustrncmp(header, "--", 2) == 0 && Ustrncmp(header+2, context->boundary, Ustrlen(context->boundary)) == 0 ) { /* found boundary */ if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0) { /* END boundary found */ DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n", context->boundary); return rc; } DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n", context->boundary); break; } } /* parse headers, set up expansion variables */ while (mime_get_header(f, header)) { struct mime_header * mh; /* look for interesting headers */ for (mh = mime_header_list; mh < mime_header_list + mime_header_list_size; mh++) if (strncmpic(mh->name, header, mh->namelen) == 0) { uschar * p = header + mh->namelen; uschar * q; /* grab the value (normalize to lower case) and copy to its corresponding expansion variable */ for (q = p; *q != ';' && *q; q++) ; *mh->value = string_copynlc(p, q-p); DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n", mh->name, *mh->value); if (*(p = q)) p++; /* jump past the ; */ { uschar * mime_fname = NULL; uschar * mime_fname_rfc2231 = NULL; uschar * mime_filename_charset = NULL; BOOL decoding_failed = FALSE; /* grab all param=value tags on the remaining line, check if they are interesting */ while (*p) { mime_parameter * mp; DEBUG(D_acl) debug_printf("MIME: considering paramlist '%s'\n", p); if ( !mime_filename && strncmpic(CUS"content-disposition:", header, 20) == 0 && strncmpic(CUS"filename*", p, 9) == 0 ) { /* RFC 2231 filename */ uschar * q; /* find value of the filename */ p += 9; while(*p != '=' && *p) p++; if (*p) p++; /* p is filename or NUL */ q = mime_param_val(&p); /* p now trailing ; or NUL */ if (q && *q) { uschar * temp_string, * err_msg; int slen; /* build up an un-decoded filename over successive filename*= parameters (for use when 2047 decode fails) */ mime_fname_rfc2231 = string_sprintf("%#s%s", mime_fname_rfc2231, q); if (!decoding_failed) { int size; if (!mime_filename_charset) { uschar * s = q; /* look for a ' in the "filename" */ while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ if ((size = s-q) > 0) mime_filename_charset = string_copyn(q, size); if (*(p = s)) p++; while(*p == '\'') p++; /* p is after 2nd ' */ } else p = q; DEBUG(D_acl) debug_printf("MIME: charset %s fname '%s'\n", mime_filename_charset ? mime_filename_charset : US"<NULL>", p); temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); DEBUG(D_acl) debug_printf("MIME: 2047-name %s\n", temp_string); temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', NULL, &err_msg); DEBUG(D_acl) debug_printf("MIME: plain-name %s\n", temp_string); size = Ustrlen(temp_string); if (size == slen) decoding_failed = TRUE; else /* build up a decoded filename over successive filename*= parameters */ mime_filename = mime_fname = mime_fname ? string_sprintf("%s%s", mime_fname, temp_string) : temp_string; } } } else /* look for interesting parameters */ for (mp = mime_parameter_list; mp < mime_parameter_list + nelem(mime_parameter_list); mp++ ) if (strncmpic(mp->name, p, mp->namelen) == 0) { uschar * q; uschar * dummy_errstr; /* grab the value and copy to its expansion variable */ p += mp->namelen; q = mime_param_val(&p); /* p now trailing ; or NUL */ *mp->value = q && *q ? rfc2047_decode(q, check_rfc2047_length, NULL, 32, NULL, &dummy_errstr) : NULL; DEBUG(D_acl) debug_printf( "MIME: found %s parameter in %s header, value '%s'\n", mp->name, mh->name, *mp->value); break; /* done matching param names */ } /* There is something, but not one of our interesting parameters. Advance past the next semicolon */ p = mime_next_semicolon(p); if (*p) p++; } /* param scan on line */ if (strncmpic(CUS"content-disposition:", header, 20) == 0) { if (decoding_failed) mime_filename = mime_fname_rfc2231; DEBUG(D_acl) debug_printf( "MIME: found %s parameter in %s header, value is '%s'\n", "filename", mh->name, mime_filename); } } } } /* set additional flag variables (easier access) */ if ( mime_content_type && Ustrncmp(mime_content_type,"multipart",9) == 0 ) mime_is_multipart = 1; /* Make a copy of the boundary pointer. Required since mime_boundary is global and can be overwritten further down in recursion */ nested_context.boundary = mime_boundary; /* raise global counter */ mime_part_count++; /* copy current file handle to global variable */ mime_stream = f; mime_current_boundary = context ? context->boundary : 0; /* Note the context */ mime_is_coverletter = !(context && context->context == MBC_ATTACHMENT); /* call ACL handling function */ rc = acl_check(ACL_WHERE_MIME, NULL, acl, user_msgptr, log_msgptr); mime_stream = NULL; mime_current_boundary = NULL; if (rc != OK) break; /* If we have a multipart entity and a boundary, go recursive */ if ( (mime_content_type != NULL) && (nested_context.boundary != NULL) && (Ustrncmp(mime_content_type,"multipart",9) == 0) ) { DEBUG(D_acl) debug_printf("MIME: Entering multipart recursion, boundary '%s'\n", nested_context.boundary); nested_context.context = context && context->context == MBC_ATTACHMENT ? MBC_ATTACHMENT : Ustrcmp(mime_content_type,"multipart/alternative") == 0 || Ustrcmp(mime_content_type,"multipart/related") == 0 ? MBC_COVERLETTER_ALL : MBC_COVERLETTER_ONESHOT; rc = mime_acl_check(acl, f, &nested_context, user_msgptr, log_msgptr); if (rc != OK) break; } else if ( (mime_content_type != NULL) && (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) ) { const uschar *rfc822name = NULL; uschar filename[2048]; int file_nr = 0; int result = 0; /* must find first free sequential filename */ do { struct stat mystat; (void)string_format(filename, 2048, "%s/scan/%s/__rfc822_%05u", spool_directory, message_id, file_nr++); /* security break */ if (file_nr >= 128) goto NO_RFC822; result = stat(CS filename,&mystat); } while (result != -1); rfc822name = filename; /* decode RFC822 attachment */ mime_decoded_filename = NULL; mime_stream = f; mime_current_boundary = context ? context->boundary : NULL; mime_decode(&rfc822name); mime_stream = NULL; mime_current_boundary = NULL; if (!mime_decoded_filename) /* decoding failed */ { log_write(0, LOG_MAIN, "mime_regex acl condition warning - could not decode RFC822 MIME part to file."); rc = DEFER; goto out; } mime_decoded_filename = NULL; } NO_RFC822: /* If the boundary of this instance is NULL, we are finished here */ if (!context) break; if (context->context == MBC_COVERLETTER_ONESHOT) context->context = MBC_ATTACHMENT; } out: mime_vars_reset(); return rc; }
void rfc2231_decode_parameters (PARAMETER **headp) { PARAMETER *head = NULL; PARAMETER **last; PARAMETER *p, *q; struct rfc2231_parameter *conthead = NULL; struct rfc2231_parameter *conttmp; char *s, *t; char charset[STRING]; int encoded; int index; short dirty = 0; /* set to 1 when we may have created * empty parameters. */ if (!headp) return; purge_empty_parameters (headp); for (last = &head, p = *headp; p; p = q) { q = p->next; if (!(s = strchr (p->attribute, '*'))) { /* * Using RFC 2047 encoding in MIME parameters is explicitly * forbidden by that document. Nevertheless, it's being * generated by some software, including certain Lotus Notes to * Internet Gateways. So we actually decode it. */ if (option (OPTRFC2047PARAMS) && p->value && strstr (p->value, "=?")) rfc2047_decode (&p->value); else if (AssumedCharset && *AssumedCharset) convert_nonmime_string (&p->value); *last = p; last = &p->next; p->next = NULL; } else if (*(s + 1) == '\0') { *s = '\0'; s = rfc2231_get_charset (p->value, charset, sizeof (charset)); rfc2231_decode_one (p->value, s); mutt_convert_string (&p->value, charset, Charset, M_ICONV_HOOK_FROM); mutt_filter_unprintable (&p->value); *last = p; last = &p->next; p->next = NULL; dirty = 1; } else { *s = '\0'; s++; /* let s point to the first character of index. */ for (t = s; *t && isdigit ((unsigned char) *t); t++) ; encoded = (*t == '*'); *t = '\0'; index = atoi (s); conttmp = rfc2231_new_parameter (); conttmp->attribute = p->attribute; conttmp->value = p->value; conttmp->encoded = encoded; conttmp->index = index; p->attribute = NULL; p->value = NULL; FREE (&p); rfc2231_list_insert (&conthead, conttmp); } } if (conthead) { rfc2231_join_continuations (last, conthead); dirty = 1; } *headp = head; if (dirty) purge_empty_parameters (headp); }