[PECL-CVS] [pecl-mail-mailparse] master: Merge pull request #45 from alexdowad/decode
[email protected] (Rasmus Lerdorf via GitHub) Sun, 5 Apr 2026 11:50:55 +0000
| Newsgroups | php.pecl.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Rasmus Lerdorf (rlerdorf)
Committer: GitHub (web-flow)
Pusher: rlerdorf
Date: 2026-04-05T12:50:48+01:00
Commit: https://github.com/php/pecl-mail-mailparse/commit/9894135fdf32c1cdede5469fadb14091687381d9
Raw diff: https://github.com/php/pecl-mail-mailparse/commit/9894135fdf32c1cdede5469fadb14091687381d9.diff
Merge pull request #45 from alexdowad/decode
Vendor encoding conversion code from mbstring
Changed paths:
A mailparse_encoding.c
A mailparse_encoding.h
M config.m4
M config.w32
M mailparse.c
M php_mailparse.h
M php_mailparse_mime.c
M php_mailparse_mime.h
Diff:
diff --git a/config.m4 b/config.m4
index eca408c..5309c51 100644
--- a/config.m4
+++ b/config.m4
@@ -2,23 +2,6 @@ PHP_ARG_ENABLE(mailparse, whether to enable mailparse support,
[ --enable-mailparse Enable mailparse support.])
if test "$PHP_MAILPARSE" != "no"; then
- if test "$ext_shared" != "yes" && test "$enable_mbstring" != "yes"; then
- AC_MSG_WARN(Activating mbstring)
- enable_mbstring=yes
- fi
-
- AC_MSG_CHECKING(libmbfl headers)
- if test -f $abs_srcdir/ext/mbstring/libmbfl/mbfl/mbfilter.h; then
- dnl build in php-src tree
- AC_MSG_RESULT(found in $abs_srcdir/ext/mbstring)
- elif test -f $phpincludedir/ext/mbstring/libmbfl/mbfl/mbfilter.h; then
- dnl build alone
- AC_MSG_RESULT(found in $phpincludedir/ext/mbstring)
- else
- AC_MSG_ERROR(mbstring extension with libmbfl is missing)
- fi
-
- PHP_NEW_EXTENSION(mailparse, mailparse.c php_mailparse_mime.c php_mailparse_rfc822.c, $ext_shared)
- PHP_ADD_EXTENSION_DEP(mailparse, mbstring, true)
+ PHP_NEW_EXTENSION(mailparse, mailparse.c php_mailparse_mime.c php_mailparse_rfc822.c mailparse_encoding.c, $ext_shared)
PHP_ADD_MAKEFILE_FRAGMENT
fi
diff --git a/config.w32 b/config.w32
index 772d372..383bec8 100644
--- a/config.w32
+++ b/config.w32
@@ -3,17 +3,9 @@
ARG_ENABLE("mailparse", "MAILPARSE support", "no");
if (PHP_MAILPARSE != "no") {
+ EXTENSION('mailparse', 'mailparse.c php_mailparse_mime.c php_mailparse_rfc822.c mailparse_encoding.c');
+ AC_DEFINE('HAVE_MAILPARSE', 1);
- if (PHP_MBSTRING == "no") {
- WARNING("mailparse requires mbstring");
- } else {
- EXTENSION('mailparse', 'mailparse.c php_mailparse_mime.c php_mailparse_rfc822.c');
- AC_DEFINE('HAVE_MAILPARSE', 1);
-
-
-// MFO.WriteLine(configure_module_dirname + "\\php_mailparse_rfc822.c: " + configure_module_dirname + "\\php_mailparse_rfc822.re");
-// MFO.WriteLine("\t$(RE2C) -b " + configure_module_dirname + "\\php_mailparse_rfc822.re > " + configure_module_dirname + "\\php_mailparse_rfc822.c");
-
- ADD_EXTENSION_DEP('mailparse', 'mbstring');
- }
+// MFO.WriteLine(configure_module_dirname + "\\php_mailparse_rfc822.c: " + configure_module_dirname + "\\php_mailparse_rfc822.re");
+// MFO.WriteLine("\t$(RE2C) -b " + configure_module_dirname + "\\php_mailparse_rfc822.re > " + configure_module_dirname + "\\php_mailparse_rfc822.c");
}
diff --git a/mailparse.c b/mailparse.c
index 5dac132..ba65418 100644
--- a/mailparse.c
+++ b/mailparse.c
@@ -28,11 +28,6 @@
#include "arginfo.h"
-/* just in case the config check doesn't enable mbstring automatically */
-#if !HAVE_MBSTRING
-#error The mailparse extension requires the mbstring extension!
-#endif
-
#define MAILPARSE_DECODE_NONE 0 /* include headers and leave section untouched */
#define MAILPARSE_DECODE_8BIT 1 /* decode body into 8-bit */
#define MAILPARSE_DECODE_NOHEADERS 2 /* don't include the headers */
@@ -92,15 +87,8 @@ zend_function_entry mailparse_functions[] = {
PHP_FE_END
};
-static const zend_module_dep mailparse_deps[] = {
- ZEND_MOD_REQUIRED("mbstring")
- ZEND_MOD_END
-};
-
zend_module_entry mailparse_module_entry = {
- STANDARD_MODULE_HEADER_EX,
- NULL,
- mailparse_deps,
+ STANDARD_MODULE_HEADER,
"mailparse",
mailparse_functions,
PHP_MINIT(mailparse),
@@ -914,7 +902,7 @@ PHP_FUNCTION(mailparse_determine_best_xfer_encoding)
int longline = 0;
int linelen = 0;
int c;
- enum mbfl_no_encoding bestenc = mbfl_no_encoding_7bit;
+ enum mb_no_encoding bestenc = mb_no_encoding_7bit;
php_stream *stream;
char * name;
@@ -930,9 +918,9 @@ PHP_FUNCTION(mailparse_determine_best_xfer_encoding)
if (c == EOF)
break;
if (c > 0x80)
- bestenc = mbfl_no_encoding_8bit;
+ bestenc = mb_no_encoding_8bit;
else if (c == 0) {
- bestenc = mbfl_no_encoding_base64;
+ bestenc = mb_no_encoding_base64;
longline = 0;
break;
}
@@ -942,20 +930,28 @@ PHP_FUNCTION(mailparse_determine_best_xfer_encoding)
longline = 1;
}
if (longline) {
- bestenc = mbfl_no_encoding_qprint;
+ bestenc = mb_no_encoding_qprint;
}
php_stream_rewind(stream);
-#if PHP_VERSION_ID < 80100
- name = (char *)mbfl_no2preferred_mime_name(bestenc);
-#else
- name = (char *)mbfl_encoding_preferred_mime_name(mbfl_no2encoding(bestenc));
-#endif
- if (name) {
- RETVAL_STRING(name);
- } else {
- RETVAL_FALSE;
+ /* Get the MIME name for the encoding */
+ switch (bestenc) {
+ case mb_no_encoding_7bit:
+ name = "7bit";
+ break;
+ case mb_no_encoding_8bit:
+ name = "8bit";
+ break;
+ case mb_no_encoding_base64:
+ name = "BASE64";
+ break;
+ case mb_no_encoding_qprint:
+ name = "quoted-printable";
+ break;
+ default:
+ RETURN_FALSE;
}
+ RETVAL_STRING(name);
}
/* }}} */
@@ -981,9 +977,9 @@ PHP_FUNCTION(mailparse_stream_encode)
char *buf;
size_t len;
size_t bufsize = 2048;
- const mbfl_encoding *encoding;
- enum mbfl_no_encoding enc;
- mbfl_convert_filter *conv = NULL;
+ const mb_encoding *encoding;
+ enum mb_no_encoding enc;
+ mb_convert_filter *conv = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rrS", &srcfile, &destfile, &encod) == FAILURE) {
RETURN_FALSE;
@@ -999,7 +995,7 @@ PHP_FUNCTION(mailparse_stream_encode)
php_stream_from_zval(srcstream, srcfile);
php_stream_from_zval(deststream, destfile);
- encoding = mbfl_name2encoding(ZSTR_VAL(encod));
+ encoding = mb_name2encoding(ZSTR_VAL(encod));
if (encoding) {
enc = encoding->no_encoding;
} else {
@@ -1013,14 +1009,14 @@ PHP_FUNCTION(mailparse_stream_encode)
buf = emalloc(bufsize);
RETVAL_TRUE;
- conv = mbfl_convert_filter_new(mbfl_no2encoding(mbfl_no_encoding_8bit),
- mbfl_no2encoding(enc),
+ conv = mb_convert_filter_new(mb_no2encoding(mb_no_encoding_8bit),
+ mb_no2encoding(enc),
mailparse_stream_output,
mailparse_stream_flush,
deststream
);
- if (enc == mbfl_no_encoding_qprint) {
+ if (enc == mb_no_encoding_qprint) {
/* If the qp encoded section is going to be digitally signed,
* it is a good idea to make sure that lines that begin "From "
* have the letter F encoded, so that MTAs do not stick a > character
@@ -1032,7 +1028,7 @@ PHP_FUNCTION(mailparse_stream_encode)
len = strlen(buf);
if (strncmp(buf, "From ", 5) == 0) {
- mbfl_convert_filter_flush(conv);
+ mb_convert_filter_flush(conv);
php_stream_write(deststream, "=46rom ", 7);
i = 5;
} else {
@@ -1040,7 +1036,7 @@ PHP_FUNCTION(mailparse_stream_encode)
}
for (; i<len; i++)
- mbfl_convert_filter_feed(buf[i], conv);
+ mb_convert_filter_feed(buf[i], conv);
}
}
@@ -1051,13 +1047,13 @@ PHP_FUNCTION(mailparse_stream_encode)
{
size_t i;
for (i=0; i<len; i++)
- mbfl_convert_filter_feed(buf[i], conv);
+ mb_convert_filter_feed(buf[i], conv);
}
}
}
- mbfl_convert_filter_flush(conv);
- mbfl_convert_filter_delete(conv);
+ mb_convert_filter_flush(conv);
+ mb_convert_filter_delete(conv);
efree(buf);
}
/* }}} */
diff --git a/mailparse_encoding.c b/mailparse_encoding.c
new file mode 100644
index 0000000..f07cdd9
--- /dev/null
+++ b/mailparse_encoding.c
@@ -0,0 +1,531 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | [email protected] so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+*/
+
+/*
+ * Vendored encoding support for mailparse
+ *
+ * This code is derived from libmbfl, as modified in the mbstring extension,
+ * and has been adapted for standalone use in mailparse.
+ *
+ * Original libmbfl code copyright (c) 1998-2002 HappySize, Inc.
+ * Licensed under GNU Lesser General Public License (version 2)
+ *
+ * The source code includes portions from:
+ * - mbfilter_base64.c (Base64 encoding/decoding)
+ * - mbfilter_qprint.c (Quoted-Printable encoding/decoding)
+ * - mbfl_convert.c (Filter infrastructure)
+ */
+
+#include "php.h"
+#include "mailparse_encoding.h"
+#include <string.h>
+
+#define CK(statement) do { if ((statement) < 0) return (-1); } while (0)
+
+/* Forward declarations */
+static int mb_filt_conv_base64enc(int c, mb_convert_filter *filter);
+static int mb_filt_conv_base64enc_flush(mb_convert_filter *filter);
+static int mb_filt_conv_base64dec(int c, mb_convert_filter *filter);
+static int mb_filt_conv_base64dec_flush(mb_convert_filter *filter);
+static int mb_filt_conv_qprintenc(int c, mb_convert_filter *filter);
+static int mb_filt_conv_qprintenc_flush(mb_convert_filter *filter);
+static int mb_filt_conv_qprintdec(int c, mb_convert_filter *filter);
+static int mb_filt_conv_qprintdec_flush(mb_convert_filter *filter);
+static void mb_filt_conv_common_ctor(mb_convert_filter *filter);
+
+/* Encoding definitions */
+static const mb_encoding mb_encoding_7bit = { mb_no_encoding_7bit, "7bit" };
+static const mb_encoding mb_encoding_8bit = { mb_no_encoding_8bit, "8bit" };
+static const mb_encoding mb_encoding_base64 = { mb_no_encoding_base64, "BASE64" };
+static const mb_encoding mb_encoding_qprint = { mb_no_encoding_qprint, "Quoted-Printable" };
+
+/* Virtual tables for conversions */
+static const mb_convert_vtbl vtbl_8bit_b64 = {
+ mb_no_encoding_8bit,
+ mb_no_encoding_base64,
+ mb_filt_conv_common_ctor,
+ NULL,
+ mb_filt_conv_base64enc,
+ mb_filt_conv_base64enc_flush
+};
+
+static const mb_convert_vtbl vtbl_b64_8bit = {
+ mb_no_encoding_base64,
+ mb_no_encoding_8bit,
+ mb_filt_conv_common_ctor,
+ NULL,
+ mb_filt_conv_base64dec,
+ mb_filt_conv_base64dec_flush
+};
+
+static const mb_convert_vtbl vtbl_8bit_qprint = {
+ mb_no_encoding_8bit,
+ mb_no_encoding_qprint,
+ mb_filt_conv_common_ctor,
+ NULL,
+ mb_filt_conv_qprintenc,
+ mb_filt_conv_qprintenc_flush
+};
+
+static const mb_convert_vtbl vtbl_qprint_8bit = {
+ mb_no_encoding_qprint,
+ mb_no_encoding_8bit,
+ mb_filt_conv_common_ctor,
+ NULL,
+ mb_filt_conv_qprintdec,
+ mb_filt_conv_qprintdec_flush
+};
+
+/* =============================================================================
+ * BASE64 encoding/decoding
+ * ============================================================================= */
+
+static const unsigned char mb_base64_table[] = {
+ /* 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', */
+ 0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,
+ /* 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', */
+ 0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,
+ /* 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', */
+ 0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,
+ /* 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', */
+ 0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,
+ /* '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0' */
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x2b,0x2f,0x00
+};
+
+/* any => BASE64 */
+static int mb_filt_conv_base64enc(int c, mb_convert_filter *filter)
+{
+ int n;
+
+ n = (filter->status & 0xff);
+ if (n == 0) {
+ filter->status++;
+ filter->cache = (c & 0xff) << 16;
+ } else if (n == 1) {
+ filter->status++;
+ filter->cache |= (c & 0xff) << 8;
+ } else {
+ filter->status &= ~0xff;
+ n = (filter->status & 0xff00) >> 8;
+ if (n > 72) {
+ CK((*filter->output_function)(0x0d, filter->data)); /* CR */
+ CK((*filter->output_function)(0x0a, filter->data)); /* LF */
+ filter->status &= ~0xff00;
+ }
+ filter->status += 0x400;
+ n = filter->cache | (c & 0xff);
+ CK((*filter->output_function)(mb_base64_table[(n >> 18) & 0x3f], filter->data));
+ CK((*filter->output_function)(mb_base64_table[(n >> 12) & 0x3f], filter->data));
+ CK((*filter->output_function)(mb_base64_table[(n >> 6) & 0x3f], filter->data));
+ CK((*filter->output_function)(mb_base64_table[n & 0x3f], filter->data));
+ }
+
+ return 0;
+}
+
+static int mb_filt_conv_base64enc_flush(mb_convert_filter *filter)
+{
+ int status, cache, len;
+
+ status = filter->status & 0xff;
+ cache = filter->cache;
+ len = (filter->status & 0xff00) >> 8;
+ filter->status &= ~0xffff;
+ filter->cache = 0;
+ /* flush fragments */
+ if (status >= 1) {
+ if (len > 72){
+ CK((*filter->output_function)(0x0d, filter->data)); /* CR */
+ CK((*filter->output_function)(0x0a, filter->data)); /* LF */
+ }
+ CK((*filter->output_function)(mb_base64_table[(cache >> 18) & 0x3f], filter->data));
+ CK((*filter->output_function)(mb_base64_table[(cache >> 12) & 0x3f], filter->data));
+ if (status == 1) {
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ } else {
+ CK((*filter->output_function)(mb_base64_table[(cache >> 6) & 0x3f], filter->data));
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ }
+ }
+
+ if (filter->flush_function) {
+ (*filter->flush_function)(filter->data);
+ }
+
+ return 0;
+}
+
+/* BASE64 => any */
+static int mb_filt_conv_base64dec(int c, mb_convert_filter *filter)
+{
+ int n;
+
+ if (c == 0x0d || c == 0x0a || c == 0x20 || c == 0x09 || c == 0x3d) { /* CR or LF or SPACE or HTAB or '=' */
+ return 0;
+ }
+
+ n = 0;
+ if (c >= 0x41 && c <= 0x5a) { /* A - Z */
+ n = c - 65;
+ } else if (c >= 0x61 && c <= 0x7a) { /* a - z */
+ n = c - 71;
+ } else if (c >= 0x30 && c <= 0x39) { /* 0 - 9 */
+ n = c + 4;
+ } else if (c == 0x2b) { /* '+' */
+ n = 62;
+ } else if (c == 0x2f) { /* '/' */
+ n = 63;
+ } else {
+ /* Invalid character - output a marker but continue */
+ return 0;
+ }
+ n &= 0x3f;
+
+ switch (filter->status) {
+ case 0:
+ filter->status = 1;
+ filter->cache = n << 18;
+ break;
+ case 1:
+ filter->status = 2;
+ filter->cache |= n << 12;
+ break;
+ case 2:
+ filter->status = 3;
+ filter->cache |= n << 6;
+ break;
+ default:
+ filter->status = 0;
+ n |= filter->cache;
+ CK((*filter->output_function)((n >> 16) & 0xff, filter->data));
+ CK((*filter->output_function)((n >> 8) & 0xff, filter->data));
+ CK((*filter->output_function)(n & 0xff, filter->data));
+ break;
+ }
+
+ return 0;
+}
+
+static int mb_filt_conv_base64dec_flush(mb_convert_filter *filter)
+{
+ int status, cache;
+
+ status = filter->status;
+ cache = filter->cache;
+ filter->status = 0;
+ filter->cache = 0;
+ /* flush fragments */
+ if (status >= 2) {
+ CK((*filter->output_function)((cache >> 16) & 0xff, filter->data));
+ if (status >= 3) {
+ CK((*filter->output_function)((cache >> 8) & 0xff, filter->data));
+ }
+ }
+
+ if (filter->flush_function) {
+ (*filter->flush_function)(filter->data);
+ }
+
+ return 0;
+}
+
+/* =============================================================================
+ * Quoted-Printable encoding/decoding
+ * ============================================================================= */
+
+static int hex2code_map[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+/* any => Quoted-Printable */
+static int mb_filt_conv_qprintenc(int c, mb_convert_filter *filter)
+{
+ int s, n;
+
+ switch (filter->status & 0xff) {
+ case 0:
+ filter->cache = c;
+ filter->status++;
+ break;
+ default:
+ s = filter->cache;
+ filter->cache = c;
+ n = (filter->status & 0xff00) >> 8;
+
+ if (s == 0) { /* null */
+ CK((*filter->output_function)(s, filter->data));
+ filter->status &= ~0xff00;
+ break;
+ }
+
+ if (s == '\n' || (s == '\r' && c != '\n')) { /* line feed */
+ CK((*filter->output_function)('\r', filter->data));
+ CK((*filter->output_function)('\n', filter->data));
+ filter->status &= ~0xff00;
+ break;
+ } else if (s == 0x0d) {
+ break;
+ }
+
+ if (n >= 72) { /* soft line feed */
+ CK((*filter->output_function)('=', filter->data));
+ CK((*filter->output_function)('\r', filter->data));
+ CK((*filter->output_function)('\n', filter->data));
+ filter->status &= ~0xff00;
+ }
+
+ if (s <= 0 || s >= 0x80 || s == '=') { /* not ASCII or '=' */
+ /* hex-octet */
+ CK((*filter->output_function)('=', filter->data));
+ n = (s >> 4) & 0xf;
+ if (n < 10) {
+ n += 48; /* '0' */
+ } else {
+ n += 55; /* 'A' - 10 */
+ }
+ CK((*filter->output_function)(n, filter->data));
+ n = s & 0xf;
+ if (n < 10) {
+ n += 48;
+ } else {
+ n += 55;
+ }
+ CK((*filter->output_function)(n, filter->data));
+ filter->status += 0x300;
+ } else {
+ CK((*filter->output_function)(s, filter->data));
+ filter->status += 0x100;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int mb_filt_conv_qprintenc_flush(mb_convert_filter *filter)
+{
+ /* flush filter cache */
+ (*filter->filter_function)('\0', filter);
+ filter->status &= ~0xffff;
+ filter->cache = 0;
+
+ if (filter->flush_function) {
+ (*filter->flush_function)(filter->data);
+ }
+
+ return 0;
+}
+
+/* Quoted-Printable => any */
+static int mb_filt_conv_qprintdec(int c, mb_convert_filter *filter)
+{
+ int n, m;
+
+ switch (filter->status) {
+ case 1:
+ if (hex2code_map[c & 0xff] >= 0) {
+ filter->cache = c;
+ filter->status = 2;
+ } else if (c == 0x0d) { /* soft line feed */
+ filter->status = 3;
+ } else if (c == 0x0a) { /* soft line feed */
+ filter->status = 0;
+ } else {
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ CK((*filter->output_function)(c, filter->data));
+ filter->status = 0;
+ }
+ break;
+ case 2:
+ m = hex2code_map[c & 0xff];
+ if (m < 0) {
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ CK((*filter->output_function)(filter->cache, filter->data));
+ n = c;
+ } else {
+ n = hex2code_map[filter->cache] << 4 | m;
+ }
+ CK((*filter->output_function)(n, filter->data));
+ filter->status = 0;
+ break;
+ case 3:
+ if (c != 0x0a) { /* LF */
+ CK((*filter->output_function)(c, filter->data));
+ }
+ filter->status = 0;
+ break;
+ default:
+ if (c == 0x3d) { /* '=' */
+ filter->status = 1;
+ } else {
+ CK((*filter->output_function)(c, filter->data));
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int mb_filt_conv_qprintdec_flush(mb_convert_filter *filter)
+{
+ int status, cache;
+
+ status = filter->status;
+ cache = filter->cache;
+ filter->status = 0;
+ filter->cache = 0;
+ /* flush fragments */
+ if (status == 1) {
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ } else if (status == 2) {
+ CK((*filter->output_function)(0x3d, filter->data)); /* '=' */
+ CK((*filter->output_function)(cache, filter->data));
+ }
+
+ if (filter->flush_function) {
+ (*filter->flush_function)(filter->data);
+ }
+
+ return 0;
+}
+
+/* =============================================================================
+ * Filter infrastructure
+ * ============================================================================= */
+
+static void mb_filt_conv_common_ctor(mb_convert_filter *filter)
+{
+ filter->status = 0;
+ filter->cache = 0;
+}
+
+static const mb_convert_vtbl* mb_convert_filter_get_vtbl(const mb_encoding *from, const mb_encoding *to)
+{
+ if (from->no_encoding == mb_no_encoding_8bit && to->no_encoding == mb_no_encoding_base64) {
+ return &vtbl_8bit_b64;
+ } else if (from->no_encoding == mb_no_encoding_base64 && to->no_encoding == mb_no_encoding_8bit) {
+ return &vtbl_b64_8bit;
+ } else if (from->no_encoding == mb_no_encoding_8bit && to->no_encoding == mb_no_encoding_qprint) {
+ return &vtbl_8bit_qprint;
+ } else if (from->no_encoding == mb_no_encoding_qprint && to->no_encoding == mb_no_encoding_8bit) {
+ return &vtbl_qprint_8bit;
+ }
+ return NULL;
+}
+
+static void mb_convert_filter_init(mb_convert_filter *filter, const mb_encoding *from, const mb_encoding *to,
+ const mb_convert_vtbl *vtbl, mb_output_function_t output_function, mb_flush_function_t flush_function, void* data)
+{
+ filter->from = from;
+ filter->to = to;
+ filter->output_function = output_function;
+ filter->flush_function = flush_function;
+ filter->data = data;
+ filter->filter_dtor = vtbl->filter_dtor;
+ filter->filter_function = vtbl->filter_function;
+ filter->filter_flush = vtbl->filter_flush;
+
+ if (vtbl->filter_ctor) {
+ (*vtbl->filter_ctor)(filter);
+ }
+}
+
+mb_convert_filter* mb_convert_filter_new(const mb_encoding *from, const mb_encoding *to,
+ mb_output_function_t output_function, mb_flush_function_t flush_function, void* data)
+{
+ const mb_convert_vtbl *vtbl = mb_convert_filter_get_vtbl(from, to);
+ if (vtbl == NULL) {
+ return NULL;
+ }
+
+ mb_convert_filter *filter = emalloc(sizeof(mb_convert_filter));
+ mb_convert_filter_init(filter, from, to, vtbl, output_function, flush_function, data);
+ return filter;
+}
+
+void mb_convert_filter_delete(mb_convert_filter *filter)
+{
+ if (filter->filter_dtor) {
+ (*filter->filter_dtor)(filter);
+ }
+ efree(filter);
+}
+
+int mb_convert_filter_feed(int c, mb_convert_filter *filter)
+{
+ return (*filter->filter_function)(c, filter);
+}
+
+int mb_convert_filter_flush(mb_convert_filter *filter)
+{
+ (*filter->filter_flush)(filter);
+ return 0;
+}
+
+/* =============================================================================
+ * Encoding lookup functions
+ * ============================================================================= */
+
+const mb_encoding* mb_name2encoding(const char *name)
+{
+ if (name == NULL) {
+ return NULL;
+ }
+
+ /* Case-insensitive comparison for encoding names */
+ if (strcasecmp(name, "base64") == 0 || strcasecmp(name, "BASE64") == 0) {
+ return &mb_encoding_base64;
+ } else if (strcasecmp(name, "quoted-printable") == 0 || strcasecmp(name, "qprint") == 0) {
+ return &mb_encoding_qprint;
+ } else if (strcasecmp(name, "8bit") == 0) {
+ return &mb_encoding_8bit;
+ } else if (strcasecmp(name, "7bit") == 0) {
+ return &mb_encoding_7bit;
+ }
+
+ return NULL;
+}
+
+const mb_encoding* mb_no2encoding(enum mb_no_encoding no_encoding)
+{
+ switch (no_encoding) {
+ case mb_no_encoding_base64:
+ return &mb_encoding_base64;
+ case mb_no_encoding_qprint:
+ return &mb_encoding_qprint;
+ case mb_no_encoding_8bit:
+ return &mb_encoding_8bit;
+ case mb_no_encoding_7bit:
+ return &mb_encoding_7bit;
+ default:
+ return NULL;
+ }
+}
diff --git a/mailparse_encoding.h b/mailparse_encoding.h
new file mode 100644
index 0000000..0ae4b77
--- /dev/null
+++ b/mailparse_encoding.h
@@ -0,0 +1,98 @@
+/*
+ +----------------------------------------------------------------------+
+ | Copyright (c) The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | [email protected] so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+*/
+
+/*
+ * Vendored encoding support for mailparse
+ *
+ * This code is derived from libmbfl, as modified in the mbstring extension,
+ * and has been adapted for standalone use in mailparse.
+ *
+ * Original libmbfl code copyright (c) 1998-2002 HappySize, Inc.
+ * Licensed under GNU Lesser General Public License (version 2)
+ */
+
+#ifndef MAILPARSE_ENCODING_H
+#define MAILPARSE_ENCODING_H
+
+#include "php.h"
+
+/* Encoding identifiers */
+enum mb_no_encoding {
+ mb_no_encoding_invalid = -1,
+ mb_no_encoding_7bit,
+ mb_no_encoding_8bit,
+ mb_no_encoding_base64,
+ mb_no_encoding_qprint
+};
+
+/* Forward declarations */
+typedef struct _mb_convert_filter mb_convert_filter;
+typedef struct _mb_encoding mb_encoding;
+typedef struct mb_convert_vtbl mb_convert_vtbl;
+
+/* Function pointer types */
+typedef int (*mb_output_function_t)(int c, void *data);
+typedef int (*mb_flush_function_t)(void *data);
+typedef int (*mb_filter_function_t)(int c, mb_convert_filter *filter);
+typedef int (*mb_filter_flush_t)(mb_convert_filter *filter);
+typedef void (*mb_filter_ctor_t)(mb_convert_filter *filter);
+typedef void (*mb_filter_dtor_t)(mb_convert_filter *filter);
+
+/* Encoding structure */
+struct _mb_encoding {
+ enum mb_no_encoding no_encoding;
+ const char *name;
+};
+
+/* Virtual table for convert filters */
+struct mb_convert_vtbl {
+ enum mb_no_encoding from;
+ enum mb_no_encoding to;
+ mb_filter_ctor_t filter_ctor;
+ mb_filter_dtor_t filter_dtor;
+ mb_filter_function_t filter_function;
+ mb_filter_flush_t filter_flush;
+};
+
+/* Convert filter structure */
+struct _mb_convert_filter {
+ mb_filter_dtor_t filter_dtor;
+ mb_filter_function_t filter_function;
+ mb_filter_flush_t filter_flush;
+ mb_output_function_t output_function;
+ mb_flush_function_t flush_function;
+ void *data;
+ int status;
+ int cache;
+ const mb_encoding *from;
+ const mb_encoding *to;
+};
+
+/* Public API functions */
+mb_convert_filter* mb_convert_filter_new(
+ const mb_encoding *from,
+ const mb_encoding *to,
+ mb_output_function_t output_function,
+ mb_flush_function_t flush_function,
+ void *data
+);
+
+void mb_convert_filter_delete(mb_convert_filter *filter);
+int mb_convert_filter_feed(int c, mb_convert_filter *filter);
+int mb_convert_filter_flush(mb_convert_filter *filter);
+
+const mb_encoding* mb_name2encoding(const char *name);
+const mb_encoding* mb_no2encoding(enum mb_no_encoding no_encoding);
+
+#endif /* MAILPARSE_ENCODING_H */
diff --git a/php_mailparse.h b/php_mailparse.h
index e8f5584..bf1ed13 100644
--- a/php_mailparse.h
+++ b/php_mailparse.h
@@ -76,8 +76,7 @@ PHP_METHOD(mimemessage, extract_uue);
PHP_METHOD(mimemessage, remove);
PHP_METHOD(mimemessage, add_child);
-# include "ext/mbstring/libmbfl/mbfl/mbfilter.h"
-
+#include "mailparse_encoding.h"
#include "php_mailparse_rfc822.h"
#include "php_mailparse_mime.h"
diff --git a/php_mailparse_mime.c b/php_mailparse_mime.c
index 87794b0..afa28bd 100644
--- a/php_mailparse_mime.c
+++ b/php_mailparse_mime.c
@@ -914,20 +914,20 @@ static int filter_into_work_buffer(int c, void *dat)
PHP_MAILPARSE_API void php_mimepart_decoder_prepare(php_mimepart *part, int do_decode, php_mimepart_extract_func_t decoder, void *ptr)
{
- const mbfl_encoding *encoding;
- enum mbfl_no_encoding from = mbfl_no_encoding_8bit;
+ const mb_encoding *encoding;
+ enum mb_no_encoding from = mb_no_encoding_8bit;
if (do_decode && part->content_transfer_encoding) {
- encoding = mbfl_name2encoding(part->content_transfer_encoding);
+ encoding = mb_name2encoding(part->content_transfer_encoding);
if (encoding) {
from = encoding->no_encoding;
} else {
if (strcasecmp("binary", part->content_transfer_encoding) != 0) {
- zend_error(E_WARNING, "%s(): mbstring doesn't know how to decode %s transfer encoding!",
+ zend_error(E_WARNING, "%s(): unknown transfer encoding %s!",
get_active_function_name(),
part->content_transfer_encoding);
}
- from = mbfl_no_encoding_8bit;
+ from = mb_no_encoding_8bit;
}
}
@@ -936,11 +936,11 @@ PHP_MAILPARSE_API void php_mimepart_decoder_prepare(php_mimepart *part, int do_d
part->parsedata.workbuf.len = 0;
if (do_decode) {
- if (from == mbfl_no_encoding_8bit || from == mbfl_no_encoding_7bit) {
+ if (from == mb_no_encoding_8bit || from == mb_no_encoding_7bit) {
part->extract_filter = NULL;
} else {
- part->extract_filter = mbfl_convert_filter_new(
- mbfl_no2encoding(from), mbfl_no2encoding(mbfl_no_encoding_8bit),
+ part->extract_filter = mb_convert_filter_new(
+ mb_no2encoding(from), mb_no2encoding(mb_no_encoding_8bit),
filter_into_work_buffer,
NULL,
part
@@ -953,8 +953,8 @@ PHP_MAILPARSE_API void php_mimepart_decoder_prepare(php_mimepart *part, int do_d
PHP_MAILPARSE_API void php_mimepart_decoder_finish(php_mimepart *part)
{
if (part->extract_filter) {
- mbfl_convert_filter_flush(part->extract_filter);
- mbfl_convert_filter_delete(part->extract_filter);
+ mb_convert_filter_flush(part->extract_filter);
+ mb_convert_filter_delete(part->extract_filter);
}
if (part->extract_func && part->parsedata.workbuf.len > 0) {
part->extract_func(part, part->extract_context, part->parsedata.workbuf.c, part->parsedata.workbuf.len);
@@ -969,7 +969,7 @@ PHP_MAILPARSE_API int php_mimepart_decoder_feed(php_mimepart *part, const char *
if (part->extract_filter) {
for (i = 0; i < bufsize; i++) {
- if (mbfl_convert_filter_feed(buf[i], part->extract_filter) < 0) {
+ if (mb_convert_filter_feed(buf[i], part->extract_filter) < 0) {
zend_error(E_WARNING, "%s() - filter conversion failed. Input message is probably incorrectly encoded\n",
get_active_function_name());
return -1;
diff --git a/php_mailparse_mime.h b/php_mailparse_mime.h
index 38c5d40..b96aa1f 100644
--- a/php_mailparse_mime.h
+++ b/php_mailparse_mime.h
@@ -62,7 +62,7 @@ struct _php_mimepart {
/* these are used during part extraction */
php_mimepart_extract_func_t extract_func;
- mbfl_convert_filter *extract_filter;
+ mb_convert_filter *extract_filter;
void *extract_context;
/* these are used during parsing */