From bfffa7dfa0246dff2b87170d7f456ddd365d71fb Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Thu, 7 Feb 2013 12:37:52 -0700 Subject: [PATCH 1/4] Fix #64166: quoted-printable-encode stream filter incorrectly discarding whitespace If trailing whitespace on a line is detected, mark the linebreak as a soft linebreak. --- ext/standard/filters.c | 12 +++++++++ ext/standard/tests/streams/bug64166.phpt | 32 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 ext/standard/tests/streams/bug64166.phpt diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 084860c76b433..21e165b6c279e 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -791,6 +791,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins unsigned int line_ccnt; unsigned int lb_ptr; unsigned int lb_cnt; + unsigned int prev_ws; int opts; static char qp_digits[] = "0123456789ABCDEF"; @@ -807,6 +808,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins icnt = *in_left_p; pd = (unsigned char *)(*out_pp); ocnt = *out_left_p; + prev_ws = 0; for (;;) { if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) { @@ -825,6 +827,14 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins break; } + /* If the character(s) immediately before the line break + * is whitespace, need to convert to soft linebreak to + * preserve that data. */ + if (prev_ws > 0) { + *(pd++) = '='; + ocnt--; + } + for (i = 0; i < lb_cnt; i++) { *(pd++) = inst->lbchars[i]; ocnt--; @@ -842,6 +852,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins } c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars); + prev_ws = 0; if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && (c == '\t' || c == ' ')) { if (line_ccnt < 2 && inst->lbchars != NULL) { @@ -866,6 +877,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins *(pd++) = c; ocnt--; line_ccnt--; + prev_ws = 1; CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); } } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { diff --git a/ext/standard/tests/streams/bug64166.phpt b/ext/standard/tests/streams/bug64166.phpt new file mode 100644 index 0000000000000..b9a7a052a4f9c --- /dev/null +++ b/ext/standard/tests/streams/bug64166.phpt @@ -0,0 +1,32 @@ +--TEST-- +Bug #64166: quoted-printable-encode stream filter incorrectly discarding whitespace +--FILE-- + "\n", + 'line-length' => 7 +)); +var_dump(stream_get_contents($fd, -1, 0)); + +stream_filter_remove($res); + +rewind($fd); +stream_filter_append($fd, 'convert.quoted-printable-encode', STREAM_FILTER_READ, array( + 'line-break-chars' => "\n", + 'line-length' => 6 +)); +var_dump(stream_get_contents($fd, -1, 0)); +?> +--EXPECT-- +string(14) "FIRST = +SECOND" +string(18) "FIRST= + = +SECON= +D" From f4d8330a907eb0d29cee08da426a1b768a47be7c Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Sun, 24 Feb 2013 18:34:35 -0700 Subject: [PATCH 2/4] Fix #64166: quoted-printable-encode stream filter incorrectly discarding whitespace Second attempt: need to use lookaheadto determine whether to encode ws --- ext/standard/filters.c | 58 +++++++++++++++++------- ext/standard/tests/streams/bug64166.phpt | 52 +++++++++++++-------- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 21e165b6c279e..99a39be9a19ce 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -791,7 +791,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins unsigned int line_ccnt; unsigned int lb_ptr; unsigned int lb_cnt; - unsigned int prev_ws; + unsigned int trail_ws; int opts; static char qp_digits[] = "0123456789ABCDEF"; @@ -808,7 +808,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins icnt = *in_left_p; pd = (unsigned char *)(*out_pp); ocnt = *out_left_p; - prev_ws = 0; + trail_ws = 0; for (;;) { if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && inst->lbchars != NULL && inst->lbchars_len > 0) { @@ -827,14 +827,6 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins break; } - /* If the character(s) immediately before the line break - * is whitespace, need to convert to soft linebreak to - * preserve that data. */ - if (prev_ws > 0) { - *(pd++) = '='; - ocnt--; - } - for (i = 0; i < lb_cnt; i++) { *(pd++) = inst->lbchars[i]; ocnt--; @@ -852,9 +844,10 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins } c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars); - prev_ws = 0; - if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && (c == '\t' || c == ' ')) { + if (!(opts & PHP_CONV_QPRINT_OPT_BINARY) && + (trail_ws == 0) && + (c == '\t' || c == ' ')) { if (line_ccnt < 2 && inst->lbchars != NULL) { if (ocnt < inst->lbchars_len + 1) { err = PHP_CONV_ERR_TOO_BIG; @@ -874,11 +867,41 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins err = PHP_CONV_ERR_TOO_BIG; break; } - *(pd++) = c; - ocnt--; - line_ccnt--; - prev_ws = 1; - CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); + + /* Check to see if this is EOL whitespace. */ + if (inst->lbchars != NULL) { + unsigned int j, lb_cnt2; + lb_cnt2 = 0; + unsigned char *ps2; + ps2 = ps; + trail_ws = 1; + + for (j = icnt - 1; j > 0; j--, ps2++) { + if (*ps2 == inst->lbchars[lb_cnt2]) { + lb_cnt2++; + if (lb_cnt2 >= inst->lbchars_len) { + /* Found trailing ws. Reset to top of main + * for loop to allow for code to do necessary + * wrapping/encoding. */ + break; + } + } else if (lb_cnt2 != 0 || (*ps2 != '\t' && *ps2 != ' ')) { + /* At least one non-EOL character following, so + * don't need to encode ws. */ + trail_ws = 0; + break; + } else { + trail_ws++; + } + } + } + + if (trail_ws == 0) { + *(pd++) = c; + ocnt--; + line_ccnt--; + CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); + } } } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { if (line_ccnt < 2 && inst->lbchars != NULL) { @@ -927,6 +950,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins *(pd++) = qp_digits[(c & 0x0f)]; ocnt -= 3; line_ccnt -= 3; + trail_ws--; CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); } } diff --git a/ext/standard/tests/streams/bug64166.phpt b/ext/standard/tests/streams/bug64166.phpt index b9a7a052a4f9c..e725a4b698c5f 100644 --- a/ext/standard/tests/streams/bug64166.phpt +++ b/ext/standard/tests/streams/bug64166.phpt @@ -2,31 +2,45 @@ Bug #64166: quoted-printable-encode stream filter incorrectly discarding whitespace --FILE-- "\n", - 'line-length' => 7 -)); -var_dump(stream_get_contents($fd, -1, 0)); + $res = stream_filter_append($fd, 'convert.quoted-printable-encode', STREAM_FILTER_READ, array( + 'line-break-chars' => "\n", + 'line-length' => 74 + )); + var_dump(stream_get_contents($fd, -1, 0)); -stream_filter_remove($res); + stream_filter_remove($res); + + rewind($fd); + stream_filter_append($fd, 'convert.quoted-printable-encode', STREAM_FILTER_READ, array( + 'line-break-chars' => "\n", + 'line-length' => 6 + )); + var_dump(stream_get_contents($fd, -1, 0)); + + fclose($fd); +} + +test_64166("FIRST \nSECOND"); +test_64166("FIRST \nSECOND"); -rewind($fd); -stream_filter_append($fd, 'convert.quoted-printable-encode', STREAM_FILTER_READ, array( - 'line-break-chars' => "\n", - 'line-length' => 6 -)); -var_dump(stream_get_contents($fd, -1, 0)); ?> --EXPECT-- -string(14) "FIRST = +string(15) "FIRST=20 +SECOND" +string(19) "FIRST= +=20 +SECON= +D" +string(18) "FIRST=20=20 SECOND" -string(18) "FIRST= - = +string(24) "FIRST= +=20= +=20 SECON= D" From caa1e65ecc121896ba30ed654b6dce2c18815258 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Mon, 18 Mar 2013 12:02:54 -0600 Subject: [PATCH 3/4] trailing ws --- ext/standard/filters.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 99a39be9a19ce..aa4be5ca9bb05 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -771,7 +771,7 @@ static void php_conv_qprint_encode_dtor(php_conv_qprint_encode *inst) } #define NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, lbchars) \ - ((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps)) + ((lb_ptr) < (lb_cnt) ? (lbchars)[(lb_ptr)] : *(ps)) #define CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt) \ if ((lb_ptr) < (lb_cnt)) { \ @@ -841,7 +841,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins if (lb_ptr >= lb_cnt && icnt <= 0) { break; - } + } c = NEXT_CHAR(ps, icnt, lb_ptr, lb_cnt, inst->lbchars); @@ -903,7 +903,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins CONSUME_CHAR(ps, icnt, lb_ptr, lb_cnt); } } - } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { + } else if ((!(opts & PHP_CONV_QPRINT_OPT_FORCE_ENCODE_FIRST) || line_ccnt < inst->line_len) && ((c >= 33 && c <= 60) || (c >= 62 && c <= 126))) { if (line_ccnt < 2 && inst->lbchars != NULL) { if (ocnt < inst->lbchars_len + 1) { err = PHP_CONV_ERR_TOO_BIG; @@ -947,7 +947,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins } *(pd++) = '='; *(pd++) = qp_digits[(c >> 4)]; - *(pd++) = qp_digits[(c & 0x0f)]; + *(pd++) = qp_digits[(c & 0x0f)]; ocnt -= 3; line_ccnt -= 3; trail_ws--; @@ -958,7 +958,7 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins *in_pp = (const char *)ps; *in_left_p = icnt; *out_pp = (char *)pd; - *out_left_p = ocnt; + *out_left_p = ocnt; inst->line_ccnt = line_ccnt; inst->lb_ptr = lb_ptr; inst->lb_cnt = lb_cnt; From 6d2e47cc90f44c52f9da1957512f790f6a6762ca Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Mon, 18 Mar 2013 12:03:11 -0600 Subject: [PATCH 4/4] Move unsigned char declaration to top of block --- ext/standard/filters.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/standard/filters.c b/ext/standard/filters.c index aa4be5ca9bb05..0a59039635881 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -870,9 +870,10 @@ static php_conv_err_t php_conv_qprint_encode_convert(php_conv_qprint_encode *ins /* Check to see if this is EOL whitespace. */ if (inst->lbchars != NULL) { + unsigned char *ps2; unsigned int j, lb_cnt2; + lb_cnt2 = 0; - unsigned char *ps2; ps2 = ps; trail_ws = 1;