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