PageRenderTime 40ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/swift/Swift/Message/Mime.php

https://gitlab.com/staging06/myproject
PHP | 500 lines | 303 code | 12 blank | 185 comment | 35 complexity | 5957a4b0c13b125be5703718d98529b7 MD5 | raw file
  1. <?php
  2. /**
  3. * Swift Mailer MIME Library central component
  4. * Please read the LICENSE file
  5. * @copyright Chris Corbyn <chris@w3style.co.uk>
  6. * @author Chris Corbyn <chris@w3style.co.uk>
  7. * @package Swift_Message
  8. * @license GNU Lesser General Public License
  9. */
  10. require_once dirname(__FILE__) . "/../ClassLoader.php";
  11. Swift_ClassLoader::load("Swift_File");
  12. Swift_ClassLoader::load("Swift_Message_MimeException");
  13. /**
  14. * Mime is the underbelly for Messages, Attachments, Parts, Embedded Images, Forwarded Mail, etc
  15. * In fact, every single component of the composed email is simply a new Mime document nested inside another
  16. * When you piece an email together in this way you see just how straight-forward it really is
  17. * @package Swift_Message
  18. * @author Chris Corbyn <chris@w3style.co.uk>
  19. */
  20. abstract class Swift_Message_Mime
  21. {
  22. /**
  23. * Constant for plain-text emails
  24. */
  25. const PLAIN = "text/plain";
  26. /**
  27. * Constant for HTML emails
  28. */
  29. const HTML = "text/html";
  30. /**
  31. * Constant for miscellaneous mime type
  32. */
  33. const MISC = "application/octet-stream";
  34. /**
  35. * Constant for MIME sections which must appear in the multipart/alternative section.
  36. */
  37. const LEVEL_ALTERNATIVE = "alternative";
  38. /**
  39. * Constant for MIME sections which must appear in the multipart/related section.
  40. */
  41. const LEVEL_RELATED = "related";
  42. /**
  43. * Constant for MIME sections which must appear in the multipart/mixed section.
  44. */
  45. const LEVEL_MIXED = "mixed";
  46. /**
  47. * Constant for MIME sections which must appear in the multipart/mixed section.
  48. */
  49. const LEVEL_TOP = "top";
  50. /**
  51. * Constant for safe line length in almost all places
  52. */
  53. const SAFE_LENGTH = 1000; //RFC 2822
  54. /**
  55. * Constant for really safe line length
  56. */
  57. const VERY_SAFE_LENGTH = 76; //For command line mail clients such as pine
  58. /**
  59. * The header part of this MIME document
  60. * @var Swift_Message_Headers
  61. */
  62. public $headers = null;
  63. /**
  64. * The body of the documented (unencoded)
  65. * @var string data
  66. */
  67. protected $data = "";
  68. /**
  69. * Maximum line length
  70. * @var int
  71. */
  72. protected $wrap = 1000; //RFC 2822
  73. /**
  74. * Nested mime parts
  75. * @var array
  76. */
  77. protected $children = array();
  78. /**
  79. * The boundary used to separate mime parts
  80. * @var string
  81. */
  82. protected $boundary = null;
  83. /**
  84. * The line ending characters needed
  85. * @var string
  86. */
  87. protected $LE = "\r\n";
  88. /**
  89. * An instance of Swift_Cache
  90. * @var Swift_Cache
  91. */
  92. protected $cache;
  93. /**
  94. * A list of used MIME boundaries after they're generated.
  95. * @var array
  96. */
  97. protected static $usedBoundaries = array();
  98. /**
  99. * Constructor
  100. */
  101. public function __construct()
  102. {
  103. Swift_ClassLoader::load("Swift_Message_Headers");
  104. $this->setHeaders(new Swift_Message_Headers());
  105. Swift_ClassLoader::load("Swift_CacheFactory");
  106. $this->cache = Swift_CacheFactory::getCache();
  107. }
  108. /**
  109. * Compute a unique boundary
  110. * @return string
  111. */
  112. public static function generateBoundary()
  113. {
  114. do
  115. {
  116. $boundary = uniqid(rand(), true);
  117. } while (in_array($boundary, self::$usedBoundaries));
  118. self::$usedBoundaries[] = $boundary;
  119. return "_=_swift-" . $boundary . "_=_";
  120. }
  121. /**
  122. * Replace the current headers with new ones
  123. * DO NOT DO THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
  124. * @param Swift_Message_Headers The headers to use
  125. */
  126. public function setHeaders($headers)
  127. {
  128. $this->headers = $headers;
  129. }
  130. /**
  131. * Set the line ending character to use
  132. * @param string The line ending sequence
  133. * @return boolean
  134. */
  135. public function setLE($le)
  136. {
  137. if (in_array($le, array("\r", "\n", "\r\n")))
  138. {
  139. $this->cache->clear("body");
  140. $this->LE = $le;
  141. //This change should be recursive
  142. $this->headers->setLE($le);
  143. foreach ($this->children as $id => $child)
  144. {
  145. $this->children[$id]->setLE($le);
  146. }
  147. return true;
  148. }
  149. else return false;
  150. }
  151. /**
  152. * Get the line ending sequence
  153. * @return string
  154. */
  155. public function getLE()
  156. {
  157. return $this->LE;
  158. }
  159. /**
  160. * Reset the entire cache state from this branch of the tree and traversing down through the children
  161. */
  162. public function uncacheAll()
  163. {
  164. $this->cache->clear("body");
  165. $this->cache->clear("append");
  166. $this->cache->clear("headers");
  167. $this->cache->clear("dbl_le");
  168. $this->headers->uncacheAll();
  169. foreach ($this->children as $id => $child)
  170. {
  171. $this->children[$id]->uncacheAll();
  172. }
  173. }
  174. /**
  175. * Set the content type of this MIME document
  176. * @param string The content type to use in the same format as MIME 1.0 expects
  177. */
  178. public function setContentType($type)
  179. {
  180. $this->headers->set("Content-Type", $type);
  181. }
  182. /**
  183. * Get the content type which has been set
  184. * The MIME 1.0 Content-Type is provided as a string
  185. * @return string
  186. */
  187. public function getContentType()
  188. {
  189. try {
  190. return $this->headers->get("Content-Type");
  191. } catch (Swift_Message_MimeException $e) {
  192. return false;
  193. }
  194. }
  195. /**
  196. * Set the encoding format to be used on the body of the document
  197. * @param string The encoding type used
  198. * @param boolean If this encoding format should be used recursively. Note, this only takes effect if no encoding is set in the children.
  199. * @param boolean If the encoding should only be applied when the string is not ascii.
  200. */
  201. public function setEncoding($encoding, $recursive=false, $non_ascii=false)
  202. {
  203. $this->cache->clear("body");
  204. switch (strtolower($encoding))
  205. {
  206. case "q": case "qp": case "quoted-printable":
  207. $encoding = "quoted-printable";
  208. break;
  209. case "b": case "base64":
  210. $encoding = "base64";
  211. break;
  212. case "7bit": case "8bit": case "binary":
  213. $encoding = strtolower($encoding);
  214. break;
  215. }
  216. $data = $this->getData();
  217. Swift_ClassLoader::load("Swift_Message_Encoder");
  218. if ($non_ascii && is_string($data) && strlen($data) > 0 && !Swift_Message_Encoder::instance()->is7BitAscii($data))
  219. {
  220. $this->headers->set("Content-Transfer-Encoding", $encoding);
  221. }
  222. elseif (!$non_ascii || !is_string($data))
  223. {
  224. $this->headers->set("Content-Transfer-Encoding", $encoding);
  225. }
  226. if ($recursive)
  227. {
  228. foreach ($this->children as $id => $child)
  229. {
  230. if (!$child->getEncoding()) $this->children[$id]->setEncoding($encoding, $recursive, $non_ascii);
  231. }
  232. }
  233. }
  234. /**
  235. * Get the encoding format used in this document
  236. * @return string
  237. */
  238. public function getEncoding()
  239. {
  240. try {
  241. return $this->headers->get("Content-Transfer-Encoding");
  242. } catch (Swift_Message_MimeException $e) {
  243. return false;
  244. }
  245. }
  246. /**
  247. * Specify the string which makes up the body of this message
  248. * HINT: You can always nest another MIME document here if you call it's build() method.
  249. * $data can be an object of Swift_File or a string
  250. * @param mixed The body of the document
  251. */
  252. public function setData($data)
  253. {
  254. $this->cache->clear("body");
  255. if ($data instanceof Swift_File) $this->data = $data;
  256. else $this->data = (string) $data;
  257. }
  258. /**
  259. * Return the string which makes up the body of this MIME document
  260. * @return string,Swift_File
  261. */
  262. public function getData()
  263. {
  264. return $this->data;
  265. }
  266. /**
  267. * Get the data in the format suitable for sending
  268. * @return Swift_Cache_OutputStream
  269. * @throws Swift_FileException If the file stream given cannot be read
  270. * @throws Swift_Message_MimeException If some required headers have been forcefully removed
  271. */
  272. public function buildData()
  273. {
  274. Swift_ClassLoader::load("Swift_Message_Encoder");
  275. Swift_ClassLoader::load("Swift_Cache_JointOutputStream");
  276. if (!empty($this->children)) //If we've got some mime parts we need to stick them onto the end of the message
  277. {
  278. if ($this->boundary === null) $this->boundary = self::generateBoundary();
  279. $this->headers->setAttribute("Content-Type", "boundary", $this->boundary);
  280. $this->cache->clear("append");
  281. foreach ($this->children as $part)
  282. {
  283. $this->cache->write("append", $this->LE . "--" . $this->boundary . $this->LE);
  284. $part_stream = $part->build();
  285. while (false !== $bytes = $part_stream->read()) $this->cache->write("append", $bytes);
  286. }
  287. $this->cache->write("append", $this->LE . "--" . $this->boundary . "--" . $this->LE);
  288. }
  289. $joint_os = new Swift_Cache_JointOutputStream();
  290. //Try using a cached version to save some cycles (at the expense of memory)
  291. //if ($this->cache !== null) return $this->cache . $append;
  292. if ($this->cache->has("body"))
  293. {
  294. $joint_os->addStream($this->cache->getOutputStream("body"));
  295. $joint_os->addStream($this->cache->getOutputStream("append"));
  296. return $joint_os;
  297. }
  298. $is_file = ($this->getData() instanceof Swift_File);
  299. switch ($this->getEncoding())
  300. {
  301. case "quoted-printable":
  302. if ($is_file)
  303. {
  304. $qp_os = Swift_Message_Encoder::instance()->QPEncodeFile($this->getData(), 76, $this->LE);
  305. while (false !== $bytes = $qp_os->read())
  306. $this->cache->write("body", $bytes);
  307. }
  308. else
  309. {
  310. $this->cache->write("body", Swift_Message_Encoder::instance()->QPEncode($this->getData(), 76, 0, false, $this->LE));
  311. }
  312. break;
  313. case "base64":
  314. if ($is_file)
  315. {
  316. $b64_os = Swift_Message_Encoder::instance()->base64EncodeFile($this->getData(), 76, $this->LE);
  317. while (false !== $bytes = $b64_os->read())
  318. $this->cache->write("body", $bytes);
  319. }
  320. else
  321. {
  322. $this->cache->write("body", Swift_Message_Encoder::instance()->base64Encode($this->getData(), 76, 0, false, $this->LE));
  323. }
  324. break;
  325. case "binary":
  326. if ($is_file)
  327. {
  328. $data = $this->getData();
  329. while (false !== $bytes = $data->read(8192))
  330. $this->cache->write("body", $bytes);
  331. }
  332. else
  333. {
  334. $this->cache->write("body", $this->getData());
  335. }
  336. break;
  337. case "7bit":
  338. if ($is_file)
  339. {
  340. $os = Swift_Message_Encoder::instance()->encode7BitFile($this->getData(), $this->wrap, $this->LE);
  341. while (false !== $bytes = $os->read())
  342. $this->cache->write("body", $bytes);
  343. }
  344. else
  345. {
  346. $this->cache->write("body", Swift_Message_Encoder::instance()->encode7Bit($this->getData(), $this->wrap, $this->LE));
  347. }
  348. break;
  349. case "8bit": default:
  350. if ($is_file)
  351. {
  352. $os = Swift_Message_Encoder::instance()->encode8BitFile($this->getData(), $this->wrap, $this->LE);
  353. while (false !== $bytes = $os->read())
  354. $this->cache->write("body", $bytes);
  355. }
  356. else
  357. {
  358. $this->cache->write("body", Swift_Message_Encoder::instance()->encode8Bit($this->getData(), $this->wrap, $this->LE));
  359. }
  360. break;
  361. }
  362. $joint_os->addStream($this->cache->getOutputStream("body"));
  363. $joint_os->addStream($this->cache->getOutputStream("append"));
  364. return $joint_os;
  365. }
  366. /**
  367. * Set the size at which lines wrap around (includes the CRLF)
  368. * @param int The length of a line
  369. */
  370. public function setLineWrap($len)
  371. {
  372. $this->cache->clear("body");
  373. $this->wrap = (int) $len;
  374. }
  375. /**
  376. * Nest a child mime part in this document
  377. * @param Swift_Message_Mime
  378. * @param string The identifier to use, optional
  379. * @param int Add the part before (-1) or after (+1) the other parts
  380. * @return string The identifier for this part
  381. */
  382. public function addChild(Swift_Message_Mime $mime, $id=null, $after=1)
  383. {
  384. if (empty($id))
  385. {
  386. do
  387. {
  388. $id = uniqid();
  389. } while (array_key_exists($id, $this->children));
  390. }
  391. $id = (string) $id;
  392. if ($after == -1) $this->children = array_merge(array($id => $mime), $this->children);
  393. else $this->children[$id] = $mime;
  394. return $id;
  395. }
  396. /**
  397. * Check if a child exists identified by $id
  398. * @param string Identifier to look for
  399. * @return boolean
  400. */
  401. public function hasChild($id)
  402. {
  403. return array_key_exists($id, $this->children);
  404. }
  405. /**
  406. * Get a child document, identified by $id
  407. * @param string The identifier for this child
  408. * @return Swift_Message_Mime The child document
  409. * @throws Swift_Message_MimeException If no such child exists
  410. */
  411. public function getChild($id)
  412. {
  413. if ($this->hasChild($id))
  414. {
  415. return $this->children[$id];
  416. }
  417. else
  418. {
  419. throw new Swift_Message_MimeException(
  420. "Cannot retrieve child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
  421. }
  422. }
  423. /**
  424. * Remove a part from the document
  425. * @param string The identifier of the child
  426. * @throws Swift_Message_MimeException If no such part exists
  427. */
  428. public function removeChild($id)
  429. {
  430. $id = (string) $id;
  431. if (!$this->hasChild($id))
  432. {
  433. throw new Swift_Message_MimeException(
  434. "Cannot remove child part identified by '" . $id . "' as it does not exist. Consider using hasChild() to check.");
  435. }
  436. else
  437. {
  438. $this->children[$id] = null;
  439. unset($this->children[$id]);
  440. }
  441. }
  442. /**
  443. * List the IDs of all children in this document
  444. * @return array
  445. */
  446. public function listChildren()
  447. {
  448. return array_keys($this->children);
  449. }
  450. /**
  451. * Get the total number of children present in this document
  452. * @return int
  453. */
  454. public function numChildren()
  455. {
  456. return count($this->children);
  457. }
  458. /**
  459. * Get the level at which this mime part would appear in a document
  460. * One of "mixed", "alternative" or "related"
  461. * @return string
  462. */
  463. abstract public function getLevel();
  464. /**
  465. * Compile the entire MIME document into a string
  466. * The returned string may be used in other documents if needed.
  467. * @return Swift_Cache_OutputStream
  468. */
  469. public function build()
  470. {
  471. $this->preBuild();
  472. $data = $this->buildData();
  473. $joint_os = new Swift_Cache_JointOutputStream();
  474. $this->cache->clear("headers");
  475. $this->cache->write("headers", $this->headers->build());
  476. $joint_os->addStream($this->cache->getOutputStream("headers"));
  477. $this->cache->clear("dbl_le");
  478. $this->cache->write("dbl_le", str_repeat($this->LE, 2));
  479. $joint_os->addStream($this->cache->getOutputStream("dbl_le"));
  480. $joint_os->addStream($data);
  481. return $joint_os;
  482. //return $this->headers->build() . str_repeat($this->LE, 2) . $data;
  483. }
  484. /**
  485. * Execute any logic needed prior to building
  486. */
  487. abstract public function preBuild();
  488. }