PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/Nette/Mail/Mail.php

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