X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=mailinfo.c;h=d9b74f30de32194a34e234314a8d28063344f9a1;hp=890e3487adf65b937f9a9d1ec82d59c746bf62de;hb=HEAD;hpb=069b20a198f171512a1d2d2163b40f70c94f5257 diff --git a/mailinfo.c b/mailinfo.c index 890e3487..d9b74f30 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -7,12 +7,11 @@ #include #include #include +#ifndef NO_ICONV #include -#include "cache.h" - -#ifdef NO_STRCASESTR -extern char *gitstrcasestr(const char *haystack, const char *needle); #endif +#include "git-compat-util.h" +#include "cache.h" static FILE *cmitmsg, *patchfile; @@ -43,13 +42,46 @@ static char *sanity_check(char *name, char *email) return name; } -static int handle_from(char *line) +static int bogus_from(char *line) { - char *at = strchr(line, '@'); + /* John Doe */ + char *bra, *ket, *dst, *cp; + + /* This is fallback, so do not bother if we already have an + * e-mail address. + */ + if (*email) + return 0; + + bra = strchr(line, '<'); + if (!bra) + return 0; + ket = strchr(bra, '>'); + if (!ket) + return 0; + + for (dst = email, cp = bra+1; cp < ket; ) + *dst++ = *cp++; + *dst = 0; + for (cp = line; isspace(*cp); cp++) + ; + for (bra--; isspace(*bra); bra--) + *bra = 0; + cp = sanity_check(cp, email); + strcpy(name, cp); + return 1; +} + +static int handle_from(char *in_line) +{ + char line[1000]; + char *at; char *dst; + strcpy(line, in_line); + at = strchr(line, '@'); if (!at) - return 0; + return bogus_from(line); /* * If we already have one email, don't take any confusing lines @@ -208,38 +240,57 @@ static int eatspace(char *line) #define SEEN_FROM 01 #define SEEN_DATE 02 #define SEEN_SUBJECT 04 +#define SEEN_BOGUS_UNIX_FROM 010 +#define SEEN_PREFIX 020 -/* First lines of body can have From:, Date:, and Subject: */ -static int handle_inbody_header(int *seen, char *line) +/* First lines of body can have From:, Date:, and Subject: or empty */ +static void handle_inbody_header(int *seen, char *line) { + if (*seen & SEEN_PREFIX) + return; + if (isspace(*line)) { + char *cp; + for (cp = line + 1; *cp; cp++) { + if (!isspace(*cp)) + break; + } + if (!*cp) + return; + } + if (!memcmp(">From", line, 5) && isspace(line[5])) { + if (!(*seen & SEEN_BOGUS_UNIX_FROM)) { + *seen |= SEEN_BOGUS_UNIX_FROM; + return; + } + } if (!memcmp("From:", line, 5) && isspace(line[5])) { if (!(*seen & SEEN_FROM) && handle_from(line+6)) { *seen |= SEEN_FROM; - return 1; + return; } } if (!memcmp("Date:", line, 5) && isspace(line[5])) { if (!(*seen & SEEN_DATE)) { handle_date(line+6); *seen |= SEEN_DATE; - return 1; + return; } } if (!memcmp("Subject:", line, 8) && isspace(line[8])) { if (!(*seen & SEEN_SUBJECT)) { handle_subject(line+9); *seen |= SEEN_SUBJECT; - return 1; + return; } } if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) { if (!(*seen & SEEN_SUBJECT)) { handle_subject(line); *seen |= SEEN_SUBJECT; - return 1; + return; } } - return 0; + *seen |= SEEN_PREFIX; } static char *cleanup_subject(char *subject) @@ -274,6 +325,7 @@ static char *cleanup_subject(char *subject) } break; } + eatspace(subject); return subject; } } @@ -295,6 +347,7 @@ static void cleanup_space(char *buf) } } +static void decode_header_bq(char *it); typedef int (*header_fn_t)(char *); struct header_def { const char *name; @@ -302,7 +355,7 @@ struct header_def { int namelen; }; -static void check_header(char *line, int len, struct header_def *header) +static void check_header(char *line, struct header_def *header) { int i; @@ -314,13 +367,17 @@ static void check_header(char *line, int len, struct header_def *header) int len = header[i].namelen; if (!strncasecmp(line, header[i].name, len) && line[len] == ':' && isspace(line[len + 1])) { + /* Unwrap inline B and Q encoding, and optionally + * normalize the meta information to utf8. + */ + decode_header_bq(line + len + 2); header[i].func(line + len + 2); break; } } } -static void check_subheader_line(char *line, int len) +static void check_subheader_line(char *line) { static struct header_def header[] = { { "Content-Type", handle_subcontent_type }, @@ -328,9 +385,9 @@ static void check_subheader_line(char *line, int len) handle_content_transfer_encoding }, { NULL }, }; - check_header(line, len, header); + check_header(line, header); } -static void check_header_line(char *line, int len) +static void check_header_line(char *line) { static struct header_def header[] = { { "From", handle_from }, @@ -341,7 +398,30 @@ static void check_header_line(char *line, int len) handle_content_transfer_encoding }, { NULL }, }; - check_header(line, len, header); + check_header(line, header); +} + +static int is_rfc2822_header(char *line) +{ + /* + * The section that defines the loosest possible + * field name is "3.6.8 Optional fields". + * + * optional-field = field-name ":" unstructured CRLF + * field-name = 1*ftext + * ftext = %d33-57 / %59-126 + */ + int ch; + char *cp = line; + while ((ch = *cp++)) { + if (ch == ':') + return cp != line; + if ((33 <= ch && ch <= 57) || + (59 <= ch && ch <= 126)) + continue; + break; + } + return 0; } static int read_one_header_line(char *line, int sz, FILE *in) @@ -350,18 +430,23 @@ static int read_one_header_line(char *line, int sz, FILE *in) while (ofs < sz) { int peek, len; if (fgets(line + ofs, sz - ofs, in) == NULL) - return ofs; + break; len = eatspace(line + ofs); - if (len == 0) - return ofs; - peek = fgetc(in); ungetc(peek, in); - if (peek == ' ' || peek == '\t') { - /* Yuck, 2822 header "folding" */ - ofs += len; - continue; + if ((len == 0) || !is_rfc2822_header(line)) { + /* Re-add the newline */ + line[ofs + len] = '\n'; + line[ofs + len + 1] = '\0'; + break; } - return ofs + len; + ofs += len; + /* Yuck, 2822 header "folding" */ + peek = fgetc(in); ungetc(peek, in); + if (peek != ' ' && peek != '\t') + break; } + /* Count mbox From headers as headers */ + if (!ofs && !memcmp(line, "From ", 5)) + ofs = 1; return ofs; } @@ -376,7 +461,7 @@ static unsigned hexval(int c) return ~0; } -static int decode_q_segment(char *in, char *ot, char *ep) +static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047) { int c; while ((c = *in++) != 0 && (in <= ep)) { @@ -385,9 +470,11 @@ static int decode_q_segment(char *in, char *ot, char *ep) if (d == '\n' || !d) break; /* drop trailing newline */ *ot++ = ((hexval(d) << 4) | hexval(*in++)); + continue; } - else - *ot++ = c; + if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */ + c = 0x20; + *ot++ = c; } *ot = 0; return 0; @@ -442,10 +529,11 @@ static int decode_b_segment(char *in, char *ot, char *ep) static void convert_to_utf8(char *line, char *charset) { +#ifndef NO_ICONV char *in, *out; size_t insize, outsize, nrc; char outbuf[4096]; /* cheat */ - static char latin_one[] = "latin-1"; + static char latin_one[] = "latin1"; char *input_charset = *charset ? charset : latin_one; iconv_t conv = iconv_open(metainfo_charset, input_charset); @@ -474,6 +562,7 @@ static void convert_to_utf8(char *line, char *charset) return; *out = 0; strcpy(line, outbuf); +#endif } static void decode_header_bq(char *it) @@ -516,7 +605,7 @@ static void decode_header_bq(char *it) sz = decode_b_segment(cp + 3, piecebuf, ep); break; case 'q': - sz = decode_q_segment(cp + 3, piecebuf, ep); + sz = decode_q_segment(cp + 3, piecebuf, ep, 1); break; } if (sz < 0) @@ -538,7 +627,7 @@ static void decode_transfer_encoding(char *line) switch (transfer_encoding) { case TE_QP: ep = line + strlen(line); - decode_q_segment(line, line, ep); + decode_q_segment(line, line, ep, 0); break; case TE_BASE64: ep = line + strlen(line); @@ -552,25 +641,13 @@ static void decode_transfer_encoding(char *line) static void handle_info(void) { char *sub; - static int done_info = 0; - if (done_info) - return; - - done_info = 1; sub = cleanup_subject(subject); cleanup_space(name); cleanup_space(date); cleanup_space(email); cleanup_space(sub); - /* Unwrap inline B and Q encoding, and optionally - * normalize the meta information to utf8. - */ - decode_header_bq(name); - decode_header_bq(date); - decode_header_bq(email); - decode_header_bq(sub); printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date); } @@ -578,7 +655,7 @@ static void handle_info(void) /* We are inside message body and have read line[] already. * Spit out the commit log. */ -static int handle_commit_msg(void) +static int handle_commit_msg(int *seen) { if (!cmitmsg) return 0; @@ -602,6 +679,11 @@ static int handle_commit_msg(void) decode_transfer_encoding(line); if (metainfo_charset) convert_to_utf8(line, charset); + + handle_inbody_header(seen, line); + if (!(*seen & SEEN_PREFIX)) + continue; + fputs(line, cmitmsg); } while (fgets(line, sizeof(line), stdin) != NULL); fclose(cmitmsg); @@ -633,26 +715,16 @@ static void handle_patch(void) * that the first part to contain commit message and a patch, and * handle other parts as pure patches. */ -static int handle_multipart_one_part(void) +static int handle_multipart_one_part(int *seen) { - int seen = 0; int n = 0; - int len; while (fgets(line, sizeof(line), stdin) != NULL) { again: - len = eatspace(line); n++; - if (!len) - continue; if (is_multipart_boundary(line)) break; - if (0 <= seen && handle_inbody_header(&seen, line)) - continue; - seen = -1; /* no more inbody headers */ - line[len] = '\n'; - handle_info(); - if (handle_commit_msg()) + if (handle_commit_msg(seen)) goto again; handle_patch(); break; @@ -664,6 +736,7 @@ static int handle_multipart_one_part(void) static void handle_multipart_body(void) { + int seen = 0; int part_num = 0; /* Skip up to the first boundary */ @@ -676,13 +749,16 @@ static void handle_multipart_body(void) return; /* We are on boundary line. Start slurping the subhead. */ while (1) { - int len = read_one_header_line(line, sizeof(line), stdin); - if (!len) { - if (handle_multipart_one_part() < 0) + int hdr = read_one_header_line(line, sizeof(line), stdin); + if (!hdr) { + if (handle_multipart_one_part(&seen) < 0) return; + /* Reset per part headers */ + transfer_encoding = TE_DONTCARE; + charset[0] = 0; } else - check_subheader_line(line, len); + check_subheader_line(line); } fclose(patchfile); if (!patch_lines) { @@ -696,19 +772,8 @@ static void handle_body(void) { int seen = 0; - while (fgets(line, sizeof(line), stdin) != NULL) { - int len = eatspace(line); - if (!len) - continue; - if (0 <= seen && handle_inbody_header(&seen, line)) - continue; - seen = -1; /* no more inbody headers */ - line[len] = '\n'; - handle_info(); - handle_commit_msg(); - handle_patch(); - break; - } + handle_commit_msg(&seen); + handle_patch(); fclose(patchfile); if (!patch_lines) { fprintf(stderr, "No patch found\n"); @@ -751,15 +816,16 @@ int main(int argc, char **argv) exit(1); } while (1) { - int len = read_one_header_line(line, sizeof(line), stdin); - if (!len) { + int hdr = read_one_header_line(line, sizeof(line), stdin); + if (!hdr) { if (multipart_boundary[0]) handle_multipart_body(); else handle_body(); + handle_info(); break; } - check_header_line(line, len); + check_header_line(line); } return 0; }