mailinfo: do not let handle_boundary() touch global "line" directly
[gitweb.git] / builtin / mailinfo.c
index 1566c19b6277b464607ddeb6ba0369383a0325ae..9b3f349a11be03223d93d4e7fa0e013ba4155912 100644 (file)
@@ -28,11 +28,19 @@ static int use_scissors;
 static int add_message_id;
 static int use_inbody_headers = 1;
 
-#define MAX_HDR_PARSED 10
 #define MAX_BOUNDARIES 5
 
-static void cleanup_space(struct strbuf *sb);
-
+static void cleanup_space(struct strbuf *sb)
+{
+       size_t pos, cnt;
+       for (pos = 0; pos < sb->len; pos++) {
+               if (isspace(sb->buf[pos])) {
+                       sb->buf[pos] = ' ';
+                       for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
+                       strbuf_remove(sb, pos + 1, cnt);
+               }
+       }
+}
 
 static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
 {
@@ -272,19 +280,7 @@ static void cleanup_subject(struct strbuf *subject)
        strbuf_trim(subject);
 }
 
-static void cleanup_space(struct strbuf *sb)
-{
-       size_t pos, cnt;
-       for (pos = 0; pos < sb->len; pos++) {
-               if (isspace(sb->buf[pos])) {
-                       sb->buf[pos] = ' ';
-                       for (cnt = 0; isspace(sb->buf[pos + cnt + 1]); cnt++);
-                       strbuf_remove(sb, pos + 1, cnt);
-               }
-       }
-}
-
-static void decode_header(struct strbuf *line);
+#define MAX_HDR_PARSED 10
 static const char *header[MAX_HDR_PARSED] = {
        "From","Subject","Date",
 };
@@ -312,139 +308,6 @@ static int is_format_patch_separator(const char *line, int len)
        return !memcmp(SAMPLE + (cp - line), cp, strlen(SAMPLE) - (cp - line));
 }
 
-static int check_header(const struct strbuf *line,
-                               struct strbuf *hdr_data[], int overwrite)
-{
-       int i, ret = 0, len;
-       struct strbuf sb = STRBUF_INIT;
-       /* search for the interesting parts */
-       for (i = 0; header[i]; i++) {
-               int len = strlen(header[i]);
-               if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
-                       /* Unwrap inline B and Q encoding, and optionally
-                        * normalize the meta information to utf8.
-                        */
-                       strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
-                       decode_header(&sb);
-                       handle_header(&hdr_data[i], &sb);
-                       ret = 1;
-                       goto check_header_out;
-               }
-       }
-
-       /* Content stuff */
-       if (cmp_header(line, "Content-Type")) {
-               len = strlen("Content-Type: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               strbuf_insert(&sb, 0, "Content-Type: ", len);
-               handle_content_type(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-       if (cmp_header(line, "Content-Transfer-Encoding")) {
-               len = strlen("Content-Transfer-Encoding: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               handle_content_transfer_encoding(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-       if (cmp_header(line, "Message-Id")) {
-               len = strlen("Message-Id: ");
-               strbuf_add(&sb, line->buf + len, line->len - len);
-               decode_header(&sb);
-               handle_message_id(&sb);
-               ret = 1;
-               goto check_header_out;
-       }
-
-       /* for inbody stuff */
-       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
-               ret = is_format_patch_separator(line->buf + 1, line->len - 1);
-               goto check_header_out;
-       }
-       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
-               for (i = 0; header[i]; i++) {
-                       if (!strcmp("Subject", header[i])) {
-                               handle_header(&hdr_data[i], line);
-                               ret = 1;
-                               goto check_header_out;
-                       }
-               }
-       }
-
-check_header_out:
-       strbuf_release(&sb);
-       return ret;
-}
-
-static int is_rfc2822_header(const struct strbuf *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->buf;
-
-       /* Count mbox From headers as headers */
-       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
-               return 1;
-
-       while ((ch = *cp++)) {
-               if (ch == ':')
-                       return 1;
-               if ((33 <= ch && ch <= 57) ||
-                   (59 <= ch && ch <= 126))
-                       continue;
-               break;
-       }
-       return 0;
-}
-
-static int read_one_header_line(struct strbuf *line, FILE *in)
-{
-       /* Get the first part of the line. */
-       if (strbuf_getline(line, in, '\n'))
-               return 0;
-
-       /*
-        * Is it an empty line or not a valid rfc2822 header?
-        * If so, stop here, and return false ("not a header")
-        */
-       strbuf_rtrim(line);
-       if (!line->len || !is_rfc2822_header(line)) {
-               /* Re-add the newline */
-               strbuf_addch(line, '\n');
-               return 0;
-       }
-
-       /*
-        * Now we need to eat all the continuation lines..
-        * Yuck, 2822 header "folding"
-        */
-       for (;;) {
-               int peek;
-               struct strbuf continuation = STRBUF_INIT;
-
-               peek = fgetc(in); ungetc(peek, in);
-               if (peek != ' ' && peek != '\t')
-                       break;
-               if (strbuf_getline(&continuation, in, '\n'))
-                       break;
-               continuation.buf[0] = ' ';
-               strbuf_rtrim(&continuation);
-               strbuf_addbuf(line, &continuation);
-       }
-
-       return 1;
-}
-
 static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 {
        const char *in = q_seg->buf;
@@ -605,6 +468,73 @@ static void decode_header(struct strbuf *it)
        strbuf_release(&piecebuf);
 }
 
+static int check_header(const struct strbuf *line,
+                               struct strbuf *hdr_data[], int overwrite)
+{
+       int i, ret = 0, len;
+       struct strbuf sb = STRBUF_INIT;
+       /* search for the interesting parts */
+       for (i = 0; header[i]; i++) {
+               int len = strlen(header[i]);
+               if ((!hdr_data[i] || overwrite) && cmp_header(line, header[i])) {
+                       /* Unwrap inline B and Q encoding, and optionally
+                        * normalize the meta information to utf8.
+                        */
+                       strbuf_add(&sb, line->buf + len + 2, line->len - len - 2);
+                       decode_header(&sb);
+                       handle_header(&hdr_data[i], &sb);
+                       ret = 1;
+                       goto check_header_out;
+               }
+       }
+
+       /* Content stuff */
+       if (cmp_header(line, "Content-Type")) {
+               len = strlen("Content-Type: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(&sb);
+               strbuf_insert(&sb, 0, "Content-Type: ", len);
+               handle_content_type(&sb);
+               ret = 1;
+               goto check_header_out;
+       }
+       if (cmp_header(line, "Content-Transfer-Encoding")) {
+               len = strlen("Content-Transfer-Encoding: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(&sb);
+               handle_content_transfer_encoding(&sb);
+               ret = 1;
+               goto check_header_out;
+       }
+       if (cmp_header(line, "Message-Id")) {
+               len = strlen("Message-Id: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(&sb);
+               handle_message_id(&sb);
+               ret = 1;
+               goto check_header_out;
+       }
+
+       /* for inbody stuff */
+       if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
+               ret = is_format_patch_separator(line->buf + 1, line->len - 1);
+               goto check_header_out;
+       }
+       if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
+               for (i = 0; header[i]; i++) {
+                       if (!strcmp("Subject", header[i])) {
+                               handle_header(&hdr_data[i], line);
+                               ret = 1;
+                               goto check_header_out;
+                       }
+               }
+       }
+
+check_header_out:
+       strbuf_release(&sb);
+       return ret;
+}
+
 static void decode_transfer_encoding(struct strbuf *line)
 {
        struct strbuf *ret;
@@ -626,64 +556,6 @@ static void decode_transfer_encoding(struct strbuf *line)
        free(ret);
 }
 
-static void handle_filter(struct strbuf *line);
-
-static int find_boundary(void)
-{
-       while (!strbuf_getline(&line, fin, '\n')) {
-               if (*content_top && is_multipart_boundary(&line))
-                       return 1;
-       }
-       return 0;
-}
-
-static int handle_boundary(void)
-{
-       struct strbuf newline = STRBUF_INIT;
-
-       strbuf_addch(&newline, '\n');
-again:
-       if (line.len >= (*content_top)->len + 2 &&
-           !memcmp(line.buf + (*content_top)->len, "--", 2)) {
-               /* we hit an end boundary */
-               /* pop the current boundary off the stack */
-               strbuf_release(*content_top);
-               free(*content_top);
-               *content_top = NULL;
-
-               /* technically won't happen as is_multipart_boundary()
-                  will fail first.  But just in case..
-                */
-               if (--content_top < content) {
-                       fprintf(stderr, "Detected mismatched boundaries, "
-                                       "can't recover\n");
-                       exit(1);
-               }
-               handle_filter(&newline);
-               strbuf_release(&newline);
-
-               /* skip to the next boundary */
-               if (!find_boundary())
-                       return 0;
-               goto again;
-       }
-
-       /* set some defaults */
-       transfer_encoding = TE_DONTCARE;
-       strbuf_reset(&charset);
-
-       /* slurp in this section's info */
-       while (read_one_header_line(&line, fin))
-               check_header(&line, p_hdr_data, 0);
-
-       strbuf_release(&newline);
-       /* replenish line */
-       if (strbuf_getline(&line, fin, '\n'))
-               return 0;
-       strbuf_addch(&line, '\n');
-       return 1;
-}
-
 static inline int patchbreak(const struct strbuf *line)
 {
        size_t i;
@@ -771,27 +643,25 @@ static int is_scissors_line(const struct strbuf *line)
                gap * 2 < perforation);
 }
 
-static int handle_commit_msg(struct strbuf *line)
+static int handle_commit_msg(struct strbuf *line, int *still_looking)
 {
-       static int still_looking = 1;
-
        if (!cmitmsg)
                return 0;
 
-       if (still_looking) {
+       if (*still_looking) {
                if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
                        return 0;
        }
 
-       if (use_inbody_headers && still_looking) {
-               still_looking = check_header(line, s_hdr_data, 0);
-               if (still_looking)
+       if (use_inbody_headers && *still_looking) {
+               *still_looking = check_header(line, s_hdr_data, 0);
+               if (*still_looking)
                        return 0;
        } else
                /* Only trim the first (blank) line of the commit message
                 * when ignoring in-body headers.
                 */
-               still_looking = 0;
+               *still_looking = 0;
 
        /* normalize the log message to UTF-8. */
        if (metainfo_charset)
@@ -803,7 +673,7 @@ static int handle_commit_msg(struct strbuf *line)
                        die_errno("Could not rewind output message file");
                if (ftruncate(fileno(cmitmsg), 0))
                        die_errno("Could not truncate output message file at scissors");
-               still_looking = 1;
+               *still_looking = 1;
 
                /*
                 * We may have already read "secondary headers"; purge
@@ -835,25 +705,148 @@ static void handle_patch(const struct strbuf *line)
        patch_lines++;
 }
 
-static void handle_filter(struct strbuf *line)
+static void handle_filter(struct strbuf *line, int *filter_stage, int *header_stage)
 {
-       static int filter = 0;
-
-       /* filter tells us which part we left off on */
-       switch (filter) {
+       switch (*filter_stage) {
        case 0:
-               if (!handle_commit_msg(line))
+               if (!handle_commit_msg(line, header_stage))
                        break;
-               filter++;
+               (*filter_stage)++;
        case 1:
                handle_patch(line);
                break;
        }
 }
 
-static void handle_body(void)
+static int is_rfc2822_header(const struct strbuf *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->buf;
+
+       /* Count mbox From headers as headers */
+       if (starts_with(cp, "From ") || starts_with(cp, ">From "))
+               return 1;
+
+       while ((ch = *cp++)) {
+               if (ch == ':')
+                       return 1;
+               if ((33 <= ch && ch <= 57) ||
+                   (59 <= ch && ch <= 126))
+                       continue;
+               break;
+       }
+       return 0;
+}
+
+static int read_one_header_line(struct strbuf *line, FILE *in)
+{
+       struct strbuf continuation = STRBUF_INIT;
+
+       /* Get the first part of the line. */
+       if (strbuf_getline(line, in, '\n'))
+               return 0;
+
+       /*
+        * Is it an empty line or not a valid rfc2822 header?
+        * If so, stop here, and return false ("not a header")
+        */
+       strbuf_rtrim(line);
+       if (!line->len || !is_rfc2822_header(line)) {
+               /* Re-add the newline */
+               strbuf_addch(line, '\n');
+               return 0;
+       }
+
+       /*
+        * Now we need to eat all the continuation lines..
+        * Yuck, 2822 header "folding"
+        */
+       for (;;) {
+               int peek;
+
+               peek = fgetc(in); ungetc(peek, in);
+               if (peek != ' ' && peek != '\t')
+                       break;
+               if (strbuf_getline(&continuation, in, '\n'))
+                       break;
+               continuation.buf[0] = ' ';
+               strbuf_rtrim(&continuation);
+               strbuf_addbuf(line, &continuation);
+       }
+       strbuf_release(&continuation);
+
+       return 1;
+}
+
+static int find_boundary(void)
+{
+       while (!strbuf_getline(&line, fin, '\n')) {
+               if (*content_top && is_multipart_boundary(&line))
+                       return 1;
+       }
+       return 0;
+}
+
+static int handle_boundary(struct strbuf *line, int *filter_stage, int *header_stage)
+{
+       struct strbuf newline = STRBUF_INIT;
+
+       strbuf_addch(&newline, '\n');
+again:
+       if (line->len >= (*content_top)->len + 2 &&
+           !memcmp(line->buf + (*content_top)->len, "--", 2)) {
+               /* we hit an end boundary */
+               /* pop the current boundary off the stack */
+               strbuf_release(*content_top);
+               free(*content_top);
+               *content_top = NULL;
+
+               /* technically won't happen as is_multipart_boundary()
+                  will fail first.  But just in case..
+                */
+               if (--content_top < content) {
+                       fprintf(stderr, "Detected mismatched boundaries, "
+                                       "can't recover\n");
+                       exit(1);
+               }
+               handle_filter(&newline, filter_stage, header_stage);
+               strbuf_release(&newline);
+
+               /* skip to the next boundary */
+               if (!find_boundary())
+                       return 0;
+               goto again;
+       }
+
+       /* set some defaults */
+       transfer_encoding = TE_DONTCARE;
+       strbuf_reset(&charset);
+
+       /* slurp in this section's info */
+       while (read_one_header_line(line, fin))
+               check_header(line, p_hdr_data, 0);
+
+       strbuf_release(&newline);
+       /* replenish line */
+       if (strbuf_getline(line, fin, '\n'))
+               return 0;
+       strbuf_addch(line, '\n');
+       return 1;
+}
+
+static void handle_body(struct strbuf *line)
 {
        struct strbuf prev = STRBUF_INIT;
+       int filter_stage = 0;
+       int header_stage = 1;
 
        /* Skip up to the first boundary */
        if (*content_top) {
@@ -863,18 +856,18 @@ static void handle_body(void)
 
        do {
                /* process any boundary lines */
-               if (*content_top && is_multipart_boundary(&line)) {
+               if (*content_top && is_multipart_boundary(line)) {
                        /* flush any leftover */
                        if (prev.len) {
-                               handle_filter(&prev);
+                               handle_filter(&prev, &filter_stage, &header_stage);
                                strbuf_reset(&prev);
                        }
-                       if (!handle_boundary())
+                       if (!handle_boundary(line, &filter_stage, &header_stage))
                                goto handle_body_out;
                }
 
                /* Unwrap transfer encoding */
-               decode_transfer_encoding(&line);
+               decode_transfer_encoding(line);
 
                switch (transfer_encoding) {
                case TE_BASE64:
@@ -883,7 +876,7 @@ static void handle_body(void)
                        struct strbuf **lines, **it, *sb;
 
                        /* Prepend any previous partial lines */
-                       strbuf_insert(&line, 0, prev.buf, prev.len);
+                       strbuf_insert(line, 0, prev.buf, prev.len);
                        strbuf_reset(&prev);
 
                        /*
@@ -891,7 +884,7 @@ static void handle_body(void)
                         * multiple new lines.  Pass only one chunk
                         * at a time to handle_filter()
                         */
-                       lines = strbuf_split(&line, '\n');
+                       lines = strbuf_split(line, '\n');
                        for (it = lines; (sb = *it); it++) {
                                if (*(it + 1) == NULL) /* The last line */
                                        if (sb->buf[sb->len - 1] != '\n') {
@@ -899,7 +892,7 @@ static void handle_body(void)
                                                strbuf_addbuf(&prev, sb);
                                                break;
                                        }
-                               handle_filter(sb);
+                               handle_filter(sb, &filter_stage, &header_stage);
                        }
                        /*
                         * The partial chunk is saved in "prev" and will be
@@ -909,10 +902,10 @@ static void handle_body(void)
                        break;
                }
                default:
-                       handle_filter(&line);
+                       handle_filter(line, &filter_stage, &header_stage);
                }
 
-       } while (!strbuf_getwholeline(&line, fin, '\n'));
+       } while (!strbuf_getwholeline(line, fin, '\n'));
 
 handle_body_out:
        strbuf_release(&prev);
@@ -998,7 +991,9 @@ static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
        while (read_one_header_line(&line, fin))
                check_header(&line, p_hdr_data, 1);
 
-       handle_body();
+       handle_body(&line);
+       fclose(patchfile);
+
        handle_info();
 
        return 0;