Merge branch 'eb/mailinfo' into next
[gitweb.git] / mailinfo.c
index bee7b202cfc5d8c1a2b7e642cf62539973ea514d..5b6c2157ede415e019099098f2d0dc522b1e7a27 100644 (file)
@@ -72,11 +72,14 @@ static int bogus_from(char *line)
        return 1;
 }
 
-static int handle_from(char *line)
+static int handle_from(char *in_line)
 {
-       char *at = strchr(line, '@');
+       char line[1000];
+       char *at;
        char *dst;
 
+       strcpy(line, in_line);
+       at = strchr(line, '@');
        if (!at)
                return bogus_from(line);
 
@@ -237,38 +240,46 @@ 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)
+static void handle_inbody_header(int *seen, char *line)
 {
+       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)
@@ -378,24 +389,54 @@ static void check_header_line(char *line)
        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)
 {
        int ofs = 0;
        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;
+                       break;
+               if (!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;
 }
 
@@ -590,12 +631,7 @@ 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);
@@ -609,7 +645,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;
@@ -633,6 +669,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);
@@ -664,26 +705,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;
@@ -695,6 +726,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 */
@@ -709,7 +741,7 @@ static void handle_multipart_body(void)
        while (1) {
                int hdr = read_one_header_line(line, sizeof(line), stdin);
                if (!hdr) {
-                       if (handle_multipart_one_part() < 0)
+                       if (handle_multipart_one_part(&seen) < 0)
                                return;
                        /* Reset per part headers */
                        transfer_encoding = TE_DONTCARE;
@@ -730,18 +762,9 @@ 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();
+       if (line[0] || fgets(line, sizeof(line), stdin) != NULL) {
+               handle_commit_msg(&seen);
                handle_patch();
-               break;
        }
        fclose(patchfile);
        if (!patch_lines) {
@@ -791,6 +814,7 @@ int main(int argc, char **argv)
                                handle_multipart_body();
                        else
                                handle_body();
+                       handle_info();
                        break;
                }
                check_header_line(line);