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>
This commit is contained in:
Johannes Schindelin 2008-05-16 14:03:30 +01:00 committed by Junio C Hamano
parent 182fb4df91
commit cce8d6fdb4
5 changed files with 46 additions and 15 deletions

View File

@ -641,7 +641,7 @@ static void decode_transfer_encoding(char *line, unsigned linesize)
} }
} }
static int handle_filter(char *line, unsigned linesize); static int handle_filter(char *line, unsigned linesize, int linelen);
static int find_boundary(void) static int find_boundary(void)
{ {
@ -669,7 +669,7 @@ again:
"can't recover\n"); "can't recover\n");
exit(1); exit(1);
} }
handle_filter(newline, sizeof(newline)); handle_filter(newline, sizeof(newline), strlen(newline));
/* skip to the next boundary */ /* skip to the next boundary */
if (!find_boundary()) if (!find_boundary())
@ -759,14 +759,14 @@ static int handle_commit_msg(char *line, unsigned linesize)
return 0; return 0;
} }
static int handle_patch(char *line) static int handle_patch(char *line, int len)
{ {
fputs(line, patchfile); fwrite(line, 1, len, patchfile);
patch_lines++; patch_lines++;
return 0; return 0;
} }
static int handle_filter(char *line, unsigned linesize) static int handle_filter(char *line, unsigned linesize, int linelen)
{ {
static int filter = 0; static int filter = 0;
@ -779,7 +779,7 @@ static int handle_filter(char *line, unsigned linesize)
break; break;
filter++; filter++;
case 1: case 1:
if (!handle_patch(line)) if (!handle_patch(line, linelen))
break; break;
filter++; filter++;
default: default:
@ -794,6 +794,7 @@ static void handle_body(void)
int rc = 0; int rc = 0;
static char newline[2000]; static char newline[2000];
static char *np = newline; static char *np = newline;
int len = strlen(line);
/* Skip up to the first boundary */ /* Skip up to the first boundary */
if (content_top->boundary) { if (content_top->boundary) {
@ -807,7 +808,8 @@ static void handle_body(void)
/* flush any leftover */ /* flush any leftover */
if ((transfer_encoding == TE_BASE64) && if ((transfer_encoding == TE_BASE64) &&
(np != newline)) { (np != newline)) {
handle_filter(newline, sizeof(newline)); handle_filter(newline, sizeof(newline),
strlen(newline));
} }
if (!handle_boundary()) if (!handle_boundary())
return; return;
@ -824,7 +826,7 @@ static void handle_body(void)
/* binary data most likely doesn't have newlines */ /* binary data most likely doesn't have newlines */
if (message_type != TYPE_TEXT) { if (message_type != TYPE_TEXT) {
rc = handle_filter(line, sizeof(newline)); rc = handle_filter(line, sizeof(line), len);
break; break;
} }
@ -841,7 +843,7 @@ static void handle_body(void)
/* should be sitting on a new line */ /* should be sitting on a new line */
*(++np) = 0; *(++np) = 0;
op++; op++;
rc = handle_filter(newline, sizeof(newline)); rc = handle_filter(newline, sizeof(newline), np - newline);
np = newline; np = newline;
} }
} while (*op != 0); } while (*op != 0);
@ -851,12 +853,12 @@ static void handle_body(void)
break; break;
} }
default: default:
rc = handle_filter(line, sizeof(newline)); rc = handle_filter(line, sizeof(line), len);
} }
if (rc) if (rc)
/* nothing left to filter */ /* nothing left to filter */
break; break;
} while (fgets(line, sizeof(line), fin)); } while ((len = read_line_with_nul(line, sizeof(line), fin)));
return; return;
} }

View File

@ -45,6 +45,25 @@ static int is_from_line(const char *line, int len)
/* Could be as small as 64, enough to hold a Unix "From " line. */ /* Could be as small as 64, enough to hold a Unix "From " line. */
static char buf[4096]; static char buf[4096];
/* We cannot use fgets() because our lines can contain NULs */
int read_line_with_nul(char *buf, int size, FILE *in)
{
int len = 0, c;
for (;;) {
c = getc(in);
buf[len++] = c;
if (c == EOF || c == '\n' || len + 1 >= size)
break;
}
if (c == EOF)
len--;
buf[len] = '\0';
return len;
}
/* Called with the first line (potentially partial) /* Called with the first line (potentially partial)
* already in buf[] -- normally that should begin with * already in buf[] -- normally that should begin with
* the Unix "From " line. Write it into the specified * 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)
* "From " and having something that looks like a date format. * "From " and having something that looks like a date format.
*/ */
for (;;) { for (;;) {
int is_partial = (buf[len-1] != '\n'); int is_partial = len && buf[len-1] != '\n';
if (fputs(buf, output) == EOF) if (fwrite(buf, 1, len, output) != len)
die("cannot write output"); die("cannot write output");
if (fgets(buf, sizeof(buf), mbox) == NULL) { len = read_line_with_nul(buf, sizeof(buf), mbox);
if (len == 0) {
if (feof(mbox)) { if (feof(mbox)) {
status = 1; status = 1;
break; break;
} }
die("cannot read mbox"); die("cannot read mbox");
} }
len = strlen(buf);
if (!is_partial && !is_bare && is_from_line(buf, len)) if (!is_partial && !is_bare && is_from_line(buf, len))
break; /* done with one message */ break; /* done with one message */
} }

View File

@ -9,6 +9,7 @@ extern const char git_usage_string[];
extern void list_common_cmds_help(void); extern void list_common_cmds_help(void);
extern void help_unknown_cmd(const char *cmd); extern void help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int); extern void prune_packed_objects(int);
extern int read_line_with_nul(char *buf, int size, FILE *file);
extern int cmd_add(int argc, const char **argv, const char *prefix); extern int cmd_add(int argc, const char **argv, const char *prefix);
extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix);

View File

@ -25,4 +25,13 @@ do
diff ../t5100/info$mail info$mail" diff ../t5100/info$mail info$mail"
done done
test_expect_success 'respect NULs' '
git mailsplit -d3 -o. ../t5100/nul &&
cmp ../t5100/nul 001 &&
(cat 001 | git mailinfo msg patch) &&
test 4 = $(wc -l < patch)
'
test_done test_done

BIN
t/t5100/nul Normal file

Binary file not shown.