]> granicus.if.org Git - php/commitdiff
- Fix multibyte handling errors in iconv_mime_encode() when quoted-printable
authorMoriyoshi Koizumi <moriyoshi@php.net>
Mon, 22 Dec 2003 01:30:00 +0000 (01:30 +0000)
committerMoriyoshi Koizumi <moriyoshi@php.net>
Mon, 22 Dec 2003 01:30:00 +0000 (01:30 +0000)
  encoding scheme is used.
- Fix segfault that occurs in iconv_mime_encode() when input_charset or
  output_charset parameter is not specified in the associative array.

ext/iconv/iconv.c

index c568efe6b4caf3925e84fe53877d15e5bf6ff588..109db29b023b739dc65e913bdc195019832e10ca 100644 (file)
@@ -911,6 +911,24 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
        size_t in_left;
        char *out_p;
        size_t out_left;
+       static int qp_table[256] = {
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x00 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
+               3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x20 */
+               1, 1, 1, 1, 1, 1, 1 ,1, 1, 1, 1, 1, 1, 3, 1, 3, /* 0x30 */
+               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 */
+               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x50 */
+               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 */
+               1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, /* 0x70 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x80 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x90 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xA0 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xB0 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xC0 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xD0 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xE0 */
+               3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3  /* 0xF0 */
+       };
 
        out_charset_len = strlen(out_charset);
        lfchars_len = strlen(lfchars);
@@ -1086,16 +1104,26 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
                        } break; /* case PHP_ICONV_ENC_SCHEME_BASE64: */
 
                        case PHP_ICONV_ENC_SCHEME_QPRINT: {
+                               size_t ini_in_left;
+                               const char *ini_in_p;
+                               const unsigned char *p;
+                               size_t nbytes_required;
+
                                smart_str_appendc(pretval, 'Q');
                                char_cnt--;
                                smart_str_appendc(pretval, '?');
                                char_cnt--;
 
-                               prev_in_left = in_left;
+                               prev_in_left = ini_in_left = in_left;
+                               ini_in_p = in_p;
+
+                               for (out_size = char_cnt; out_size > 0;) {
+                                       size_t prev_out_left;
+
+                                       nbytes_required = 0;
 
-                               while (in_left > 0) {
                                        out_p = buf;
-                                       out_left = out_size = 1;
+                                       out_left = out_size;
 
                                        if (iconv(cd, (char **)&in_p, &in_left, (char **) &out_p, &out_left) == (size_t)-1) {
 #if ICONV_SUPPORTS_ERRNO
@@ -1109,6 +1137,10 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
                                                                goto out;
 
                                                        case E2BIG:
+                                                               if (prev_in_left == in_left) {
+                                                                       err = PHP_ICONV_ERR_UNKNOWN;
+                                                                       goto out;
+                                                               }
                                                                break;
        
                                                        default:
@@ -1123,36 +1155,47 @@ static php_iconv_err_t _php_iconv_mime_encode(smart_str *pretval, const char *fn
 #endif
                                        }
 
-                                       if (out_size > out_left) {
-                                               if ((buf[0] >= 33 && buf[0] <= 60) ||
-                                                       (buf[0] >= 62 && buf[0] <= 126)) {
+                                       prev_out_left = out_left;
+                                       if (iconv(cd, NULL, NULL, (char **) &out_p, &out_left) == (size_t)-1) {
+#if ICONV_SUPPORTS_ERRNO
+                                               if (errno != E2BIG) {
+                                                       err = PHP_ICONV_ERR_UNKNOWN;
+                                                       goto out;
+                                               }
+#else
+                                               if (out_left == prev_out_left) {
+                                                       err = PHP_ICONV_ERR_UNKNOWN;
+                                                       goto out;
+                                               }
+#endif
+                                       }
+
+                                       for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
+                                               nbytes_required += qp_table[*p];
+                                       }
 
-                                                       if (char_cnt >= 1 + 2) {
-                                                               smart_str_appendc(pretval, buf[0]);
-                                                               char_cnt--;
-                                                       } else {
-                                                               in_p -= (prev_in_left - in_left);
-                                                               in_left = prev_in_left;
+                                       if (nbytes_required <= char_cnt - 2) {
+                                               break;
+                                       }
 
-                                                               break;
-                                                       }
-                                               } else {
-                                                       if (char_cnt >= 3 + 2) { 
-                                                               static char qp_digits[] = "0123456789ABCDEF";
-                                                               smart_str_appendc(pretval, '=');
-                                                               smart_str_appendc(pretval, qp_digits[(buf[0] >> 4) & 0x0f]);
-                                                               smart_str_appendc(pretval, qp_digits[(buf[0] & 0x0f)]);
-                                                               char_cnt -= 3;
-                                                       } else {
-                                                               in_p -= (prev_in_left - in_left);
-                                                               in_left = prev_in_left;
+                                       out_size -= ((nbytes_required - (char_cnt - 2)) + 1) / (3 - 1);
+                                       in_left = ini_in_left;
+                                       in_p = ini_in_p;
+                               }
 
-                                                               break;
-                                                       }
-                                               }
+                               for (p = (unsigned char *)buf; p < (unsigned char *)out_p; p++) {
+                                       if (qp_table[*p] == 1) {
+                                               smart_str_appendc(pretval, *(char *)p);
+                                               char_cnt--;
+                                       } else {
+                                               static char qp_digits[] = "0123456789ABCDEF";
+                                               smart_str_appendc(pretval, '=');
+                                               smart_str_appendc(pretval, qp_digits[(*p >> 4) & 0x0f]);
+                                               smart_str_appendc(pretval, qp_digits[(*p & 0x0f)]);
+                                               char_cnt -= 3;
                                        }
-                                       prev_in_left = in_left;
                                }
+                               prev_in_left = in_left;
 
                                smart_str_appendl(pretval, "?=", sizeof("?=") - 1);
                                char_cnt -= 2;
@@ -1904,19 +1947,19 @@ PHP_FUNCTION(iconv_mime_encode)
                }
        }
 
+       in_charset = ICONVG(internal_encoding);
+
        if (zend_hash_find(Z_ARRVAL_P(pref), "input-charset", sizeof("input-charset"), (void **)&ppval) == SUCCESS) {
                if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
                        in_charset = Z_STRVAL_PP(ppval);
-               } else {
-                       in_charset = ICONVG(internal_encoding);
                }
        }
 
+       out_charset = in_charset;
+
        if (zend_hash_find(Z_ARRVAL_P(pref), "output-charset", sizeof("output-charset"), (void **)&ppval) == SUCCESS) {
                if (Z_TYPE_PP(ppval) == IS_STRING && Z_STRLEN_PP(ppval) > 0) {
                        out_charset = Z_STRVAL_PP(ppval);
-               } else {
-                       out_charset = in_charset;
                }
        }