MessageTest.php 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE
  9. * Redistributions of files must retain the above copyright notice
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 4.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Mailer;
  17. use Cake\Core\Configure;
  18. use Cake\Mailer\Message;
  19. use Cake\Mailer\Transport\DebugTransport;
  20. use Cake\TestSuite\TestCase;
  21. use Laminas\Diactoros\UploadedFile;
  22. use TestApp\Mailer\TestMessage;
  23. /**
  24. * MessageTest class
  25. */
  26. class MessageTest extends TestCase
  27. {
  28. /**
  29. * @var \Cake\Mailer\Message
  30. */
  31. protected $message;
  32. public function setUp(): void
  33. {
  34. parent::setUp();
  35. $this->message = new TestMessage();
  36. }
  37. /**
  38. * testWrap method
  39. *
  40. * @return void
  41. */
  42. public function testWrap()
  43. {
  44. $renderer = new TestMessage();
  45. $text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac turpis orci, non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.';
  46. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  47. $expected = [
  48. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac turpis orci,',
  49. 'non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.',
  50. '',
  51. ];
  52. $this->assertSame($expected, $result);
  53. $text = 'Lorem ipsum dolor sit amet, consectetur < adipiscing elit. Donec ac turpis orci, non commodo odio. Morbi nibh nisi, vehicula > pellentesque accumsan amet.';
  54. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  55. $expected = [
  56. 'Lorem ipsum dolor sit amet, consectetur < adipiscing elit. Donec ac turpis',
  57. 'orci, non commodo odio. Morbi nibh nisi, vehicula > pellentesque accumsan',
  58. 'amet.',
  59. '',
  60. ];
  61. $this->assertSame($expected, $result);
  62. $text = '<p>Lorem ipsum dolor sit amet,<br> consectetur adipiscing elit.<br> Donec ac turpis orci, non <b>commodo</b> odio. <br /> Morbi nibh nisi, vehicula pellentesque accumsan amet.<hr></p>';
  63. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  64. $expected = [
  65. '<p>Lorem ipsum dolor sit amet,<br> consectetur adipiscing elit.<br> Donec ac',
  66. 'turpis orci, non <b>commodo</b> odio. <br /> Morbi nibh nisi, vehicula',
  67. 'pellentesque accumsan amet.<hr></p>',
  68. '',
  69. ];
  70. $this->assertSame($expected, $result);
  71. $text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac <a href="http://cakephp.org">turpis</a> orci, non commodo odio. Morbi nibh nisi, vehicula pellentesque accumsan amet.';
  72. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  73. $expected = [
  74. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac',
  75. '<a href="http://cakephp.org">turpis</a> orci, non commodo odio. Morbi nibh',
  76. 'nisi, vehicula pellentesque accumsan amet.',
  77. '',
  78. ];
  79. $this->assertSame($expected, $result);
  80. $text = 'Lorem ipsum <a href="http://www.cakephp.org/controller/action/param1/param2" class="nice cool fine amazing awesome">ok</a>';
  81. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  82. $expected = [
  83. 'Lorem ipsum',
  84. '<a href="http://www.cakephp.org/controller/action/param1/param2" class="nice cool fine amazing awesome">',
  85. 'ok</a>',
  86. '',
  87. ];
  88. $this->assertSame($expected, $result);
  89. $text = 'Lorem ipsum withonewordverybigMorethanthelineshouldsizeofrfcspecificationbyieeeavailableonieeesite ok.';
  90. $result = $renderer->doWrap($text, Message::LINE_LENGTH_SHOULD);
  91. $expected = [
  92. 'Lorem ipsum',
  93. 'withonewordverybigMorethanthelineshouldsizeofrfcspecificationbyieeeavailableonieeesite',
  94. 'ok.',
  95. '',
  96. ];
  97. $this->assertSame($expected, $result);
  98. }
  99. /**
  100. * testWrapLongLine()
  101. *
  102. * @return void
  103. */
  104. public function testWrapLongLine()
  105. {
  106. $transort = new DebugTransport();
  107. $message = '<a href="http://cakephp.org">' . str_repeat('x', Message::LINE_LENGTH_MUST) . '</a>';
  108. $this->message->setFrom('cake@cakephp.org');
  109. $this->message->setTo('cake@cakephp.org');
  110. $this->message->setSubject('Wordwrap Test');
  111. $this->message->setBodyText($message);
  112. $result = $transort->send($this->message);
  113. $expected = "<a\r\n" . 'href="http://cakephp.org">' . str_repeat('x', Message::LINE_LENGTH_MUST - 26) . "\r\n" .
  114. str_repeat('x', 26) . "\r\n</a>\r\n\r\n";
  115. $this->assertEquals($expected, $result['message']);
  116. $this->assertLineLengths($result['message']);
  117. $str1 = 'a ';
  118. $str2 = ' b';
  119. $length = strlen($str1) + strlen($str2);
  120. $message = $str1 . str_repeat('x', Message::LINE_LENGTH_MUST - $length - 1) . $str2;
  121. $this->message->setBodyText($message);
  122. $result = $transort->send($this->message);
  123. $expected = "{$message}\r\n\r\n";
  124. $this->assertEquals($expected, $result['message']);
  125. $this->assertLineLengths($result['message']);
  126. $message = $str1 . str_repeat('x', Message::LINE_LENGTH_MUST - $length) . $str2;
  127. $this->message->setBodyText($message);
  128. $result = $transort->send($this->message);
  129. $expected = "{$message}\r\n\r\n";
  130. $this->assertEquals($expected, $result['message']);
  131. $this->assertLineLengths($result['message']);
  132. $message = $str1 . str_repeat('x', Message::LINE_LENGTH_MUST - $length + 1) . $str2;
  133. $this->message->setBodyText($message);
  134. $result = $transort->send($this->message);
  135. $expected = $str1 . str_repeat('x', Message::LINE_LENGTH_MUST - $length + 1) . sprintf("\r\n%s\r\n\r\n", trim($str2));
  136. $this->assertEquals($expected, $result['message']);
  137. $this->assertLineLengths($result['message']);
  138. }
  139. /**
  140. * testWrapWithTagsAcrossLines()
  141. *
  142. * @return void
  143. */
  144. public function testWrapWithTagsAcrossLines()
  145. {
  146. $str = <<<HTML
  147. <table>
  148. <th align="right" valign="top"
  149. style="font-weight: bold">The tag is across multiple lines</th>
  150. </table>
  151. HTML;
  152. $message = $str . str_repeat('x', Message::LINE_LENGTH_MUST + 1);
  153. $this->message->setFrom('cake@cakephp.org');
  154. $this->message->setTo('cake@cakephp.org');
  155. $this->message->setSubject('Wordwrap Test');
  156. $this->message->setBodyText($message);
  157. $result = (new DebugTransport())->send($this->message);
  158. $message = str_replace("\r\n", "\n", substr($message, 0, -9));
  159. $message = str_replace("\n", "\r\n", $message);
  160. $expected = "{$message}\r\nxxxxxxxxx\r\n\r\n";
  161. $this->assertEquals($expected, $result['message']);
  162. $this->assertLineLengths($result['message']);
  163. }
  164. /**
  165. * CakeEmailTest::testWrapIncludeLessThanSign()
  166. *
  167. * @return void
  168. */
  169. public function testWrapIncludeLessThanSign()
  170. {
  171. $str = 'foo<bar';
  172. $length = strlen($str);
  173. $message = $str . str_repeat('x', Message::LINE_LENGTH_MUST - $length + 1);
  174. $this->message->setFrom('cake@cakephp.org');
  175. $this->message->setTo('cake@cakephp.org');
  176. $this->message->setSubject('Wordwrap Test');
  177. $this->message->setBodyText($message);
  178. $result = (new DebugTransport())->send($this->message);
  179. $message = substr($message, 0, -1);
  180. $expected = "{$message}\r\nx\r\n\r\n";
  181. $this->assertEquals($expected, $result['message']);
  182. $this->assertLineLengths($result['message']);
  183. }
  184. /**
  185. * CakeEmailTest::testWrapForJapaneseEncoding()
  186. *
  187. * @return void
  188. */
  189. public function testWrapForJapaneseEncoding()
  190. {
  191. $this->skipIf(!function_exists('mb_convert_encoding'));
  192. $message = mb_convert_encoding('受け付けました', 'iso-2022-jp', 'UTF-8');
  193. $this->message->setFrom('cake@cakephp.org');
  194. $this->message->setTo('cake@cakephp.org');
  195. $this->message->setSubject('Wordwrap Test');
  196. $this->message->setCharset('iso-2022-jp');
  197. $this->message->setHeaderCharset('iso-2022-jp');
  198. $this->message->setBodyText($message);
  199. $result = (new DebugTransport())->send($this->message);
  200. $expected = "{$message}\r\n\r\n";
  201. $this->assertEquals($expected, $result['message']);
  202. }
  203. /**
  204. * testHeaders method
  205. *
  206. * @return void
  207. */
  208. public function testHeaders()
  209. {
  210. $this->message->setMessageId(false);
  211. $this->message->setHeaders(['X-Something' => 'nice']);
  212. $expected = [
  213. 'X-Something' => 'nice',
  214. 'Date' => date(DATE_RFC2822),
  215. 'MIME-Version' => '1.0',
  216. 'Content-Type' => 'text/plain; charset=UTF-8',
  217. 'Content-Transfer-Encoding' => '8bit',
  218. ];
  219. $this->assertSame($expected, $this->message->getHeaders());
  220. $this->message->addHeaders(['X-Something' => 'very nice', 'X-Other' => 'cool']);
  221. $expected = [
  222. 'X-Something' => 'very nice',
  223. 'X-Other' => 'cool',
  224. 'Date' => date(DATE_RFC2822),
  225. 'MIME-Version' => '1.0',
  226. 'Content-Type' => 'text/plain; charset=UTF-8',
  227. 'Content-Transfer-Encoding' => '8bit',
  228. ];
  229. $this->assertSame($expected, $this->message->getHeaders());
  230. $this->message->setFrom('cake@cakephp.org');
  231. $this->assertSame($expected, $this->message->getHeaders());
  232. $expected = [
  233. 'From' => 'cake@cakephp.org',
  234. 'X-Something' => 'very nice',
  235. 'X-Other' => 'cool',
  236. 'Date' => date(DATE_RFC2822),
  237. 'MIME-Version' => '1.0',
  238. 'Content-Type' => 'text/plain; charset=UTF-8',
  239. 'Content-Transfer-Encoding' => '8bit',
  240. ];
  241. $this->assertSame($expected, $this->message->getHeaders(['from' => true]));
  242. $this->message->setFrom('cake@cakephp.org', 'CakePHP');
  243. $expected['From'] = 'CakePHP <cake@cakephp.org>';
  244. $this->assertSame($expected, $this->message->getHeaders(['from' => true]));
  245. $this->message->setTo(['cake@cakephp.org', 'php@cakephp.org' => 'CakePHP']);
  246. $expected = [
  247. 'From' => 'CakePHP <cake@cakephp.org>',
  248. 'To' => 'cake@cakephp.org, CakePHP <php@cakephp.org>',
  249. 'X-Something' => 'very nice',
  250. 'X-Other' => 'cool',
  251. 'Date' => date(DATE_RFC2822),
  252. 'MIME-Version' => '1.0',
  253. 'Content-Type' => 'text/plain; charset=UTF-8',
  254. 'Content-Transfer-Encoding' => '8bit',
  255. ];
  256. $this->assertSame($expected, $this->message->getHeaders(['from' => true, 'to' => true]));
  257. $this->message->setCharset('ISO-2022-JP');
  258. $expected = [
  259. 'From' => 'CakePHP <cake@cakephp.org>',
  260. 'To' => 'cake@cakephp.org, CakePHP <php@cakephp.org>',
  261. 'X-Something' => 'very nice',
  262. 'X-Other' => 'cool',
  263. 'Date' => date(DATE_RFC2822),
  264. 'MIME-Version' => '1.0',
  265. 'Content-Type' => 'text/plain; charset=ISO-2022-JP',
  266. 'Content-Transfer-Encoding' => '7bit',
  267. ];
  268. $this->assertSame($expected, $this->message->getHeaders(['from' => true, 'to' => true]));
  269. $result = $this->message->setHeaders([]);
  270. $this->assertInstanceOf(Message::class, $result);
  271. $this->message->setHeaders(['o:tag' => ['foo']]);
  272. $this->message->addHeaders(['o:tag' => ['bar']]);
  273. $result = $this->message->getHeaders();
  274. $this->assertEquals(['foo', 'bar'], $result['o:tag']);
  275. }
  276. /**
  277. * testHeadersString method
  278. *
  279. * @return void
  280. */
  281. public function testHeadersString()
  282. {
  283. $this->message->setMessageId(false);
  284. $this->message->setHeaders(['X-Something' => 'nice']);
  285. $expected = [
  286. 'X-Something: nice',
  287. 'Date: ' . date(DATE_RFC2822),
  288. 'MIME-Version: 1.0',
  289. 'Content-Type: text/plain; charset=UTF-8',
  290. 'Content-Transfer-Encoding: 8bit',
  291. ];
  292. $this->assertSame(implode("\r\n", $expected), $this->message->getHeadersString());
  293. }
  294. /**
  295. * testFrom method
  296. *
  297. * @return void
  298. */
  299. public function testFrom()
  300. {
  301. $this->assertSame([], $this->message->getFrom());
  302. $this->message->setFrom('cake@cakephp.org');
  303. $expected = ['cake@cakephp.org' => 'cake@cakephp.org'];
  304. $this->assertSame($expected, $this->message->getFrom());
  305. $this->message->setFrom(['cake@cakephp.org']);
  306. $this->assertSame($expected, $this->message->getFrom());
  307. $this->message->setFrom('cake@cakephp.org', 'CakePHP');
  308. $expected = ['cake@cakephp.org' => 'CakePHP'];
  309. $this->assertSame($expected, $this->message->getFrom());
  310. $result = $this->message->setFrom(['cake@cakephp.org' => 'CakePHP']);
  311. $this->assertSame($expected, $this->message->getFrom());
  312. $this->assertSame($this->message, $result);
  313. $this->expectException(\InvalidArgumentException::class);
  314. $result = $this->message->setFrom(['cake@cakephp.org' => 'CakePHP', 'fail@cakephp.org' => 'From can only be one address']);
  315. }
  316. /**
  317. * Test that from addresses using colons work.
  318. *
  319. * @return void
  320. */
  321. public function testFromWithColonsAndQuotes()
  322. {
  323. $address = [
  324. 'info@example.com' => '70:20:00 " Forum',
  325. ];
  326. $this->message->setFrom($address);
  327. $this->assertEquals($address, $this->message->getFrom());
  328. $result = $this->message->getHeadersString(['from']);
  329. $this->assertStringContainsString('From: "70:20:00 \" Forum" <info@example.com>', $result);
  330. }
  331. /**
  332. * testSender method
  333. *
  334. * @return void
  335. */
  336. public function testSender()
  337. {
  338. $this->message->reset();
  339. $this->assertSame([], $this->message->getSender());
  340. $this->message->setSender('cake@cakephp.org', 'Name');
  341. $expected = ['cake@cakephp.org' => 'Name'];
  342. $this->assertSame($expected, $this->message->getSender());
  343. $headers = $this->message->getHeaders(['from' => true, 'sender' => true]);
  344. $this->assertFalse($headers['From']);
  345. $this->assertSame('Name <cake@cakephp.org>', $headers['Sender']);
  346. $this->message->setFrom('cake@cakephp.org', 'CakePHP');
  347. $headers = $this->message->getHeaders(['from' => true, 'sender' => true]);
  348. $this->assertSame('CakePHP <cake@cakephp.org>', $headers['From']);
  349. $this->assertSame('', $headers['Sender']);
  350. }
  351. /**
  352. * testTo method
  353. *
  354. * @return void
  355. */
  356. public function testTo()
  357. {
  358. $this->assertSame([], $this->message->getTo());
  359. $result = $this->message->setTo('cake@cakephp.org');
  360. $expected = ['cake@cakephp.org' => 'cake@cakephp.org'];
  361. $this->assertSame($expected, $this->message->getTo());
  362. $this->assertSame($this->message, $result);
  363. $this->message->setTo('cake@cakephp.org', 'CakePHP');
  364. $expected = ['cake@cakephp.org' => 'CakePHP'];
  365. $this->assertSame($expected, $this->message->getTo());
  366. $list = [
  367. 'root@localhost' => 'root',
  368. 'bjørn@hammeröath.com' => 'Bjorn',
  369. 'cake.php@cakephp.org' => 'Cake PHP',
  370. 'cake-php@googlegroups.com' => 'Cake Groups',
  371. 'root@cakephp.org',
  372. ];
  373. $this->message->setTo($list);
  374. $expected = [
  375. 'root@localhost' => 'root',
  376. 'bjørn@hammeröath.com' => 'Bjorn',
  377. 'cake.php@cakephp.org' => 'Cake PHP',
  378. 'cake-php@googlegroups.com' => 'Cake Groups',
  379. 'root@cakephp.org' => 'root@cakephp.org',
  380. ];
  381. $this->assertSame($expected, $this->message->getTo());
  382. $this->message->addTo('jrbasso@cakephp.org');
  383. $this->message->addTo('mark_story@cakephp.org', 'Mark Story');
  384. $this->message->addTo('foobar@ætdcadsl.dk');
  385. $result = $this->message->addTo(['phpnut@cakephp.org' => 'PhpNut', 'jose_zap@cakephp.org']);
  386. $expected = [
  387. 'root@localhost' => 'root',
  388. 'bjørn@hammeröath.com' => 'Bjorn',
  389. 'cake.php@cakephp.org' => 'Cake PHP',
  390. 'cake-php@googlegroups.com' => 'Cake Groups',
  391. 'root@cakephp.org' => 'root@cakephp.org',
  392. 'jrbasso@cakephp.org' => 'jrbasso@cakephp.org',
  393. 'mark_story@cakephp.org' => 'Mark Story',
  394. 'foobar@ætdcadsl.dk' => 'foobar@ætdcadsl.dk',
  395. 'phpnut@cakephp.org' => 'PhpNut',
  396. 'jose_zap@cakephp.org' => 'jose_zap@cakephp.org',
  397. ];
  398. $this->assertSame($expected, $this->message->getTo());
  399. $this->assertSame($this->message, $result);
  400. }
  401. /**
  402. * test to address with _ in domain name
  403. *
  404. * @return void
  405. */
  406. public function testToUnderscoreDomain()
  407. {
  408. $result = $this->message->setTo('cake@cake_php.org');
  409. $expected = ['cake@cake_php.org' => 'cake@cake_php.org'];
  410. $this->assertSame($expected, $this->message->getTo());
  411. $this->assertSame($this->message, $result);
  412. }
  413. /**
  414. * Data provider function for testBuildInvalidData
  415. *
  416. * @return array
  417. */
  418. public static function invalidEmails()
  419. {
  420. return [
  421. [''],
  422. ['string'],
  423. ['<tag>'],
  424. [['ok@cakephp.org', '1.0', '', 'string']],
  425. ];
  426. }
  427. /**
  428. * testBuildInvalidData
  429. *
  430. * @dataProvider invalidEmails
  431. * @return void
  432. */
  433. public function testInvalidEmail($value)
  434. {
  435. $this->expectException(\InvalidArgumentException::class);
  436. $this->message->setTo($value);
  437. }
  438. /**
  439. * testBuildInvalidData
  440. *
  441. * @dataProvider invalidEmails
  442. * @return void
  443. */
  444. public function testInvalidEmailAdd($value)
  445. {
  446. $this->expectException(\InvalidArgumentException::class);
  447. $this->message->addTo($value);
  448. }
  449. /**
  450. * test emailPattern method
  451. *
  452. * @return void
  453. */
  454. public function testEmailPattern()
  455. {
  456. $regex = '/.+@.+\..+/i';
  457. $this->assertSame($regex, $this->message->setEmailPattern($regex)->getEmailPattern());
  458. }
  459. /**
  460. * Tests that it is possible to set email regex configuration to a CakeEmail object
  461. *
  462. * @return void
  463. */
  464. public function testConfigEmailPattern()
  465. {
  466. $regex = '/.+@.+\..+/i';
  467. $email = new Message(['emailPattern' => $regex]);
  468. $this->assertSame($regex, $email->getEmailPattern());
  469. }
  470. /**
  471. * Tests that it is possible set custom email validation
  472. *
  473. * @return void
  474. */
  475. public function testCustomEmailValidation()
  476. {
  477. $regex = '/^[\.a-z0-9!#$%&\'*+\/=?^_`{|}~-]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]{2,6}$/i';
  478. $this->message->setEmailPattern($regex)->setTo('pass.@example.com');
  479. $this->assertSame([
  480. 'pass.@example.com' => 'pass.@example.com',
  481. ], $this->message->getTo());
  482. $this->message->addTo('pass..old.docomo@example.com');
  483. $this->assertSame([
  484. 'pass.@example.com' => 'pass.@example.com',
  485. 'pass..old.docomo@example.com' => 'pass..old.docomo@example.com',
  486. ], $this->message->getTo());
  487. $this->message->reset();
  488. $emails = [
  489. 'pass.@example.com',
  490. 'pass..old.docomo@example.com',
  491. ];
  492. $additionalEmails = [
  493. '.extend.@example.com',
  494. '.docomo@example.com',
  495. ];
  496. $this->message->setEmailPattern($regex)->setTo($emails);
  497. $this->assertSame([
  498. 'pass.@example.com' => 'pass.@example.com',
  499. 'pass..old.docomo@example.com' => 'pass..old.docomo@example.com',
  500. ], $this->message->getTo());
  501. $this->message->addTo($additionalEmails);
  502. $this->assertSame([
  503. 'pass.@example.com' => 'pass.@example.com',
  504. 'pass..old.docomo@example.com' => 'pass..old.docomo@example.com',
  505. '.extend.@example.com' => '.extend.@example.com',
  506. '.docomo@example.com' => '.docomo@example.com',
  507. ], $this->message->getTo());
  508. }
  509. /**
  510. * Tests that it is possible to unset the email pattern and make use of filter_var() instead.
  511. *
  512. * @return void
  513. *
  514. */
  515. public function testUnsetEmailPattern()
  516. {
  517. $this->expectException(\InvalidArgumentException::class);
  518. $this->expectExceptionMessage('Invalid email set for "to". You passed "fail.@example.com".');
  519. $email = new Message();
  520. $this->assertSame(Message::EMAIL_PATTERN, $email->getEmailPattern());
  521. $email->setEmailPattern(null);
  522. $this->assertNull($email->getEmailPattern());
  523. $email->setTo('pass@example.com');
  524. $email->setTo('fail.@example.com');
  525. }
  526. /**
  527. * Tests that passing an empty string throws an InvalidArgumentException.
  528. *
  529. * @return void
  530. *
  531. */
  532. public function testEmptyTo()
  533. {
  534. $this->expectException(\InvalidArgumentException::class);
  535. $this->expectExceptionMessage('The email set for "to" is empty.');
  536. $email = new Message();
  537. $email->setTo('');
  538. }
  539. /**
  540. * testFormatAddress method
  541. *
  542. * @return void
  543. */
  544. public function testFormatAddress()
  545. {
  546. $result = $this->message->fmtAddress(['cake@cakephp.org' => 'cake@cakephp.org']);
  547. $expected = ['cake@cakephp.org'];
  548. $this->assertSame($expected, $result);
  549. $result = $this->message->fmtAddress([
  550. 'cake@cakephp.org' => 'cake@cakephp.org',
  551. 'php@cakephp.org' => 'php@cakephp.org',
  552. ]);
  553. $expected = ['cake@cakephp.org', 'php@cakephp.org'];
  554. $this->assertSame($expected, $result);
  555. $result = $this->message->fmtAddress([
  556. 'cake@cakephp.org' => 'CakePHP',
  557. 'php@cakephp.org' => 'Cake',
  558. ]);
  559. $expected = ['CakePHP <cake@cakephp.org>', 'Cake <php@cakephp.org>'];
  560. $this->assertSame($expected, $result);
  561. $result = $this->message->fmtAddress(['me@example.com' => 'Last, First']);
  562. $expected = ['"Last, First" <me@example.com>'];
  563. $this->assertSame($expected, $result);
  564. $result = $this->message->fmtAddress(['me@example.com' => '"Last" First']);
  565. $expected = ['"\"Last\" First" <me@example.com>'];
  566. $this->assertSame($expected, $result);
  567. $result = $this->message->fmtAddress(['me@example.com' => 'Last First']);
  568. $expected = ['Last First <me@example.com>'];
  569. $this->assertSame($expected, $result);
  570. $result = $this->message->fmtAddress(['cake@cakephp.org' => 'ÄÖÜTest']);
  571. $expected = ['=?UTF-8?B?w4TDlsOcVGVzdA==?= <cake@cakephp.org>'];
  572. $this->assertSame($expected, $result);
  573. $result = $this->message->fmtAddress(['cake@cakephp.org' => '日本語Test']);
  574. $expected = ['=?UTF-8?B?5pel5pys6KqeVGVzdA==?= <cake@cakephp.org>'];
  575. $this->assertSame($expected, $result);
  576. }
  577. /**
  578. * testFormatAddressJapanese
  579. *
  580. * @return void
  581. */
  582. public function testFormatAddressJapanese()
  583. {
  584. $this->message->setHeaderCharset('ISO-2022-JP');
  585. $result = $this->message->fmtAddress(['cake@cakephp.org' => '日本語Test']);
  586. $expected = ['=?ISO-2022-JP?B?GyRCRnxLXDhsGyhCVGVzdA==?= <cake@cakephp.org>'];
  587. $this->assertSame($expected, $result);
  588. $result = $this->message->fmtAddress(['cake@cakephp.org' => '寿限無寿限無五劫の擦り切れ海砂利水魚の水行末雲来末風来末食う寝る処に住む処やぶら小路の藪柑子パイポパイポパイポのシューリンガンシューリンガンのグーリンダイグーリンダイのポンポコピーのポンポコナーの長久命の長助']);
  589. $expected = ["=?ISO-2022-JP?B?GyRCPHc4Qkw1PHc4Qkw1OF45ZSROOyQkakBaJGwzJDo9TXg/ZTV7GyhC?=\r\n" .
  590. " =?ISO-2022-JP?B?GyRCJE4/ZTlUS3YxQE1oS3ZJd01oS3Y/KSQmPzIkaz1oJEs9OyRgGyhC?=\r\n" .
  591. " =?ISO-2022-JP?B?GyRCPWgkZCRWJGk+Lk8pJE5pLjQ7O1IlUSUkJV0lUSUkJV0lUSUkGyhC?=\r\n" .
  592. " =?ISO-2022-JP?B?GyRCJV0kTiU3JWUhPCVqJXMlLCVzJTclZSE8JWolcyUsJXMkTiUwGyhC?=\r\n" .
  593. " =?ISO-2022-JP?B?GyRCITwlaiVzJUAlJCUwITwlaiVzJUAlJCROJV0lcyVdJTMlVCE8GyhC?=\r\n" .
  594. ' =?ISO-2022-JP?B?GyRCJE4lXSVzJV0lMyVKITwkTkQ5NVdMPyRORDk9dRsoQg==?= <cake@cakephp.org>'];
  595. $this->assertSame($expected, $result);
  596. }
  597. /**
  598. * testAddresses method
  599. *
  600. * @return void
  601. */
  602. public function testAddresses()
  603. {
  604. $this->message->reset();
  605. $this->message->setFrom('cake@cakephp.org', 'CakePHP');
  606. $this->message->setReplyTo('replyto@cakephp.org', 'ReplyTo CakePHP');
  607. $this->message->setReadReceipt('readreceipt@cakephp.org', 'ReadReceipt CakePHP');
  608. $this->message->setReturnPath('returnpath@cakephp.org', 'ReturnPath CakePHP');
  609. $this->message->setTo('to@cakephp.org', 'To, CakePHP');
  610. $this->message->setCc('cc@cakephp.org', 'Cc CakePHP');
  611. $this->message->setBcc('bcc@cakephp.org', 'Bcc CakePHP');
  612. $this->message->addTo('to2@cakephp.org', 'To2 CakePHP');
  613. $this->message->addCc('cc2@cakephp.org', 'Cc2 CakePHP');
  614. $this->message->addBcc('bcc2@cakephp.org', 'Bcc2 CakePHP');
  615. $this->assertSame($this->message->getFrom(), ['cake@cakephp.org' => 'CakePHP']);
  616. $this->assertSame($this->message->getReplyTo(), ['replyto@cakephp.org' => 'ReplyTo CakePHP']);
  617. $this->assertSame($this->message->getReadReceipt(), ['readreceipt@cakephp.org' => 'ReadReceipt CakePHP']);
  618. $this->assertSame($this->message->getReturnPath(), ['returnpath@cakephp.org' => 'ReturnPath CakePHP']);
  619. $this->assertSame($this->message->getTo(), ['to@cakephp.org' => 'To, CakePHP', 'to2@cakephp.org' => 'To2 CakePHP']);
  620. $this->assertSame($this->message->getCc(), ['cc@cakephp.org' => 'Cc CakePHP', 'cc2@cakephp.org' => 'Cc2 CakePHP']);
  621. $this->assertSame($this->message->getBcc(), ['bcc@cakephp.org' => 'Bcc CakePHP', 'bcc2@cakephp.org' => 'Bcc2 CakePHP']);
  622. $headers = $this->message->getHeaders(array_fill_keys(['from', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc'], true));
  623. $this->assertSame($headers['From'], 'CakePHP <cake@cakephp.org>');
  624. $this->assertSame($headers['Reply-To'], 'ReplyTo CakePHP <replyto@cakephp.org>');
  625. $this->assertSame($headers['Disposition-Notification-To'], 'ReadReceipt CakePHP <readreceipt@cakephp.org>');
  626. $this->assertSame($headers['Return-Path'], 'ReturnPath CakePHP <returnpath@cakephp.org>');
  627. $this->assertSame($headers['To'], '"To, CakePHP" <to@cakephp.org>, To2 CakePHP <to2@cakephp.org>');
  628. $this->assertSame($headers['Cc'], 'Cc CakePHP <cc@cakephp.org>, Cc2 CakePHP <cc2@cakephp.org>');
  629. $this->assertSame($headers['Bcc'], 'Bcc CakePHP <bcc@cakephp.org>, Bcc2 CakePHP <bcc2@cakephp.org>');
  630. }
  631. /**
  632. * test reset addresses method
  633. *
  634. * @return void
  635. */
  636. public function testResetAddresses()
  637. {
  638. $this->message->reset();
  639. $this->message
  640. ->setFrom('cake@cakephp.org', 'CakePHP')
  641. ->setReplyTo('replyto@cakephp.org', 'ReplyTo CakePHP')
  642. ->setReadReceipt('readreceipt@cakephp.org', 'ReadReceipt CakePHP')
  643. ->setReturnPath('returnpath@cakephp.org', 'ReturnPath CakePHP')
  644. ->setTo('to@cakephp.org', 'To, CakePHP')
  645. ->setCc('cc@cakephp.org', 'Cc CakePHP')
  646. ->setBcc('bcc@cakephp.org', 'Bcc CakePHP');
  647. $this->assertNotEmpty($this->message->getFrom());
  648. $this->assertNotEmpty($this->message->getReplyTo());
  649. $this->assertNotEmpty($this->message->getReadReceipt());
  650. $this->assertNotEmpty($this->message->getReturnPath());
  651. $this->assertNotEmpty($this->message->getTo());
  652. $this->assertNotEmpty($this->message->getCc());
  653. $this->assertNotEmpty($this->message->getBcc());
  654. $this->message
  655. ->setFrom([])
  656. ->setReplyTo([])
  657. ->setReadReceipt([])
  658. ->setReturnPath([])
  659. ->setTo([])
  660. ->setCc([])
  661. ->setBcc([]);
  662. $this->assertEmpty($this->message->getFrom());
  663. $this->assertEmpty($this->message->getReplyTo());
  664. $this->assertEmpty($this->message->getReadReceipt());
  665. $this->assertEmpty($this->message->getReturnPath());
  666. $this->assertEmpty($this->message->getTo());
  667. $this->assertEmpty($this->message->getCc());
  668. $this->assertEmpty($this->message->getBcc());
  669. }
  670. /**
  671. * testMessageId method
  672. *
  673. * @return void
  674. */
  675. public function testMessageId()
  676. {
  677. $this->message->setMessageId(true);
  678. $result = $this->message->getHeaders();
  679. $this->assertArrayHasKey('Message-ID', $result);
  680. $this->message->setMessageId(false);
  681. $result = $this->message->getHeaders();
  682. $this->assertArrayNotHasKey('Message-ID', $result);
  683. $result = $this->message->setMessageId('<my-email@localhost>');
  684. $this->assertSame($this->message, $result);
  685. $result = $this->message->getHeaders();
  686. $this->assertSame('<my-email@localhost>', $result['Message-ID']);
  687. $result = $this->message->getMessageId();
  688. $this->assertSame('<my-email@localhost>', $result);
  689. }
  690. public function testAutoMessageIdIsIdempotent()
  691. {
  692. $headers = $this->message->getHeaders();
  693. $this->assertArrayHasKey('Message-ID', $headers);
  694. $regeneratedHeaders = $this->message->getHeaders();
  695. $this->assertSame($headers['Message-ID'], $regeneratedHeaders['Message-ID']);
  696. }
  697. /**
  698. * @return void
  699. */
  700. public function testPriority()
  701. {
  702. $this->message->setPriority(4);
  703. $this->assertSame(4, $this->message->getPriority());
  704. $result = $this->message->getHeaders();
  705. $this->assertArrayHasKey('X-Priority', $result);
  706. }
  707. /**
  708. * testMessageIdInvalid method
  709. *
  710. * @return void
  711. */
  712. public function testMessageIdInvalid()
  713. {
  714. $this->expectException(\InvalidArgumentException::class);
  715. $this->message->setMessageId('my-email@localhost');
  716. }
  717. /**
  718. * testDomain method
  719. *
  720. * @return void
  721. */
  722. public function testDomain()
  723. {
  724. $result = $this->message->getDomain();
  725. $expected = env('HTTP_HOST') ? env('HTTP_HOST') : php_uname('n');
  726. $this->assertSame($expected, $result);
  727. $this->message->setDomain('example.org');
  728. $result = $this->message->getDomain();
  729. $expected = 'example.org';
  730. $this->assertSame($expected, $result);
  731. }
  732. /**
  733. * testMessageIdWithDomain method
  734. *
  735. * @return void
  736. */
  737. public function testMessageIdWithDomain()
  738. {
  739. $this->message->setDomain('example.org');
  740. $result = $this->message->getHeaders();
  741. $expected = '@example.org>';
  742. $this->assertTextContains($expected, $result['Message-ID']);
  743. $_SERVER['HTTP_HOST'] = 'example.org';
  744. $result = $this->message->getHeaders();
  745. $this->assertTextContains('example.org', $result['Message-ID']);
  746. $_SERVER['HTTP_HOST'] = 'example.org:81';
  747. $result = $this->message->getHeaders();
  748. $this->assertTextNotContains(':81', $result['Message-ID']);
  749. }
  750. /**
  751. * testSubject method
  752. *
  753. * @return void
  754. */
  755. public function testSubject()
  756. {
  757. $this->message->setSubject('You have a new message.');
  758. $this->assertSame('You have a new message.', $this->message->getSubject());
  759. $this->message->setSubject('You have a new message, I think.');
  760. $this->assertSame($this->message->getSubject(), 'You have a new message, I think.');
  761. $this->message->setSubject('1');
  762. $this->assertSame('1', $this->message->getSubject());
  763. $input = 'هذه رسالة بعنوان طويل مرسل للمستلم';
  764. $this->message->setSubject($input);
  765. $expected = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?=';
  766. $this->assertSame($expected, $this->message->getSubject());
  767. $this->assertSame($input, $this->message->getOriginalSubject());
  768. }
  769. /**
  770. * testSubjectJapanese
  771. *
  772. * @return void
  773. */
  774. public function testSubjectJapanese()
  775. {
  776. mb_internal_encoding('UTF-8');
  777. $this->message->setHeaderCharset('ISO-2022-JP');
  778. $this->message->setSubject('日本語のSubjectにも対応するよ');
  779. $expected = '=?ISO-2022-JP?B?GyRCRnxLXDhsJE4bKEJTdWJqZWN0GyRCJEskYkJQMX4kOSRrJGgbKEI=?=';
  780. $this->assertSame($expected, $this->message->getSubject());
  781. $this->message->setSubject('長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?');
  782. $expected = "=?ISO-2022-JP?B?GyRCRDkkJEQ5JCREOSQkGyhCU3ViamVjdBskQiROPmw5ZyRPGyhCZm9s?=\r\n" .
  783. " =?ISO-2022-JP?B?ZGluZxskQiQ5JGskTiQsQDUkNyQkJHMkQCQxJEkkJCRDJD8kJCRJGyhC?=\r\n" .
  784. ' =?ISO-2022-JP?B?GyRCJCYkSiRrJHMkQCRtJCYhKRsoQg==?=';
  785. $this->assertSame($expected, $this->message->getSubject());
  786. }
  787. /**
  788. * testAttachments method
  789. *
  790. * @return void
  791. */
  792. public function testSetAttachments()
  793. {
  794. $uploadedFile = new UploadedFile(
  795. __FILE__,
  796. filesize(__FILE__),
  797. UPLOAD_ERR_OK,
  798. 'MessageTest.php',
  799. 'text/x-php'
  800. );
  801. $this->message->setAttachments([
  802. CAKE . 'basics.php',
  803. $uploadedFile,
  804. ]);
  805. $expected = [
  806. 'basics.php' => [
  807. 'file' => CAKE . 'basics.php',
  808. 'mimetype' => 'text/x-php',
  809. ],
  810. 'MessageTest.php' => [
  811. 'file' => $uploadedFile,
  812. 'mimetype' => 'text/x-php',
  813. ],
  814. ];
  815. $this->assertSame($expected, $this->message->getAttachments());
  816. $this->message->setAttachments([]);
  817. $this->assertSame([], $this->message->getAttachments());
  818. $this->message->setAttachments([
  819. ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain'],
  820. ]);
  821. $this->message->addAttachments([CORE_PATH . 'config' . DS . 'bootstrap.php']);
  822. $this->message->addAttachments([CORE_PATH . 'config' . DS . 'bootstrap.php']);
  823. $this->message->addAttachments([
  824. 'other.txt' => CORE_PATH . 'config' . DS . 'bootstrap.php',
  825. 'license' => CORE_PATH . 'LICENSE',
  826. ]);
  827. $expected = [
  828. 'basics.php' => ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain'],
  829. 'bootstrap.php' => ['file' => CORE_PATH . 'config' . DS . 'bootstrap.php', 'mimetype' => 'text/x-php'],
  830. 'other.txt' => ['file' => CORE_PATH . 'config' . DS . 'bootstrap.php', 'mimetype' => 'text/x-php'],
  831. 'license' => ['file' => CORE_PATH . 'LICENSE', 'mimetype' => 'text/plain'],
  832. ];
  833. $this->assertSame($expected, $this->message->getAttachments());
  834. $this->expectException(\InvalidArgumentException::class);
  835. $this->message->setAttachments([['nofile' => CAKE . 'basics.php', 'mimetype' => 'text/plain']]);
  836. }
  837. /**
  838. * Test send() with no template and data string attachment and no mimetype
  839. *
  840. * @return void
  841. */
  842. public function testSetAttachmentDataNoMimetype()
  843. {
  844. $this->message->setAttachments(['cake.icon.gif' => [
  845. 'data' => 'test',
  846. ]]);
  847. $result = $this->message->getAttachments();
  848. $expected = [
  849. 'cake.icon.gif' => [
  850. 'data' => base64_encode('test') . "\r\n",
  851. 'mimetype' => 'application/octet-stream',
  852. ],
  853. ];
  854. $this->assertSame($expected, $this->message->getAttachments());
  855. }
  856. public function testSetAttachmentInvalidFile()
  857. {
  858. $this->expectException(\InvalidArgumentException::class);
  859. $this->expectExceptionMessage(
  860. 'File must be a filepath or UploadedFileInterface instance. Found `boolean` instead.'
  861. );
  862. $this->message->setAttachments(['cake.icon.gif' => [
  863. 'file' => true,
  864. ]]);
  865. }
  866. /**
  867. * testReset method
  868. *
  869. * @return void
  870. */
  871. public function testReset()
  872. {
  873. $this->message->setTo('cake@cakephp.org');
  874. $this->message->setEmailPattern('/.+@.+\..+/i');
  875. $this->assertSame(['cake@cakephp.org' => 'cake@cakephp.org'], $this->message->getTo());
  876. $this->message->reset();
  877. $this->assertSame([], $this->message->getTo());
  878. $this->assertSame(Message::EMAIL_PATTERN, $this->message->getEmailPattern());
  879. }
  880. /**
  881. * testReset with charset
  882. *
  883. * @return void
  884. */
  885. public function testResetWithCharset()
  886. {
  887. $this->message->setCharset('ISO-2022-JP');
  888. $this->message->reset();
  889. $this->assertSame('utf-8', $this->message->getCharset());
  890. $this->assertSame('utf-8', $this->message->getHeaderCharset());
  891. }
  892. /**
  893. * testEmailFormat method
  894. *
  895. * @return void
  896. */
  897. public function testEmailFormat()
  898. {
  899. $result = $this->message->getEmailFormat();
  900. $this->assertSame('text', $result);
  901. $result = $this->message->setEmailFormat('html');
  902. $this->assertInstanceOf(Message::class, $result);
  903. $result = $this->message->getEmailFormat();
  904. $this->assertSame('html', $result);
  905. $this->expectException(\InvalidArgumentException::class);
  906. $this->message->setEmailFormat('invalid');
  907. }
  908. /**
  909. * Tests that it is possible to add charset configuration to a CakeEmail object
  910. *
  911. * @return void
  912. */
  913. public function testConfigCharset()
  914. {
  915. $email = new Message();
  916. $this->assertEquals(Configure::read('App.encoding'), $email->getCharset());
  917. $this->assertEquals(Configure::read('App.encoding'), $email->getHeaderCharset());
  918. $email = new Message(['charset' => 'iso-2022-jp', 'headerCharset' => 'iso-2022-jp-ms']);
  919. $this->assertSame('iso-2022-jp', $email->getCharset());
  920. $this->assertSame('iso-2022-jp-ms', $email->getHeaderCharset());
  921. $email = new Message(['charset' => 'iso-2022-jp']);
  922. $this->assertSame('iso-2022-jp', $email->getCharset());
  923. $this->assertSame('iso-2022-jp', $email->getHeaderCharset());
  924. $email = new Message(['headerCharset' => 'iso-2022-jp-ms']);
  925. $this->assertEquals(Configure::read('App.encoding'), $email->getCharset());
  926. $this->assertSame('iso-2022-jp-ms', $email->getHeaderCharset());
  927. }
  928. public function testGetBody()
  929. {
  930. $message = new Message();
  931. $uploadedFile = new UploadedFile(
  932. __FILE__,
  933. filesize(__FILE__),
  934. UPLOAD_ERR_OK,
  935. 'MessageTest.php',
  936. 'text/x-php'
  937. );
  938. $chunks = base64_encode(file_get_contents(__FILE__));
  939. $result = $message->setAttachments([$uploadedFile])
  940. ->setBodyText('Attached an uploaded file')
  941. ->getBody();
  942. $result = implode("\r\n", $result);
  943. $this->assertStringContainsString($chunks[0], $result);
  944. }
  945. /**
  946. * Tests that the body is encoded using the configured charset (Japanese standard encoding)
  947. *
  948. * @return void
  949. */
  950. public function testBodyEncodingIso2022Jp()
  951. {
  952. $message = new Message([
  953. 'charset' => 'iso-2022-jp',
  954. 'headerCharset' => 'iso-2022-jp',
  955. 'transport' => 'debug',
  956. ]);
  957. $message->setSubject('あれ?もしかしての前と');
  958. $headers = $message->getHeaders(['subject']);
  959. $expected = '?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?=';
  960. $this->assertStringContainsString($expected, $headers['Subject']);
  961. $message->setBodyHtml('①㈱ ってテーブルを作ってやってたらう');
  962. $result = $message->getHeadersString();
  963. $this->assertTextContains('Content-Type: text/plain; charset=ISO-2022-JP', $result);
  964. $this->assertTextNotContains('Content-Type: text/plain; charset=ISO-2022-JP-MS', $result); // not charset=iso-2022-jp-ms
  965. $result = implode('', $message->getBody());
  966. $this->assertTextNotContains(mb_convert_encoding('①㈱ ってテーブルを作ってやってたらう', 'ISO-2022-JP-MS'), $result);
  967. }
  968. /**
  969. * Tests that the body is encoded using the configured charset (Japanese irregular encoding, but sometime use this)
  970. *
  971. * @return void
  972. */
  973. public function testBodyEncodingIso2022JpMs()
  974. {
  975. $message = new Message([
  976. 'charset' => 'iso-2022-jp-ms',
  977. 'headerCharset' => 'iso-2022-jp-ms',
  978. 'transport' => 'debug',
  979. ]);
  980. $message->setSubject('あれ?もしかしての前と');
  981. $headers = $message->getHeaders(['subject']);
  982. $expected = '?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?=';
  983. $this->assertStringContainsString($expected, $headers['Subject']);
  984. $result = $message->setBodyText('①㈱ ってテーブルを作ってやってたらう');
  985. $result = $message->getHeadersString();
  986. $this->assertTextContains('Content-Type: text/plain; charset=ISO-2022-JP', $result);
  987. $this->assertTextNotContains('Content-Type: text/plain; charset=iso-2022-jp-ms', $result); // not charset=iso-2022-jp-ms
  988. $result = implode('', $message->getBody());
  989. $this->assertStringContainsString(mb_convert_encoding('①㈱ ってテーブルを作ってやってたらう', 'ISO-2022-JP-MS'), $result);
  990. }
  991. /**
  992. * Tests that the body is encoded using the configured charset
  993. *
  994. * @return void
  995. */
  996. public function testEncodingMixed()
  997. {
  998. $message = new Message([
  999. 'headerCharset' => 'iso-2022-jp-ms',
  1000. 'charset' => 'iso-2022-jp',
  1001. ]);
  1002. $message->setBodyText('ってテーブルを作ってやってたらう');
  1003. $result = $message->getHeadersString();
  1004. $this->assertStringContainsString('Content-Type: text/plain; charset=ISO-2022-JP', $result);
  1005. $result = implode('', $message->getBody());
  1006. $this->assertStringContainsString(mb_convert_encoding('ってテーブルを作ってやってたらう', 'ISO-2022-JP'), $result);
  1007. }
  1008. /**
  1009. * Test CakeMessage::_encode function
  1010. *
  1011. * @return void
  1012. */
  1013. public function testEncode()
  1014. {
  1015. $this->message->setHeaderCharset('ISO-2022-JP');
  1016. $result = $this->message->encode('日本語');
  1017. $expected = '=?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=';
  1018. $this->assertSame($expected, $result);
  1019. $this->message->setHeaderCharset('ISO-2022-JP');
  1020. $result = $this->message->encode('長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?');
  1021. $expected = "=?ISO-2022-JP?B?GyRCRDkkJEQ5JCREOSQkGyhCU3ViamVjdBskQiROPmw5ZyRPGyhCZm9s?=\r\n" .
  1022. " =?ISO-2022-JP?B?ZGluZxskQiQ5JGskTiQsQDUkNyQkJHMkQCQxJEkkJCRDJD8kJCRJGyhC?=\r\n" .
  1023. ' =?ISO-2022-JP?B?GyRCJCYkSiRrJHMkQCRtJCYhKRsoQg==?=';
  1024. $this->assertSame($expected, $result);
  1025. }
  1026. /**
  1027. * Test CakeMessage::_decode function
  1028. *
  1029. * @return void
  1030. */
  1031. public function testDecode()
  1032. {
  1033. $this->message->setHeaderCharset('ISO-2022-JP');
  1034. $result = $this->message->decode('=?ISO-2022-JP?B?GyRCRnxLXDhsGyhC?=');
  1035. $expected = '日本語';
  1036. $this->assertSame($expected, $result);
  1037. $this->message->setHeaderCharset('ISO-2022-JP');
  1038. $result = $this->message->decode("=?ISO-2022-JP?B?GyRCRDkkJEQ5JCREOSQkGyhCU3ViamVjdBskQiROPmw5ZyRPGyhCZm9s?=\r\n" .
  1039. " =?ISO-2022-JP?B?ZGluZxskQiQ5JGskTiQsQDUkNyQkJHMkQCQxJEkkJCRDJD8kJCRJGyhC?=\r\n" .
  1040. ' =?ISO-2022-JP?B?GyRCJCYkSiRrJHMkQCRtJCYhKRsoQg==?=');
  1041. $expected = '長い長い長いSubjectの場合はfoldingするのが正しいんだけどいったいどうなるんだろう?';
  1042. $this->assertSame($expected, $result);
  1043. }
  1044. /**
  1045. * Tests charset setter/getter
  1046. *
  1047. * @return void
  1048. */
  1049. public function testCharset()
  1050. {
  1051. $this->message->setCharset('UTF-8');
  1052. $this->assertSame($this->message->getCharset(), 'UTF-8');
  1053. $this->message->setCharset('ISO-2022-JP');
  1054. $this->assertSame($this->message->getCharset(), 'ISO-2022-JP');
  1055. $charset = $this->message->setCharset('Shift_JIS');
  1056. $this->assertSame('Shift_JIS', $charset->getCharset());
  1057. }
  1058. /**
  1059. * Tests headerCharset setter/getter
  1060. *
  1061. * @return void
  1062. */
  1063. public function testHeaderCharset()
  1064. {
  1065. $this->message->setHeaderCharset('UTF-8');
  1066. $this->assertSame($this->message->getHeaderCharset(), 'UTF-8');
  1067. $this->message->setHeaderCharset('ISO-2022-JP');
  1068. $this->assertSame($this->message->getHeaderCharset(), 'ISO-2022-JP');
  1069. $charset = $this->message->setHeaderCharset('Shift_JIS');
  1070. $this->assertSame('Shift_JIS', $charset->getHeaderCharset());
  1071. }
  1072. /**
  1073. * Test transferEncoding
  1074. *
  1075. * @return void
  1076. */
  1077. public function testTransferEncoding()
  1078. {
  1079. // Test new transfer encoding
  1080. $expected = 'quoted-printable';
  1081. $this->message->setTransferEncoding($expected);
  1082. $this->assertSame($expected, $this->message->getTransferEncoding());
  1083. $this->assertSame($expected, $this->message->getContentTransferEncoding());
  1084. // Test default charset/encoding : utf8/8bit
  1085. $expected = '8bit';
  1086. $this->message->reset();
  1087. $this->assertNull($this->message->getTransferEncoding());
  1088. $this->assertSame($expected, $this->message->getContentTransferEncoding());
  1089. //Test wrong encoding
  1090. $this->expectException(\InvalidArgumentException::class);
  1091. $this->message->setTransferEncoding('invalid');
  1092. }
  1093. /**
  1094. * Tests for compatible check.
  1095. * charset property and charset() method.
  1096. * headerCharset property and headerCharset() method.
  1097. *
  1098. * @return void
  1099. */
  1100. public function testCharsetsCompatible()
  1101. {
  1102. $checkHeaders = [
  1103. 'from' => true,
  1104. 'to' => true,
  1105. 'cc' => true,
  1106. 'subject' => true,
  1107. ];
  1108. // Header Charset : null (used by default UTF-8)
  1109. // Body Charset : ISO-2022-JP
  1110. $oldStyleEmail = $this->_getEmailByOldStyleCharset('iso-2022-jp', null);
  1111. $oldStyleHeaders = $oldStyleEmail->getHeaders($checkHeaders);
  1112. $newStyleEmail = $this->_getEmailByNewStyleCharset('iso-2022-jp', null);
  1113. $newStyleHeaders = $newStyleEmail->getHeaders($checkHeaders);
  1114. $this->assertSame($oldStyleHeaders['From'], $newStyleHeaders['From']);
  1115. $this->assertSame($oldStyleHeaders['To'], $newStyleHeaders['To']);
  1116. $this->assertSame($oldStyleHeaders['Cc'], $newStyleHeaders['Cc']);
  1117. $this->assertSame($oldStyleHeaders['Subject'], $newStyleHeaders['Subject']);
  1118. // Header Charset : UTF-8
  1119. // Boby Charset : ISO-2022-JP
  1120. $oldStyleEmail = $this->_getEmailByOldStyleCharset('iso-2022-jp', 'utf-8');
  1121. $oldStyleHeaders = $oldStyleEmail->getHeaders($checkHeaders);
  1122. $newStyleEmail = $this->_getEmailByNewStyleCharset('iso-2022-jp', 'utf-8');
  1123. $newStyleHeaders = $newStyleEmail->getHeaders($checkHeaders);
  1124. $this->assertSame($oldStyleHeaders['From'], $newStyleHeaders['From']);
  1125. $this->assertSame($oldStyleHeaders['To'], $newStyleHeaders['To']);
  1126. $this->assertSame($oldStyleHeaders['Cc'], $newStyleHeaders['Cc']);
  1127. $this->assertSame($oldStyleHeaders['Subject'], $newStyleHeaders['Subject']);
  1128. // Header Charset : ISO-2022-JP
  1129. // Boby Charset : UTF-8
  1130. $oldStyleEmail = $this->_getEmailByOldStyleCharset('utf-8', 'iso-2022-jp');
  1131. $oldStyleHeaders = $oldStyleEmail->getHeaders($checkHeaders);
  1132. $newStyleEmail = $this->_getEmailByNewStyleCharset('utf-8', 'iso-2022-jp');
  1133. $newStyleHeaders = $newStyleEmail->getHeaders($checkHeaders);
  1134. $this->assertSame($oldStyleHeaders['From'], $newStyleHeaders['From']);
  1135. $this->assertSame($oldStyleHeaders['To'], $newStyleHeaders['To']);
  1136. $this->assertSame($oldStyleHeaders['Cc'], $newStyleHeaders['Cc']);
  1137. $this->assertSame($oldStyleHeaders['Subject'], $newStyleHeaders['Subject']);
  1138. }
  1139. /**
  1140. * @param mixed $charset
  1141. * @param mixed $headerCharset
  1142. * @return \Cake\Mailer\Message
  1143. */
  1144. protected function _getEmailByOldStyleCharset($charset, $headerCharset)
  1145. {
  1146. $message = new Message(['transport' => 'debug']);
  1147. if (!empty($charset)) {
  1148. $message->setCharset($charset);
  1149. }
  1150. if (!empty($headerCharset)) {
  1151. $message->setHeaderCharset($headerCharset);
  1152. }
  1153. $message->setFrom('someone@example.com', 'どこかの誰か');
  1154. $message->setTo('someperson@example.jp', 'どこかのどなたか');
  1155. $message->setCc('miku@example.net', 'ミク');
  1156. $message->setSubject('テストメール');
  1157. $message->setBodyText('テストメールの本文');
  1158. return $message;
  1159. }
  1160. /**
  1161. * @param mixed $charset
  1162. * @param mixed $headerCharset
  1163. * @return \Cake\Mailer\Message
  1164. */
  1165. protected function _getEmailByNewStyleCharset($charset, $headerCharset)
  1166. {
  1167. $message = new Message();
  1168. if (!empty($charset)) {
  1169. $message->setCharset($charset);
  1170. }
  1171. if (!empty($headerCharset)) {
  1172. $message->setHeaderCharset($headerCharset);
  1173. }
  1174. $message->setFrom('someone@example.com', 'どこかの誰か');
  1175. $message->setTo('someperson@example.jp', 'どこかのどなたか');
  1176. $message->setCc('miku@example.net', 'ミク');
  1177. $message->setSubject('テストメール');
  1178. $message->setBodyText('テストメールの本文');
  1179. return $message;
  1180. }
  1181. /**
  1182. * @param string $message
  1183. * @return void
  1184. */
  1185. protected function assertLineLengths($message)
  1186. {
  1187. $lines = explode("\r\n", $message);
  1188. foreach ($lines as $line) {
  1189. $this->assertTrue(
  1190. strlen($line) <= Message::LINE_LENGTH_MUST,
  1191. 'Line length exceeds the max. limit of Message::LINE_LENGTH_MUST'
  1192. );
  1193. }
  1194. }
  1195. }