@matheusbsbs / matheus-bagliano / commits / cce8d6fdb4
Fork 0

mailsplit and mailinfo: gracefully handle NUL characters

The function fgets() has a big problem with NUL characters: it reads them, but nobody will know if the NUL comes from the file stream, or was appended at the end of the line. So implement a custom read_line_with_nul() function. Noticed by Tommy Thorn. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed May 16, 2008 at 14:03 UTC cce8d6fdb4d7170a73763586daf6ac4f6b8fce2c
5 files changed +46 -15
builtin-mailinfo.c
+13 -11
@@ -641,7 +641,7 @@ static void decode_transfer_encoding(char *line, unsigned linesize)
641 }
642 }
643
644 -static int handle_filter(char *line, unsigned linesize);
644 +static int handle_filter(char *line, unsigned linesize, int linelen);
645
646 static int find_boundary(void)
647 {
@@ -669,7 +669,7 @@ again:
669 "can't recover\n");
670 exit(1);
671 }
672 - handle_filter(newline, sizeof(newline));
672 + handle_filter(newline, sizeof(newline), strlen(newline));
673
674 /* skip to the next boundary */
675 if (!find_boundary())
@@ -759,14 +759,14 @@ static int handle_commit_msg(char *line, unsigned linesize)
759 return 0;
760 }
761
762 -static int handle_patch(char *line)
762 +static int handle_patch(char *line, int len)
763 {
764 - fputs(line, patchfile);
764 + fwrite(line, 1, len, patchfile);
765 patch_lines++;
766 return 0;
767 }
768
769 -static int handle_filter(char *line, unsigned linesize)
769 +static int handle_filter(char *line, unsigned linesize, int linelen)
770 {
771 static int filter = 0;
772
@@ -779,7 +779,7 @@ static int handle_filter(char *line, unsigned linesize)
779 break;
780 filter++;
781 case 1:
782 - if (!handle_patch(line))
782 + if (!handle_patch(line, linelen))
783 break;
784 filter++;
785 default:
@@ -794,6 +794,7 @@ static void handle_body(void)
794 int rc = 0;
795 static char newline[2000];
796 static char *np = newline;
797 + int len = strlen(line);
798
799 /* Skip up to the first boundary */
800 if (content_top->boundary) {
@@ -807,7 +808,8 @@ static void handle_body(void)
808 /* flush any leftover */
809 if ((transfer_encoding == TE_BASE64) &&
810 (np != newline)) {
810 - handle_filter(newline, sizeof(newline));
811 + handle_filter(newline, sizeof(newline),
812 + strlen(newline));
813 }
814 if (!handle_boundary())
815 return;
@@ -824,7 +826,7 @@ static void handle_body(void)
826
827 /* binary data most likely doesn't have newlines */
828 if (message_type != TYPE_TEXT) {
827 - rc = handle_filter(line, sizeof(newline));
829 + rc = handle_filter(line, sizeof(line), len);
830 break;
831 }
832
@@ -841,7 +843,7 @@ static void handle_body(void)
843 /* should be sitting on a new line */
844 *(++np) = 0;
845 op++;
844 - rc = handle_filter(newline, sizeof(newline));
846 + rc = handle_filter(newline, sizeof(newline), np - newline);
847 np = newline;
848 }
849 } while (*op != 0);
@@ -851,12 +853,12 @@ static void handle_body(void)
853 break;
854 }
855 default:
854 - rc = handle_filter(line, sizeof(newline));
856 + rc = handle_filter(line, sizeof(line), len);
857 }
858 if (rc)
859 /* nothing left to filter */
860 break;
859 - } while (fgets(line, sizeof(line), fin));
861 + } while ((len = read_line_with_nul(line, sizeof(line), fin)));
862
863 return;
864 }
builtin-mailsplit.c
+23 -4
@@ -45,6 +45,25 @@ static int is_from_line(const char *line, int len)
45 /* Could be as small as 64, enough to hold a Unix "From " line. */
46 static char buf[4096];
47
48 +/* We cannot use fgets() because our lines can contain NULs */
49 +int read_line_with_nul(char *buf, int size, FILE *in)
50 +{
51 + int len = 0, c;
52 +
53 + for (;;) {
54 + c = getc(in);
55 + buf[len++] = c;
56 + if (c == EOF || c == '\n' || len + 1 >= size)
57 + break;
58 + }
59 +
60 + if (c == EOF)
61 + len--;
62 + buf[len] = '\0';
63 +
64 + return len;
65 +}
66 +
67 /* Called with the first line (potentially partial)
68 * already in buf[] -- normally that should begin with
69 * the Unix "From " line. Write it into the specified
@@ -70,19 +89,19 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
89 * "From " and having something that looks like a date format.
90 */
91 for (;;) {
73 - int is_partial = (buf[len-1] != '\n');
92 + int is_partial = len && buf[len-1] != '\n';
93
75 - if (fputs(buf, output) == EOF)
94 + if (fwrite(buf, 1, len, output) != len)
95 die("cannot write output");
96
78 - if (fgets(buf, sizeof(buf), mbox) == NULL) {
97 + len = read_line_with_nul(buf, sizeof(buf), mbox);
98 + if (len == 0) {
99 if (feof(mbox)) {
100 status = 1;
101 break;
102 }
103 die("cannot read mbox");
104 }
85 - len = strlen(buf);
105 if (!is_partial && !is_bare && is_from_line(buf, len))
106 break; /* done with one message */
107 }
builtin.h
+1
@@ -9,6 +9,7 @@ extern const char git_usage_string[];
9 extern void list_common_cmds_help(void);
10 extern void help_unknown_cmd(const char *cmd);
11 extern void prune_packed_objects(int);
12 +extern int read_line_with_nul(char *buf, int size, FILE *file);
13
14 extern int cmd_add(int argc, const char **argv, const char *prefix);
15 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
t/t5100-mailinfo.sh
+9
@@ -25,4 +25,13 @@ do
25 diff ../t5100/info$mail info$mail"
26 done
27
28 +test_expect_success 'respect NULs' '
29 +
30 + git mailsplit -d3 -o. ../t5100/nul &&
31 + cmp ../t5100/nul 001 &&
32 + (cat 001 | git mailinfo msg patch) &&
33 + test 4 = $(wc -l < patch)
34 +
35 +'
36 +
37 test_done
t/t5100/nul
Binary files /dev/null and b/t/t5100/nul differ