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

/tags/0_9_4/zibo_modules/zibo.mail/src/zibo/library/mail/transport/MessageParser.php

https://gitlab.com/BGCX261/zibo-svn-to-git
PHP | 401 lines | 179 code | 67 blank | 155 comment | 27 complexity | 14c454dd412bb94e4b8c5bab514d6813 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. namespace zibo\library\mail\transport;
  3. use zibo\core\Zibo;
  4. use zibo\library\mail\exception\MailException;
  5. use zibo\library\mail\Address;
  6. use zibo\library\mail\Message;
  7. use zibo\library\mail\MimePart;
  8. /**
  9. * Object to parse mail message objects into the parts needed to actually send the message
  10. */
  11. class MessageParser {
  12. /**
  13. * Configuration key for the debug recipient, when this is set, all mails will go to this address and no other
  14. * @var string
  15. */
  16. const CONFIG_DEBUG = 'mail.debug';
  17. /**
  18. * Configuration key for the default sender of messages
  19. * @var string
  20. */
  21. const CONFIG_SENDER = 'mail.sender';
  22. /**
  23. * Header for the MIME version
  24. * @var string
  25. */
  26. const HEADER_MIME_VERSION = 'MIME-Version';
  27. /**
  28. * Header for the content type
  29. * @var string
  30. */
  31. const HEADER_CONTENT_TYPE = 'Content-Type';
  32. /**
  33. * Header for the content disposition
  34. * @var string
  35. */
  36. const HEADER_CONTENT_DISPOSITION = 'Content-Disposition';
  37. /**
  38. * Header for the content transfer encoding
  39. * @var string
  40. */
  41. const HEADER_CONTENT_TRANSFER_ENCODING = 'Content-Transfer-Encoding';
  42. /**
  43. * Header for the from address
  44. * @var string
  45. */
  46. const HEADER_FROM = 'From';
  47. /**
  48. * Header for the to addresses
  49. * @var string
  50. */
  51. const HEADER_TO = 'To';
  52. /**
  53. * Header for the carbon copy addresses
  54. * @var string
  55. */
  56. const HEADER_CC = 'Cc';
  57. /**
  58. * Header for the blind carbon copy addresses
  59. * @var string
  60. */
  61. const HEADER_BCC = 'Bcc';
  62. /**
  63. * Header for the reply to address
  64. * @var string
  65. */
  66. const HEADER_REPLY_TO = 'Reply-To';
  67. /**
  68. * Header for the message id
  69. * @var string
  70. */
  71. const HEADER_MESSAGE_ID = 'Message-Id';
  72. /**
  73. * Header for the in reply to
  74. * @var string
  75. */
  76. const HEADER_IN_REPLY_TO = 'In-Reply-To';
  77. /**
  78. * Header for the references
  79. * @var string
  80. */
  81. const HEADER_REFERENCES = 'References';
  82. /**
  83. * Header for the auto submitted flag
  84. * @var string
  85. */
  86. const HEADER_AUTO_SUBMITTED = 'Auto-Submitted';
  87. /**
  88. * MIME type for a multipart mixed message
  89. * @var string
  90. */
  91. const MIME_MULTIPART_MIXED = 'multipart/mixed';
  92. /**
  93. * MIME type for a alternative message
  94. * @var string
  95. */
  96. const MIME_MULTIPART_ALTERNATIVE = 'multipart/alternative';
  97. /**
  98. * The subject of the parsed message
  99. * @var string
  100. */
  101. private $subject;
  102. /**
  103. * The body of the parsed message
  104. * @var string
  105. */
  106. private $body;
  107. /**
  108. * The headers of the parsed message
  109. * @var array
  110. */
  111. private $headers;
  112. /**
  113. * Parses the provided message and get all data to actually send it
  114. * @param zibo\library\mail\Message $message The message to parse
  115. * @param array $variables Array with variables to replace in the body
  116. * @return null
  117. */
  118. public function __construct(Message $message, array $variables = array()) {
  119. $this->parseMessage($message, $variables);
  120. }
  121. /**
  122. * Gets the subject of the message
  123. * @return string
  124. */
  125. public function getSubject() {
  126. return $this->subject;
  127. }
  128. /**
  129. * Gets the parsed body of the message
  130. * @return string
  131. */
  132. public function getBody() {
  133. return $this->body;
  134. }
  135. /**
  136. * Gets the parsed headers of the message
  137. * @return array Array with the header name as key and the full header as value
  138. */
  139. public function getHeaders() {
  140. return $this->headers;
  141. }
  142. /**
  143. * Parses the headers and the body from the provided message
  144. * @param zibo\library\mail\Message $message The message to parse
  145. * @param array $variables Array with variables to replace in the body
  146. * @return null
  147. */
  148. private function parseMessage(Message $message, array $variables) {
  149. $this->headers = array();
  150. $this->subject = $message->getSubject();
  151. $this->body = '';
  152. $this->parseAddresses($message);
  153. $this->parseHeaders($message);
  154. $this->parseParts($message, $variables);
  155. }
  156. /**
  157. * Parses the addresses of the provided message and adds them to the headers of the message
  158. * @param zibo\library\mail\Message $message The message to parse the addresses of
  159. * @return null
  160. */
  161. private function parseAddresses(Message $message) {
  162. $from = $message->getFrom();
  163. $to = $message->getTo();
  164. $cc = $message->getCc();
  165. $bcc = $message->getBcc();
  166. $replyTo = $message->getReplyTo();
  167. if (empty($to) && empty($cc) && empty($bcc)) {
  168. throw new MailException('No recipients set');
  169. }
  170. if (!$from) {
  171. $sender = Zibo::getInstance()->getConfigValue(self::CONFIG_SENDER);
  172. if ($sender) {
  173. $from = new Address($sender);
  174. }
  175. }
  176. if ($from) {
  177. $this->headers[self::HEADER_FROM] = self::HEADER_FROM . ': ' . $from->__toString();
  178. }
  179. $debug = Zibo::getInstance()->getConfigValue(self::CONFIG_DEBUG);
  180. if ($debug) {
  181. $debugAddress = new Address($debug);
  182. $this->addAddressesToHeaders(self::HEADER_TO, array($debugAddress));
  183. } else {
  184. $this->addAddressesToHeaders(self::HEADER_TO, $to);
  185. $this->addAddressesToHeaders(self::HEADER_CC, $cc);
  186. $this->addAddressesToHeaders(self::HEADER_BCC, $bcc);
  187. if ($replyTo) {
  188. $this->headers[self::HEADER_REPLY_TO] = self::HEADER_REPLY_TO . ': ' . $replyTo->__toString();
  189. }
  190. }
  191. }
  192. /**
  193. * Adds the recipients to the provided header
  194. * @param string $header Name of the header (To, Cc, Bcc, ...)
  195. * @param array $addresses Array with Address objects
  196. * @return null
  197. */
  198. private function addAddressesToHeaders($header, $addresses) {
  199. if (!$addresses) {
  200. return;
  201. }
  202. $content = '';
  203. foreach ($addresses as $address) {
  204. $content .= ($content == '' ? '' : ', ') . $address->__toString();
  205. }
  206. $this->headers[$header] = $header . ': ' . $content;
  207. }
  208. /**
  209. * Parses the headers of the provided message
  210. * @param zibo\library\mail\Message $message The message to parse the headers of
  211. * @return null
  212. */
  213. private function parseHeaders(Message $message) {
  214. $this->headers[self::HEADER_MIME_VERSION] = self::HEADER_MIME_VERSION . ': 1.0';
  215. $messageId = $message->getMessageId();
  216. if ($messageId) {
  217. $this->headers[self::HEADER_MESSAGE_ID] = self::HEADER_MESSAGE_ID . ': ' . $messageId;
  218. }
  219. $inReplyTo = $message->getInReplyTo();
  220. if ($inReplyTo) {
  221. $this->headers[self::HEADER_IN_REPLY_TO] = self::HEADER_IN_REPLY_TO . ': ' . $inReplyTo;
  222. }
  223. $references = $message->getReferences();
  224. if ($references) {
  225. $this->headers[self::HEADER_REFERENCES] = self::HEADER_REFERENCES . ': ' . implode(' ', $references);
  226. }
  227. $autoSubmitted = $message->getAutoSubmitted();
  228. if ($autoSubmitted) {
  229. $this->headers[self::HEADER_AUTO_SUBMITTED] = self::HEADER_AUTO_SUBMITTED . ': ' . $autoSubmitted;
  230. }
  231. }
  232. /**
  233. * Parses the body parts of the provided message
  234. * @param zibo\library\mail\Message $message The message to parse the parts of
  235. * @param array $variables Array with variables to replace in the body
  236. * @return null
  237. */
  238. private function parseParts(Message $message, array $variables) {
  239. $attachments = $message->getParts();
  240. $numParts = count($attachments);
  241. $body = null;
  242. if (isset($attachments[Message::PART_BODY])) {
  243. $body = $attachments[Message::PART_BODY];
  244. unset($attachments[Message::PART_BODY]);
  245. }
  246. $alternative = null;
  247. if (isset($attachments[Message::PART_ALTERNATIVE])) {
  248. $alternative = $attachments[Message::PART_ALTERNATIVE];
  249. unset($attachments[Message::PART_ALTERNATIVE]);
  250. }
  251. if (!$alternative && $numParts == 1) {
  252. $this->headers[self::HEADER_CONTENT_TYPE] = self::HEADER_CONTENT_TYPE . ': ' . $body->getMimeType() . '; charset=' . $body->getCharset();
  253. $this->headers[self::HEADER_CONTENT_TRANSFER_ENCODING] = self::HEADER_CONTENT_TRANSFER_ENCODING . ': ' . $body->getTransferEncoding();
  254. $this->addPartToBody($body, $variables, true);
  255. return;
  256. }
  257. $salt = $this->generateSalt();
  258. if ($alternative && $numParts == 2) {
  259. $this->headers[self::HEADER_CONTENT_TYPE] = self::HEADER_CONTENT_TYPE . ': ' . self::MIME_MULTIPART_ALTERNATIVE . '; boundary=' . $salt;
  260. $this->addPartsToBody($body, $alternative, $variables, $salt);
  261. return;
  262. }
  263. $this->headers[self::HEADER_CONTENT_TYPE] = self::HEADER_CONTENT_TYPE . ': ' . self::MIME_MULTIPART_MIXED . '; boundary=' . $salt;
  264. if ($alternative) {
  265. $messageSalt = $this->generateSalt('message');
  266. $this->body .= '--' . $salt . "\n";
  267. $this->body .= self::HEADER_CONTENT_TYPE . ': ' . self::MIME_MULTIPART_ALTERNATIVE . '; boundary=' . $messageSalt . "\n\n";
  268. $this->addPartsToBody($body, $alternative, $variables, $messageSalt);
  269. } elseif ($body) {
  270. $this->body .= '--' . $salt . "\n";
  271. $this->addPartToBody($body, $variables);
  272. }
  273. if (isset($attachments) && is_array($attachments)) {
  274. foreach ($attachments as $name => $attachment) {
  275. $this->body .= '--' . $salt . "\n";
  276. $this->addAttachmentToBody($attachment, $name);
  277. }
  278. }
  279. $this->body .= '--' . $salt . '--' . "\n";
  280. }
  281. /**
  282. * Adds the body and the alternative body to the body of the mail
  283. * @param zibo\library\mail\MimePart $body The MIME part of the body (HTML)
  284. * @param zibo\library\mail\MimePart $alternative The MIME part of the alternative body (plain text)
  285. * @param array $variables Array with variables to replace in the body
  286. * @param string $salt The salt to delimit the parts
  287. * @return null
  288. */
  289. private function addPartsToBody(MimePart $body, MimePart $alternative, array $variables, $salt) {
  290. $this->body .= '--' . $salt . "\n";
  291. $this->addPartToBody($alternative, $variables);
  292. $this->body .= '--' . $salt . "\n";
  293. $this->addPartToBody($body, $variables);
  294. $this->body .= '--' . $salt . '--' . "\n";
  295. }
  296. /**
  297. * Adds a MIME part to the body of the message
  298. * @param zibo\library\mail\MimePart $part The MIME part to add
  299. * @param array $variables Array with variables to replace in the body of the part
  300. * @param boolean $skipHeaders Set to true to skip the content type and transfer encoding
  301. * @return null
  302. */
  303. private function addPartToBody(MimePart $part, array $variables, $skipHeaders = false) {
  304. if (!$skipHeaders) {
  305. $this->body .= self::HEADER_CONTENT_TYPE . ': ' . $part->getMimeType() . '; charset=' . $part->getCharset() . "\n";
  306. $this->body .= self::HEADER_CONTENT_TRANSFER_ENCODING . ': ' . $part->getTransferEncoding() . "\n\n";
  307. }
  308. $body = $part->getBody();
  309. foreach ($variables as $key => $value) {
  310. $body = str_replace('%' . $key . '%', $value, $body);
  311. }
  312. $this->body .= $body . "\n\n";
  313. }
  314. /**
  315. * Adds a attachment to the body of the message
  316. * @param zibo\library\mail\MimePart $part The MIME part of the attachment
  317. * @param string $name The name of the attachment
  318. * @return null
  319. */
  320. private function addAttachmentToBody(MimePart $part, $name) {
  321. $this->body .= self::HEADER_CONTENT_TYPE . ': ' . $part->getMimeType() . '; name="' . $name . "\"\n";
  322. $this->body .= self::HEADER_CONTENT_DISPOSITION . ': attachment; filename="' . $name . "\"\n";
  323. $this->body .= self::HEADER_CONTENT_TRANSFER_ENCODING . ': ' . $part->getTransferEncoding() . "\n\n";
  324. $this->body .= $part->getBody() . "\n\n";
  325. }
  326. /**
  327. * Generates a salt to delimit MIME parts
  328. * @param string $salt Token for the salt (optional)
  329. * @return string
  330. */
  331. private function generateSalt($salt = '') {
  332. return md5($salt . microtime());
  333. }
  334. }