PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/nette/mail/src/Mail/Message.php

https://gitlab.com/ramos.lauty/softlord
PHP | 432 lines | 229 code | 70 blank | 133 comment | 15 complexity | 46106a1218af6ea91b1723c66db02a7a MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Nette Framework (https://nette.org)
  4. * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  5. */
  6. namespace Nette\Mail;
  7. use Nette;
  8. use Nette\Utils\Strings;
  9. /**
  10. * Mail provides functionality to compose and send both text and MIME-compliant multipart email messages.
  11. *
  12. * @property string $subject
  13. * @property mixed $htmlBody
  14. */
  15. class Message extends MimePart
  16. {
  17. /** Priority */
  18. const HIGH = 1,
  19. NORMAL = 3,
  20. LOW = 5;
  21. /** @var array */
  22. public static $defaultHeaders = [
  23. 'MIME-Version' => '1.0',
  24. 'X-Mailer' => 'Nette Framework',
  25. ];
  26. /** @var array */
  27. private $attachments = [];
  28. /** @var array */
  29. private $inlines = [];
  30. /** @var mixed */
  31. private $html;
  32. public function __construct()
  33. {
  34. foreach (static::$defaultHeaders as $name => $value) {
  35. $this->setHeader($name, $value);
  36. }
  37. $this->setHeader('Date', date('r'));
  38. }
  39. /**
  40. * Sets the sender of the message.
  41. * @param string email or format "John Doe" <doe@example.com>
  42. * @param string
  43. * @return self
  44. */
  45. public function setFrom($email, $name = NULL)
  46. {
  47. $this->setHeader('From', $this->formatEmail($email, $name));
  48. return $this;
  49. }
  50. /**
  51. * Returns the sender of the message.
  52. * @return array
  53. */
  54. public function getFrom()
  55. {
  56. return $this->getHeader('From');
  57. }
  58. /**
  59. * Adds the reply-to address.
  60. * @param string email or format "John Doe" <doe@example.com>
  61. * @param string
  62. * @return self
  63. */
  64. public function addReplyTo($email, $name = NULL)
  65. {
  66. $this->setHeader('Reply-To', $this->formatEmail($email, $name), TRUE);
  67. return $this;
  68. }
  69. /**
  70. * Sets the subject of the message.
  71. * @param string
  72. * @return self
  73. */
  74. public function setSubject($subject)
  75. {
  76. $this->setHeader('Subject', $subject);
  77. return $this;
  78. }
  79. /**
  80. * Returns the subject of the message.
  81. * @return string
  82. */
  83. public function getSubject()
  84. {
  85. return $this->getHeader('Subject');
  86. }
  87. /**
  88. * Adds email recipient.
  89. * @param string email or format "John Doe" <doe@example.com>
  90. * @param string
  91. * @return self
  92. */
  93. public function addTo($email, $name = NULL) // addRecipient()
  94. {
  95. $this->setHeader('To', $this->formatEmail($email, $name), TRUE);
  96. return $this;
  97. }
  98. /**
  99. * Adds carbon copy email recipient.
  100. * @param string email or format "John Doe" <doe@example.com>
  101. * @param string
  102. * @return self
  103. */
  104. public function addCc($email, $name = NULL)
  105. {
  106. $this->setHeader('Cc', $this->formatEmail($email, $name), TRUE);
  107. return $this;
  108. }
  109. /**
  110. * Adds blind carbon copy email recipient.
  111. * @param string email or format "John Doe" <doe@example.com>
  112. * @param string
  113. * @return self
  114. */
  115. public function addBcc($email, $name = NULL)
  116. {
  117. $this->setHeader('Bcc', $this->formatEmail($email, $name), TRUE);
  118. return $this;
  119. }
  120. /**
  121. * Formats recipient email.
  122. * @param string
  123. * @param string
  124. * @return array
  125. */
  126. private function formatEmail($email, $name)
  127. {
  128. if (!$name && preg_match('#^(.+) +<(.*)>\z#', $email, $matches)) {
  129. return [$matches[2] => $matches[1]];
  130. } else {
  131. return [$email => $name];
  132. }
  133. }
  134. /**
  135. * Sets the Return-Path header of the message.
  136. * @param string email
  137. * @return self
  138. */
  139. public function setReturnPath($email)
  140. {
  141. $this->setHeader('Return-Path', $email);
  142. return $this;
  143. }
  144. /**
  145. * Returns the Return-Path header.
  146. * @return string
  147. */
  148. public function getReturnPath()
  149. {
  150. return $this->getHeader('Return-Path');
  151. }
  152. /**
  153. * Sets email priority.
  154. * @param int
  155. * @return self
  156. */
  157. public function setPriority($priority)
  158. {
  159. $this->setHeader('X-Priority', (int) $priority);
  160. return $this;
  161. }
  162. /**
  163. * Returns email priority.
  164. * @return int
  165. */
  166. public function getPriority()
  167. {
  168. return $this->getHeader('X-Priority');
  169. }
  170. /**
  171. * Sets HTML body.
  172. * @param string
  173. * @param mixed base-path
  174. * @return self
  175. */
  176. public function setHtmlBody($html, $basePath = NULL)
  177. {
  178. $html = (string) $html;
  179. if ($basePath) {
  180. $cids = [];
  181. $matches = Strings::matchAll(
  182. $html,
  183. '#
  184. (<img[^<>]*\s src\s*=\s*
  185. |<body[^<>]*\s background\s*=\s*
  186. |<[^<>]+\s style\s*=\s* ["\'][^"\'>]+[:\s] url\(
  187. |<style[^>]*>[^<]+ [:\s] url\()
  188. (["\']?)(?![a-z]+:|[/\\#])([^"\'>)\s]+)
  189. |\[\[ ([\w()+./@~-]+) \]\]
  190. #ix',
  191. PREG_OFFSET_CAPTURE
  192. );
  193. foreach (array_reverse($matches) as $m) {
  194. $file = rtrim($basePath, '/\\') . '/' . (isset($m[4]) ? $m[4][0] : urldecode($m[3][0]));
  195. if (!isset($cids[$file])) {
  196. $cids[$file] = substr($this->addEmbeddedFile($file)->getHeader('Content-ID'), 1, -1);
  197. }
  198. $html = substr_replace($html,
  199. "{$m[1][0]}{$m[2][0]}cid:{$cids[$file]}",
  200. $m[0][1], strlen($m[0][0])
  201. );
  202. }
  203. }
  204. if ($this->getSubject() == NULL) { // intentionally ==
  205. $html = Strings::replace($html, '#<title>(.+?)</title>#is', function ($m) use (& $title) {
  206. $title = $m[1];
  207. });
  208. $this->setSubject(html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
  209. }
  210. $this->html = ltrim(str_replace("\r", '', $html), "\n");
  211. if ($this->getBody() == NULL && $html != NULL) { // intentionally ==
  212. $this->setBody($this->buildText($html));
  213. }
  214. return $this;
  215. }
  216. /**
  217. * Gets HTML body.
  218. * @return mixed
  219. */
  220. public function getHtmlBody()
  221. {
  222. return $this->html;
  223. }
  224. /**
  225. * Adds embedded file.
  226. * @param string
  227. * @param string
  228. * @param string
  229. * @return MimePart
  230. */
  231. public function addEmbeddedFile($file, $content = NULL, $contentType = NULL)
  232. {
  233. return $this->inlines[$file] = $this->createAttachment($file, $content, $contentType, 'inline')
  234. ->setHeader('Content-ID', $this->getRandomId());
  235. }
  236. /**
  237. * Adds inlined Mime Part.
  238. * @param MimePart
  239. * @return self
  240. */
  241. public function addInlinePart(MimePart $part)
  242. {
  243. $this->inlines[] = $part;
  244. return $this;
  245. }
  246. /**
  247. * Adds attachment.
  248. * @param string
  249. * @param string
  250. * @param string
  251. * @return MimePart
  252. */
  253. public function addAttachment($file, $content = NULL, $contentType = NULL)
  254. {
  255. return $this->attachments[] = $this->createAttachment($file, $content, $contentType, 'attachment');
  256. }
  257. /**
  258. * Gets all email attachments.
  259. * @return MimePart[]
  260. */
  261. public function getAttachments()
  262. {
  263. return $this->attachments;
  264. }
  265. /**
  266. * Creates file MIME part.
  267. * @return MimePart
  268. */
  269. private function createAttachment($file, $content, $contentType, $disposition)
  270. {
  271. $part = new MimePart;
  272. if ($content === NULL) {
  273. $content = @file_get_contents($file); // @ is escalated to exception
  274. if ($content === FALSE) {
  275. throw new Nette\FileNotFoundException("Unable to read file '$file'.");
  276. }
  277. } else {
  278. $content = (string) $content;
  279. }
  280. $part->setBody($content);
  281. $part->setContentType($contentType ? $contentType : finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content));
  282. $part->setEncoding(preg_match('#(multipart|message)/#A', $contentType) ? self::ENCODING_8BIT : self::ENCODING_BASE64);
  283. $part->setHeader('Content-Disposition', $disposition . '; filename="' . Strings::fixEncoding(basename($file)) . '"');
  284. return $part;
  285. }
  286. /********************* building and sending ****************d*g**/
  287. /**
  288. * Returns encoded message.
  289. * @return string
  290. */
  291. public function generateMessage()
  292. {
  293. return $this->build()->getEncodedMessage();
  294. }
  295. /**
  296. * Builds email. Does not modify itself, but returns a new object.
  297. * @return self
  298. */
  299. protected function build()
  300. {
  301. $mail = clone $this;
  302. $mail->setHeader('Message-ID', $this->getRandomId());
  303. $cursor = $mail;
  304. if ($mail->attachments) {
  305. $tmp = $cursor->setContentType('multipart/mixed');
  306. $cursor = $cursor->addPart();
  307. foreach ($mail->attachments as $value) {
  308. $tmp->addPart($value);
  309. }
  310. }
  311. if ($mail->html != NULL) { // intentionally ==
  312. $tmp = $cursor->setContentType('multipart/alternative');
  313. $cursor = $cursor->addPart();
  314. $alt = $tmp->addPart();
  315. if ($mail->inlines) {
  316. $tmp = $alt->setContentType('multipart/related');
  317. $alt = $alt->addPart();
  318. foreach ($mail->inlines as $value) {
  319. $tmp->addPart($value);
  320. }
  321. }
  322. $alt->setContentType('text/html', 'UTF-8')
  323. ->setEncoding(preg_match('#[^\n]{990}#', $mail->html)
  324. ? self::ENCODING_QUOTED_PRINTABLE
  325. : (preg_match('#[\x80-\xFF]#', $mail->html) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
  326. ->setBody($mail->html);
  327. }
  328. $text = $mail->getBody();
  329. $mail->setBody(NULL);
  330. $cursor->setContentType('text/plain', 'UTF-8')
  331. ->setEncoding(preg_match('#[^\n]{990}#', $text)
  332. ? self::ENCODING_QUOTED_PRINTABLE
  333. : (preg_match('#[\x80-\xFF]#', $text) ? self::ENCODING_8BIT : self::ENCODING_7BIT))
  334. ->setBody($text);
  335. return $mail;
  336. }
  337. /**
  338. * Builds text content.
  339. * @return string
  340. */
  341. protected function buildText($html)
  342. {
  343. $text = Strings::replace($html, [
  344. '#<(style|script|head).*</\\1>#Uis' => '',
  345. '#<t[dh][ >]#i' => ' $0',
  346. '#<a\s[^>]*href=(?|"([^"]+)"|\'([^\']+)\')[^>]*>(.*?)</a>#is' => '$2 &lt;$1&gt;',
  347. '#[\r\n]+#' => ' ',
  348. '#<(/?p|/?h\d|li|br|/tr)[ >/]#i' => "\n$0",
  349. ]);
  350. $text = html_entity_decode(strip_tags($text), ENT_QUOTES, 'UTF-8');
  351. $text = Strings::replace($text, '#[ \t]+#', ' ');
  352. return trim($text);
  353. }
  354. /** @return string */
  355. private function getRandomId()
  356. {
  357. return '<' . Nette\Utils\Random::generate() . '@'
  358. . preg_replace('#[^\w.-]+#', '', isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n'))
  359. . '>';
  360. }
  361. }