PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/nette-dev/Mail/Mail.php

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