diff --git a/config.php.in b/config.php.in index 72839a5d..1aa24fbc 100644 --- a/config.php.in +++ b/config.php.in @@ -277,6 +277,7 @@ $config['JQUERY_DATE_FORMAT'] = 'yy-mm-dd'; $config['DECIMAL_SEPARATOR'] = "."; // See https://www.php.net/manual/en/function.number-format $config['THOUSANDS_SEPARATOR'] = ","; // for the format options +$config['ENABLE_GB2312_FIX'] = 1; $config['FROM_LENGTH_TO_SHOW'] = 28; diff --git a/webui/system/helper/mime.php b/webui/system/helper/mime.php index 3604a450..7f89a25d 100644 --- a/webui/system/helper/mime.php +++ b/webui/system/helper/mime.php @@ -2,7 +2,7 @@ class Piler_Mime_Decode { - const HEADER_FIELDS = ['from', 'to', 'cc', 'subject', 'date']; + const HEADER_FIELDS = ['from', 'sender', 'to', 'cc', 'subject', 'date']; public static function normalize_message($message) { @@ -24,10 +24,10 @@ class Piler_Mime_Decode { self::parseMessage($body, $result); } else { - $result[] = array( + $result[] = [ 'headers' => $headers, 'body' => $body - ); + ]; } return; @@ -45,7 +45,7 @@ class Piler_Mime_Decode { } else { if(in_array($headers['content-type']['type'], ["text/plain", "text/html"])) { - $result[] = array('headers' => $headers, 'body' => $body); + $result[] = ['headers' => $headers, 'body' => $body]; } else if($headers['content-type']['type'] == "message/rfc822") { self::parseMessage($body, $result); @@ -57,14 +57,14 @@ class Piler_Mime_Decode { public static function splitMime($body, $boundary) { $start = 0; - $res = array(); + $res = []; // Extract the mime parts excluding the boundary itself $p = strpos($body, '--' . $boundary . EOL, $start); if($p === false) { // no parts found! - return array(); + return []; } // Position after first boundary line @@ -80,7 +80,7 @@ class Piler_Mime_Decode { $p = strpos($body, '--' . $boundary . '--', $start); if($p === false) { - return array(); + return []; } // The remaining part also needs to be parsed: @@ -100,6 +100,8 @@ class Piler_Mime_Decode { $headers = []; $body = ''; + $message = self::normalize_message($message); + // Find an empty line between headers and body, otherwise we got a header-only message if(strpos($message, EOL . EOL)) { @@ -180,7 +182,7 @@ class Piler_Mime_Decode { continue; } - $headers[$lower] = array($headers[$lower], $header); + $headers[$lower] = [$headers[$lower], $header]; } // Add some default values, if they are missing @@ -196,9 +198,21 @@ class Piler_Mime_Decode { for($i=0; $i $v) { - if(strchr($v, EOL)) { $result[$k] = explode(EOL, $v); } @@ -269,7 +282,7 @@ class Piler_Mime_Decode { public static function splitContentType($field = '') { - $split = array(); + $split = []; $what = 'type'; $field = $what . '=' . $field; @@ -277,7 +290,7 @@ class Piler_Mime_Decode { return $split; } - $split = array(); + $split = []; foreach ($matches[1] as $key => $name) { $name = strtolower($name); if($matches[2][$key][0] == '"') { @@ -291,7 +304,7 @@ class Piler_Mime_Decode { } - public static function getBoundary($headers = array()) { + public static function getBoundary($headers = []) { if(isset($headers['content-type']['boundary'])) { return $headers['content-type']['boundary']; } @@ -300,7 +313,7 @@ class Piler_Mime_Decode { } - public static function fixMimeBodyPart($headers = array(), $body = '') { + public static function fixMimeBodyPart($headers = [], $body = '') { if(isset($headers['content-transfer-encoding'])) { if(strtolower($headers['content-transfer-encoding']) == 'quoted-printable') { @@ -313,9 +326,10 @@ class Piler_Mime_Decode { } if(isset($headers['content-type']['charset'])) { - if(strtolower($headers['content-type']['charset']) == 'gb2312') { + if(ENABLE_GB2312_FIX && strtolower($headers['content-type']['charset']) == 'gb2312') { $headers['content-type']['charset'] = 'GBK'; } + $body = iconv($headers['content-type']['charset'], 'utf-8' . '//IGNORE', $body); } diff --git a/webui/tests/ParseMessageTest.php b/webui/tests/ParseMessageTest.php index fbf67cd1..80469b65 100644 --- a/webui/tests/ParseMessageTest.php +++ b/webui/tests/ParseMessageTest.php @@ -11,17 +11,17 @@ final class MailParserTest extends TestCase { public function providerTestParseMessage() { return [ - ["1.eml", 1, ["Liebe Gueste,\n\ndie Einarbeitung der Rechen- und Summenfunktionen ins RK-Formular"]], - ["2.eml", 1, ["Hallo!\nDie seltsamen Zeilenumbr=C3=BCche treten tats=C3=A4chlich auf."]], - ["3.eml", 1, ["\n\nCan we discuss? Send Reply For more information, THANKS."]], + ["1.eml", 1, ["Liebe Gueste,\r\n\r\ndie Einarbeitung der Rechen- und Summenfunktionen ins RK-Formular"]], + ["2.eml", 1, ["Hallo!\r\nDie seltsamen Zeilenumbr=C3=BCche treten tats=C3=A4chlich auf."]], + ["3.eml", 1, ["\r\n\r\nCan we discuss? Send Reply For more information, THANKS."]], ["4.eml", 2, ["=0D=0A=0D=0A=0D=0A=0D=0A", "=0D=0A"]], - ["8.eml", 2, ["Hello,\n\nYou have received a newsletter from Chemol Travel.", ""]], + ["7.eml", 2, ["Mai ajánlat: \r\n \r\n Exkluzív!", ""]], + ["8.eml", 2, ["Hello,\r\n\r\nYou have received a newsletter from Chemol Travel.", ""]], ]; - } + } /** diff --git a/webui/tests/SplitMessageTest.php b/webui/tests/SplitMessageTest.php index 8947de68..bfc047bf 100644 --- a/webui/tests/SplitMessageTest.php +++ b/webui/tests/SplitMessageTest.php @@ -1,70 +1,65 @@ 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\r\nTo:bbb\r\nSubject: test\r\n\r\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\r\nTo:bbb\r\nCC ccc\r\nSubject: test\r\n\r\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\r\nSender: alala@aaa\r\nTo:bbb\r\nCC ccc\r\nSubject: test\r\n\r\nThis is a test", + array('sender' => 'alala@aaa', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\r\nTo:bbb\r\nSubject: test\r\n\r\n\r\n\r\n" . THIS_IS_A_TEST . "\nAaa\n", - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - "\n\n" . THIS_IS_A_TEST . "\nAaa\n"], + ["From: aaa\r\nTo:bbb\r\nSubject: test\r\n\r\n\r\n\r\nThis is a test\nAaa\n", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "\r\n\r\nThis is a test\r\nAaa\r\n"], - ["From: aaa\r\nTo:bbb\r\nSubject: test\r\nContent-type: text/html\r\n\r\n\r\n" . THIS_IS_A_TEST . "\nAaa\n", - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => 'text/html')), - "\n" . THIS_IS_A_TEST . "\nAaa\n"], + ["From: aaa\r\nTo:bbb\r\nSubject: test\r\nContent-type: text/html\r\n\r\n\r\nThis is a test\nAaa\n", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/html')), + "\r\nThis is a test\r\nAaa\r\n"], - ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/plain\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/plain\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nDate: Sun, 17 Apr 2016 22:40:03 +0800\nDKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=chemoltravel.hu; s=ml;\n\tt=1471888357; bh=A/l/HLQe3HM4Xc4jFxAmhaWVCMU=;\n\th=Date:To:From:Subject:Sender:From:To:Subject:Date;\n\tb=JlEqXiAKBOoT/YyXKTMsXnEphh2J6sXxgNmbKbGybjo3cU1rgQEL0m1h26gl5AaBP\nContent-Type: " . TEXT_PLAIN . "\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', SUBJECT => 'test', 'date' => 'Sun, 17 Apr 2016 22:40:03 +0800', 'dkim-signature' => 'v=1; a=rsa-sha1; c=relaxed/relaxed; d=chemoltravel.hu; s=ml; t=1471888357; bh=A/l/HLQe3HM4Xc4jFxAmhaWVCMU=; h=Date:To:From:Subject:Sender:From:To:Subject:Date; b=JlEqXiAKBOoT/YyXKTMsXnEphh2J6sXxgNmbKbGybjo3cU1rgQEL0m1h26gl5AaBP', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nDate: Sun, 17 Apr 2016 22:40:03 +0800\nDKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=chemoltravel.hu; s=ml;\n\tt=1471888357; bh=A/l/HLQe3HM4Xc4jFxAmhaWVCMU=;\n\th=Date:To:From:Subject:Sender:From:To:Subject:Date;\n\tb=JlEqXiAKBOoT/YyXKTMsXnEphh2J6sXxgNmbKbGybjo3cU1rgQEL0m1h26gl5AaBP\nContent-Type: text/plain\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'subject' => 'test', 'date' => 'Sun, 17 Apr 2016 22:40:03 +0800', 'dkim-signature' => 'v=1; a=rsa-sha1; c=relaxed/relaxed; d=chemoltravel.hu; s=ml; t=1471888357; bh=A/l/HLQe3HM4Xc4jFxAmhaWVCMU=; h=Date:To:From:Subject:Sender:From:To:Subject:Date; b=JlEqXiAKBOoT/YyXKTMsXnEphh2J6sXxgNmbKbGybjo3cU1rgQEL0m1h26gl5AaBP', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/PLAIN\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN)), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/PLAIN\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/plain; charset=\"ISO-8859-1\"\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', CONTENT_TYPE => array('type' => TEXT_PLAIN, 'charset' => 'ISO-8859-1')), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nContent-Type: text/plain; charset=\"ISO-8859-1\"\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'content-type' => array('type' => 'text/plain', 'charset' => 'ISO-8859-1')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/alternative; boundary=\"_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_\"\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', 'mime-version' => '1.0', CONTENT_TYPE => array('type' => 'multipart/alternative', 'boundary' => '_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_')), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/alternative; boundary=\"_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_\"\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'mime-version' => '1.0', 'content-type' => array('type' => 'multipart/alternative', 'boundary' => '_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/alternative;\n boundary=\"_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_\"\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', 'mime-version' => '1.0', CONTENT_TYPE => array('type' => 'multipart/alternative', 'boundary' => '_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_')), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/alternative;\n boundary=\"_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_\"\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'mime-version' => '1.0', 'content-type' => array('type' => 'multipart/alternative', 'boundary' => '_=_SWIFT_v4_1460476188_145aa333fc0127705a7e904aab6d1957_=_')), + "This is a test"], - ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/related;\n\ttype=\"multipart/alternative\";\n\tboundary=\"----=_NextPart_000_0006_01D195BC.69E26510\"\n\n" . THIS_IS_A_TEST, - array('from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', SUBJECT => 'test', 'mime-version' => '1.0', CONTENT_TYPE => array('type' => 'multipart/alternative', 'boundary' => '----=_NextPart_000_0006_01D195BC.69E26510')), - THIS_IS_A_TEST], + ["From: aaa\nTo:bbb\nSubject: test\nMIME-Version: 1.0\nContent-Type: multipart/related;\n\ttype=\"multipart/alternative\";\n\tboundary=\"----=_NextPart_000_0006_01D195BC.69E26510\"\n\nThis is a test", + array('sender' => '', 'from' => 'aaa', 'to' => 'bbb', 'cc' => '', 'date' => '', 'subject' => 'test', 'mime-version' => '1.0', 'content-type' => array('type' => 'multipart/alternative', 'boundary' => '----=_NextPart_000_0006_01D195BC.69E26510')), + "This is a test"], - ]; - } + ]; + } /**