From 6b4f310cd228be3dc08aa0b3e7ad8699f5ec02be Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Mon, 4 Jul 2022 15:45:51 +0900 Subject: [PATCH] Email\Sending add dedicated kv folding flag kv folding is now done via flag and only if an encoded is detected that is japanese or UTF-8 --- 4dev/tests/CoreLibsCreateEmailTest.php | 125 ++++++++++++++++++++++++- www/admin/class_test.create_email.php | 27 +++++- www/lib/CoreLibs/Create/Email.php | 76 ++++++++++++--- 3 files changed, 205 insertions(+), 23 deletions(-) diff --git a/4dev/tests/CoreLibsCreateEmailTest.php b/4dev/tests/CoreLibsCreateEmailTest.php index ce4462bc..003a552b 100644 --- a/4dev/tests/CoreLibsCreateEmailTest.php +++ b/4dev/tests/CoreLibsCreateEmailTest.php @@ -41,36 +41,56 @@ final class CoreLibsCreateEmailTest extends TestCase // 0: email // 1: name // 2: encoding - // 3: expected + // 3: kv_folding + // 4: expected return [ 'all empty' => [ '', null, null, + null, '' ], 'email only' => [ 'test@test.com', null, null, + null, 'test@test.com' ], 'email and name' => [ 'test@test.com', 'Test Name', null, + null, '"Test Name" ' ], 'name in mime encoded, default UTF-8' => [ 'test@test.com', '日本語', null, + null, '"=?UTF-8?B?5pel5pys6Kqe?=" ' ], + 'name in mime encoded with half width Katakana, default UTF-8' => [ + 'test@test.com', + '日本語カタカナパ', + null, + null, + '"=?UTF-8?B?5pel5pys6Kqe7722776A7722776F776K776f?=" ' + ], + 'name in mime encoded with half width Katakana, folding on, default UTF-8' => [ + 'test@test.com', + '日本語カタカナパ', + 'UTF-8', + true, + '"=?UTF-8?B?5pel5pys6Kqe44Kr44K/44Kr44OK44OR?=" ' + ], 'name in mime encoded, UTF-8 parameter' => [ 'test@test.com', '日本語', 'UTF-8', + null, '"=?UTF-8?B?5pel5pys6Kqe?=" ' ], // does internal UTF-8 to ISO-2022-JP convert @@ -78,7 +98,22 @@ final class CoreLibsCreateEmailTest extends TestCase 'test@test.com', '日本語', 'ISO-2022-JP', - '"=?ISO-2022-JP?B?GyRCRnxLXA==?=" ' + null, + '"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=" ' + ], + 'encoding with half width Katakana in ISO-2022-JP' => [ + 'test@test.com', + '日本語カタカナパ', + 'ISO-2022-JP', + null, + '"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhCPz8/Pz8/?=" ' + ], + 'encoding with half width Katakana, folding on in ISO-2022-JP' => [ + 'test@test.com', + '日本語カタカナパ', + 'ISO-2022-JP', + true, + '"=?ISO-2022-JP?B?GyRCRnxLXDhsGyhCPz8/Pz8=?=" ' ] ]; } @@ -95,14 +130,17 @@ final class CoreLibsCreateEmailTest extends TestCase string $email, ?string $name, ?string $encoding, + ?bool $kv_folding, string $expected ): void { - if ($name === null && $encoding === null) { + if ($name === null && $encoding === null && $kv_folding === null) { $encoded_email = \CoreLibs\Create\Email::encodeEmailName($email); - } elseif ($encoding === null) { + } elseif ($encoding === null && $kv_folding === null) { $encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name); - } else { + } elseif ($kv_folding === null) { $encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name, $encoding); + } else { + $encoded_email = \CoreLibs\Create\Email::encodeEmailName($email, $name, $encoding, $kv_folding); } $this->assertEquals( $expected, @@ -119,6 +157,7 @@ final class CoreLibsCreateEmailTest extends TestCase // 4: array for to email // 5: replace content ([]/null) // 6: encoding (UTF-8/null) + // 7: kv_folding // 8: return status // 9: expected content return [ @@ -130,6 +169,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'to_email' => [], 'replace' => null, 'encoding' => null, + 'kv_folding' => null, 'expected_status' => -1, 'expected_content' => [], ], @@ -141,9 +181,22 @@ final class CoreLibsCreateEmailTest extends TestCase 'to_email' => [], 'replace' => null, 'encoding' => null, + 'kv_folding' => null, 'expected_status' => -2, 'expected_content' => [], ], + 'bad encoding, fail -3' => [ + 'subject' => 'SUBJECT', + 'body' => 'BODY', + 'from_email' => 'test@test.com', + 'from_name' => '', + 'to_email' => ['to@test.com'], + 'replace' => null, + 'encoding' => 'IDONTEXISTENCODING', + 'kv_folding' => null, + 'expected_status' => -3, + 'expected_content' => [], + ], 'sending email 1' => [ 'subject' => 'SUBJECT', 'body' => 'BODY', @@ -154,6 +207,7 @@ final class CoreLibsCreateEmailTest extends TestCase ], 'replace' => null, 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -176,6 +230,7 @@ final class CoreLibsCreateEmailTest extends TestCase ], 'replace' => null, 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -188,6 +243,52 @@ final class CoreLibsCreateEmailTest extends TestCase ] ], ], + 'sending email 1, encoded, with half width katakanata' => [ + 'subject' => 'SUBJECT 日本語カタカナパ', + 'body' => 'BODY 日本語', + 'from_email' => 'test@test.com', + 'from_name' => '', + 'to_email' => [ + 'test@test.com' + ], + 'replace' => null, + 'encoding' => 'UTF-8', + 'kv_folding' => null, + 'expected_status' => 2, + 'expected_content' => [ + [ + 'header' => [ + 'From' => 'test@test.com' + ], + 'to' => 'test@test.com', + 'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe7722776A7722776F776K776f?=', + 'body' => 'BODY 日本語', + ] + ], + ], + 'sending email 1, encoded, with half width katakanata, folding on' => [ + 'subject' => 'SUBJECT 日本語カタカナパ', + 'body' => 'BODY 日本語', + 'from_email' => 'test@test.com', + 'from_name' => '', + 'to_email' => [ + 'test@test.com' + ], + 'replace' => null, + 'encoding' => 'UTF-8', + 'kv_folding' => true, + 'expected_status' => 2, + 'expected_content' => [ + [ + 'header' => [ + 'From' => 'test@test.com' + ], + 'to' => 'test@test.com', + 'subject' => 'SUBJECT =?UTF-8?B?5pel5pys6Kqe44Kr44K/44Kr44OK44OR?=', + 'body' => 'BODY 日本語', + ] + ], + ], 'sending email 1, encoded subject ISO-2022-JP' => [ 'subject' => 'SUBJECT 日本語', 'body' => 'BODY 日本語', @@ -198,6 +299,7 @@ final class CoreLibsCreateEmailTest extends TestCase ], 'replace' => null, 'encoding' => 'ISO-2022-JP', + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -222,6 +324,7 @@ final class CoreLibsCreateEmailTest extends TestCase ], 'replace' => null, 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -255,6 +358,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -280,6 +384,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -310,6 +415,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -340,6 +446,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -379,6 +486,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -434,6 +542,7 @@ final class CoreLibsCreateEmailTest extends TestCase 'VAR' => 'bar', ], 'encoding' => null, + 'kv_folding' => null, 'expected_status' => 2, 'expected_content' => [ [ @@ -478,6 +587,7 @@ final class CoreLibsCreateEmailTest extends TestCase * @param array $to_email * @param array|null $replace * @param string|null $encoding + * @param bool|null $kv_folding * @param int $expected_status * @param array $expected_content * @return void @@ -490,6 +600,7 @@ final class CoreLibsCreateEmailTest extends TestCase array $to_email, ?array $replace, ?string $encoding, + ?bool $kv_folding, int $expected_status, array $expected_content ): void { @@ -499,6 +610,9 @@ final class CoreLibsCreateEmailTest extends TestCase if ($encoding === null) { $encoding = 'UTF-8'; } + if ($kv_folding === null) { + $kv_folding = false; + } // force new set for each run self::$log->setLogUniqueId(true); // set on of unique log id @@ -512,6 +626,7 @@ final class CoreLibsCreateEmailTest extends TestCase $to_email, $replace, $encoding, + $kv_folding, true, self::$log ); diff --git a/www/admin/class_test.create_email.php b/www/admin/class_test.create_email.php index 40f77554..ab55cd36 100644 --- a/www/admin/class_test.create_email.php +++ b/www/admin/class_test.create_email.php @@ -70,10 +70,11 @@ $status = Email::sendEmail( ], [], 'UTF-8', + false, true, $log ); -print "SENDING: " . $status . "
"; +print "SENDING A: " . $status . "
"; $status = Email::sendEmail( 'TEST {REPLACE}', 'BODY {OTHER}', @@ -97,10 +98,11 @@ $status = Email::sendEmail( 'OTHER' => '**other**' ], 'UTF-8', + false, true, $log ); -print "SENDING: " . $status . "
"; +print "SENDING B: " . $status . "
"; $status = Email::sendEmail( 'TEST', @@ -110,10 +112,29 @@ $status = Email::sendEmail( ['a@a.com', 'b@b.com'], [], 'UTF-8', + false, true, $log ); -print "SENDING: " . $status . "
"; +print "SENDING C: " . $status . "
"; + +// SUBJECT 日本語カタカナパ +$status = Email::sendEmail( + 'TEST 日本語カタカナパカタカナバ', + 'BODY 日本語カタカナパカタカナバ', + 'test@test.com', + 'Test Name 日本語カタカナパ', + [ + ['email' => 'a@a.com', 'name' => 'a 日本語カタカナパカタカナバ'], + ['email' => 'b@b.com', 'name' => 'b 日本語プブガバケブプガバケ'], + ], + [], + 'UTF-8', + false, + true, + $log +); +print "SENDING D: " . $status . "
"; // error message print $log->printErrorMsg(); diff --git a/www/lib/CoreLibs/Create/Email.php b/www/lib/CoreLibs/Create/Email.php index af173221..d27c8d1a 100644 --- a/www/lib/CoreLibs/Create/Email.php +++ b/www/lib/CoreLibs/Create/Email.php @@ -13,6 +13,20 @@ namespace CoreLibs\Create; */ class Email { + /** @var array allowed list for encodings that can do KV folding */ + private static $encoding_kv_allowed = [ + 'UTF-8', + 'EUC-JP', + 'SJIS', + 'SJIS-win', + 'ISO-2022-JP', + 'ISO-2022-JP-MS', + 'JIS', + 'JIS-ms', + ]; + /** @var string, normaly this does not need to be changed */ + private static $mb_convert_kana_mode = 'KV'; + /** * create mime encoded email part for to/from emails. * If encoding is not UTF-8 it will convert the email name to target encoding @@ -22,12 +36,14 @@ class Email * @param string $email E-Mail address * @param string $email_name Name for the email address, in UTF-8, if not set, empty * @param string $encoding Encoding, if not set UTF-8 + * @param bool $kv_fodling If set to true and a valid encoding, do KV folding * @return string Correctly encoded and build email string */ public static function encodeEmailName( string $email, string $email_name = '', - string $encoding = 'UTF-8' + string $encoding = 'UTF-8', + bool $kv_folding = false ): string { if (!empty($email_name)) { // if encoding is not UTF-8 then we convert @@ -36,11 +52,13 @@ class Email } $email_name = mb_encode_mimeheader( - mb_convert_kana( + in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ? + mb_convert_kana( + $email_name, + self::$mb_convert_kana_mode, + $encoding + ) : $email_name, - 'KV', - $encoding - ), $encoding ); return '"' . $email_name . '" ' @@ -53,17 +71,20 @@ class Email /** * Subject/Body replace sub function * - * @param string $subject Subject string, in UTF-8 - * @param string $body Body string, in UTF-8 - * @param array $replace Replace the array as key -> value, in UTF-8 - * @param string $encoding Encoding for subject encode mime header - * @return array Pos 0: Subject, Pos 1: Body + * @param string $subject Subject string, in UTF-8 + * @param string $body Body string, in UTF-8 + * @param array $replace Replace the array as key -> value, in UTF-8 + * @param string $encoding Encoding for subject encode mime header + * @param bool $kv_fodling If set to true and a valid encoding, + * do KV folding + * @return array Pos 0: Subject, Pos 1: Body */ private static function replaceContent( string $subject, string $body, array $replace, - string $encoding + string $encoding, + bool $kv_folding ): array { foreach (['subject', 'body'] as $element) { $$element = str_replace( @@ -83,7 +104,17 @@ class Email $body = mb_convert_encoding($body, $encoding, 'UTF-8'); } // we need to encodde the subject - $subject = mb_encode_mimeheader($subject, $encoding); + $subject = mb_encode_mimeheader( + in_array($encoding, self::$encoding_kv_allowed) && $kv_folding ? + // for any non UTF-8 encoding convert kana + mb_convert_kana( + $subject, + self::$mb_convert_kana_mode, + $encoding + ) : + $subject, + $encoding + ); return [$subject, $body]; } @@ -104,6 +135,8 @@ class Email * @param array $replace_content Subject/Body replace as * search -> replace, in UTF-8 * @param string $encoding E-Mail encoding, default UTF-8 + * @param bool $kv_folding If set to true and a valid encoding, + * do KV folding * @param bool $test test flag, default off * @param \CoreLibs\Debug\Logging|null $log Logging class, * only used if test flag is true @@ -112,6 +145,7 @@ class Email * 0 for send not ok * -1 for nothing set (emails, subject, body) * -2 for empty to list + * -3 encoding target not valid or not installed */ public static function sendEmail( string $subject, @@ -121,6 +155,7 @@ class Email array $send_to_emails, array $replace_content = [], string $encoding = 'UTF-8', + bool $kv_folding = false, bool $test = false, ?\CoreLibs\Debug\Logging $log = null ): int { @@ -136,6 +171,12 @@ class Email if (empty($subject) || empty($body) || empty($from_email)) { return -1; } + if ( + $encoding != 'UTF-8' && + !in_array($encoding, mb_list_encodings()) + ) { + return -3; + } // if not one valid to, abort foreach ($send_to_emails as $to_email) { // to_email can be string, then only to email @@ -147,7 +188,8 @@ class Email $_to_email = self::encodeEmailName( $to_email['email'], $to_email['name'] ?? '', - $encoding + $encoding, + $kv_folding ); $to_emails[] = $_to_email; // if we have to replacement, this override replace content @@ -183,7 +225,8 @@ class Email $subject, $body, $replace_content, - $encoding + $encoding, + $kv_folding ); } @@ -205,7 +248,8 @@ class Email $subject, $body, $_replace, - $encoding + $encoding, + $kv_folding ); } } @@ -220,6 +264,7 @@ class Email // build debug strings: convert to UTF-8 if not utf-8 $log->debug('SEND EMAIL', 'HEADERS: ' . $log->prAr($headers) . ', ' . 'ENCODING: ' . $encoding . ', ' + . 'KV FOLDING: ' . $log->prBl($kv_folding) . ', ' . 'TO: ' . $to_email . ', ' . 'SUBJECT: ' . $out_subject . ', ' . 'BODY: ' . ($encoding == 'UTF-8' ? @@ -227,6 +272,7 @@ class Email mb_convert_encoding($out_body, 'UTF-8', $encoding))); $log->debug('SEND EMAIL JSON', json_encode([ 'encoding' => $encoding, + 'kv_folding' => $kv_folding, 'header' => $headers, 'to' => $to_email, 'subject' => $out_subject,