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

/tine20/Zend/Mail/Part.php

https://gitlab.com/rsilveira1987/Expresso
PHP | 603 lines | 260 code | 68 blank | 275 comment | 51 complexity | 259141ea4cb28e9859d3481079e264ea MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Mail
  17. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * @see Zend_Mime_Decode
  23. */
  24. require_once 'Zend/Mime/Decode.php';
  25. /**
  26. * @see Zend_Mail_Header_HeaderName
  27. */
  28. require_once 'Zend/Mail/Header/HeaderName.php';
  29. /**
  30. * @see Zend_Mail_Header_HeaderValue
  31. */
  32. require_once 'Zend/Mail/Header/HeaderValue.php';
  33. /**
  34. * @see Zend_Mail_Part_Interface
  35. */
  36. require_once 'Zend/Mail/Part/Interface.php';
  37. /**
  38. * @category Zend
  39. * @package Zend_Mail
  40. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  41. * @license http://framework.zend.com/license/new-bsd New BSD License
  42. */
  43. class Zend_Mail_Part implements RecursiveIterator, Zend_Mail_Part_Interface
  44. {
  45. /**
  46. * headers of part as array
  47. * @var null|array
  48. */
  49. protected $_headers;
  50. /**
  51. * raw part body
  52. * @var null|string
  53. */
  54. protected $_content;
  55. /**
  56. * toplines as fetched with headers
  57. * @var string
  58. */
  59. protected $_topLines = '';
  60. /**
  61. * parts of multipart message
  62. * @var array
  63. */
  64. protected $_parts = array();
  65. /**
  66. * count of parts of a multipart message
  67. * @var null|int
  68. */
  69. protected $_countParts;
  70. /**
  71. * current position of iterator
  72. * @var int
  73. */
  74. protected $_iterationPos = 1;
  75. /**
  76. * mail handler, if late fetch is active
  77. * @var null|Zend_Mail_Storage_Abstract
  78. */
  79. protected $_mail;
  80. /**
  81. * message number for mail handler
  82. * @var int
  83. */
  84. protected $_messageNum = 0;
  85. /**
  86. * Class to use when creating message parts
  87. * @var string
  88. */
  89. protected $_partClass;
  90. /**
  91. * Public constructor
  92. *
  93. * Zend_Mail_Part supports different sources for content. The possible params are:
  94. * - handler a instance of Zend_Mail_Storage_Abstract for late fetch
  95. * - id number of message for handler
  96. * - raw raw content with header and body as string
  97. * - headers headers as array (name => value) or string, if a content part is found it's used as toplines
  98. * - noToplines ignore content found after headers in param 'headers'
  99. * - content content as string
  100. *
  101. * @param array $params full message with or without headers
  102. * @throws Zend_Mail_Exception
  103. */
  104. public function __construct(array $params)
  105. {
  106. if (isset($params['handler'])) {
  107. if (!$params['handler'] instanceof Zend_Mail_Storage_Abstract) {
  108. /**
  109. * @see Zend_Mail_Exception
  110. */
  111. require_once 'Zend/Mail/Exception.php';
  112. throw new Zend_Mail_Exception('handler is not a valid mail handler');
  113. }
  114. if (!isset($params['id'])) {
  115. /**
  116. * @see Zend_Mail_Exception
  117. */
  118. require_once 'Zend/Mail/Exception.php';
  119. throw new Zend_Mail_Exception('need a message id with a handler');
  120. }
  121. $this->_mail = $params['handler'];
  122. $this->_messageNum = $params['id'];
  123. }
  124. if (isset($params['partclass'])) {
  125. $this->setPartClass($params['partclass']);
  126. }
  127. if (isset($params['raw'])) {
  128. Zend_Mime_Decode::splitMessage($params['raw'], $this->_headers, $this->_content, "\r\n");
  129. } else if (isset($params['headers'])) {
  130. if (is_array($params['headers'])) {
  131. $this->_headers = $params['headers'];
  132. $this->_validateHeaders($this->_headers);
  133. } else {
  134. if (!empty($params['noToplines'])) {
  135. Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $null, "\r\n");
  136. } else {
  137. Zend_Mime_Decode::splitMessage($params['headers'], $this->_headers, $this->_topLines, "\r\n");
  138. }
  139. }
  140. if (isset($params['content'])) {
  141. $this->_content = $params['content'];
  142. }
  143. }
  144. }
  145. /**
  146. * Set name pf class used to encapsulate message parts
  147. * @param string $class
  148. * @return Zend_Mail_Part
  149. */
  150. public function setPartClass($class)
  151. {
  152. if ( !class_exists($class) ) {
  153. /**
  154. * @see Zend_Mail_Exception
  155. */
  156. require_once 'Zend/Mail/Exception.php';
  157. throw new Zend_Mail_Exception("Class '{$class}' does not exist");
  158. }
  159. if ( !is_subclass_of($class, 'Zend_Mail_Part_Interface') ) {
  160. /**
  161. * @see Zend_Mail_Exception
  162. */
  163. require_once 'Zend/Mail/Exception.php';
  164. throw new Zend_Mail_Exception("Class '{$class}' must implement Zend_Mail_Part_Interface");
  165. }
  166. $this->_partClass = $class;
  167. return $this;
  168. }
  169. /**
  170. * Retrieve the class name used to encapsulate message parts
  171. * @return string
  172. */
  173. public function getPartClass()
  174. {
  175. if ( !$this->_partClass ) {
  176. $this->_partClass = __CLASS__;
  177. }
  178. return $this->_partClass;
  179. }
  180. /**
  181. * Check if part is a multipart message
  182. *
  183. * @return bool if part is multipart
  184. */
  185. public function isMultipart()
  186. {
  187. try {
  188. return stripos($this->contentType, 'multipart/') === 0;
  189. } catch(Zend_Mail_Exception $e) {
  190. return false;
  191. }
  192. }
  193. /**
  194. * Body of part
  195. *
  196. * If part is multipart the raw content of this part with all sub parts is returned
  197. *
  198. * @return string body
  199. * @throws Zend_Mail_Exception
  200. */
  201. public function getContent()
  202. {
  203. if ($this->_content !== null) {
  204. return $this->_content;
  205. }
  206. if ($this->_mail) {
  207. return $this->_mail->getRawContent($this->_messageNum);
  208. } else {
  209. /**
  210. * @see Zend_Mail_Exception
  211. */
  212. require_once 'Zend/Mail/Exception.php';
  213. throw new Zend_Mail_Exception('no content');
  214. }
  215. }
  216. /**
  217. * Return size of part
  218. *
  219. * Quite simple implemented currently (not decoding). Handle with care.
  220. *
  221. * @return int size
  222. */
  223. public function getSize() {
  224. return strlen($this->getContent());
  225. }
  226. /**
  227. * Cache content and split in parts if multipart
  228. *
  229. * @return null
  230. * @throws Zend_Mail_Exception
  231. */
  232. protected function _cacheContent()
  233. {
  234. // caching content if we can't fetch parts
  235. if ($this->_content === null && $this->_mail) {
  236. $this->_content = $this->_mail->getRawContent($this->_messageNum);
  237. }
  238. if (!$this->isMultipart()) {
  239. return;
  240. }
  241. // split content in parts
  242. $boundary = $this->getHeaderField('content-type', 'boundary');
  243. if (!$boundary) {
  244. /**
  245. * @see Zend_Mail_Exception
  246. */
  247. require_once 'Zend/Mail/Exception.php';
  248. throw new Zend_Mail_Exception('no boundary found in content type to split message');
  249. }
  250. $parts = Zend_Mime_Decode::splitMessageStruct($this->_content, $boundary);
  251. if ($parts === null) {
  252. return;
  253. }
  254. $partClass = $this->getPartClass();
  255. $counter = 1;
  256. foreach ($parts as $part) {
  257. $this->_parts[$counter++] = new $partClass(array('headers' => $part['header'], 'content' => $part['body']));
  258. }
  259. }
  260. /**
  261. * Get part of multipart message
  262. *
  263. * @param int $num number of part starting with 1 for first part
  264. * @return Zend_Mail_Part wanted part
  265. * @throws Zend_Mail_Exception
  266. */
  267. public function getPart($num)
  268. {
  269. if (isset($this->_parts[$num])) {
  270. return $this->_parts[$num];
  271. }
  272. if (!$this->_mail && $this->_content === null) {
  273. /**
  274. * @see Zend_Mail_Exception
  275. */
  276. require_once 'Zend/Mail/Exception.php';
  277. throw new Zend_Mail_Exception('part not found');
  278. }
  279. if ($this->_mail && $this->_mail->hasFetchPart) {
  280. // TODO: fetch part
  281. // return
  282. }
  283. $this->_cacheContent();
  284. if (!isset($this->_parts[$num])) {
  285. /**
  286. * @see Zend_Mail_Exception
  287. */
  288. require_once 'Zend/Mail/Exception.php';
  289. throw new Zend_Mail_Exception('part not found');
  290. }
  291. return $this->_parts[$num];
  292. }
  293. /**
  294. * Count parts of a multipart part
  295. *
  296. * @return int number of sub-parts
  297. */
  298. public function countParts()
  299. {
  300. if ($this->_countParts) {
  301. return $this->_countParts;
  302. }
  303. $this->_countParts = count($this->_parts);
  304. if ($this->_countParts) {
  305. return $this->_countParts;
  306. }
  307. if ($this->_mail && $this->_mail->hasFetchPart) {
  308. // TODO: fetch part
  309. // return
  310. }
  311. $this->_cacheContent();
  312. $this->_countParts = count($this->_parts);
  313. return $this->_countParts;
  314. }
  315. /**
  316. * Get all headers
  317. *
  318. * The returned headers are as saved internally. All names are lowercased. The value is a string or an array
  319. * if a header with the same name occurs more than once.
  320. *
  321. * @return array headers as array(name => value)
  322. */
  323. public function getHeaders()
  324. {
  325. if ($this->_headers === null) {
  326. if (!$this->_mail) {
  327. $this->_headers = array();
  328. } else {
  329. $part = $this->_mail->getRawHeader($this->_messageNum);
  330. Zend_Mime_Decode::splitMessage($part, $this->_headers, $null);
  331. }
  332. }
  333. return $this->_headers;
  334. }
  335. /**
  336. * Get a header in specificed format
  337. *
  338. * Internally headers that occur more than once are saved as array, all other as string. If $format
  339. * is set to string implode is used to concat the values (with Zend_Mime::LINEEND as delim).
  340. *
  341. * @param string $name name of header, matches case-insensitive, but camel-case is replaced with dashes
  342. * @param string $format change type of return value to 'string' or 'array'
  343. * @return string|array value of header in wanted or internal format
  344. * @throws Zend_Mail_Exception
  345. */
  346. public function getHeader($name, $format = null)
  347. {
  348. if ($this->_headers === null) {
  349. $this->getHeaders();
  350. }
  351. $lowerName = strtolower($name);
  352. if ($this->headerExists($name) == false) {
  353. $lowerName = strtolower(preg_replace('%([a-z])([A-Z])%', '\1-\2', $name));
  354. if($this->headerExists($lowerName) == false) {
  355. /**
  356. * @see Zend_Mail_Exception
  357. */
  358. require_once 'Zend/Mail/Exception.php';
  359. throw new Zend_Mail_Exception("no Header with Name $name or $lowerName found");
  360. }
  361. }
  362. $name = $lowerName;
  363. $header = $this->_headers[$name];
  364. switch ($format) {
  365. case 'string':
  366. if (is_array($header)) {
  367. $header = implode(Zend_Mime::LINEEND, $header);
  368. }
  369. break;
  370. case 'array':
  371. $header = (array)$header;
  372. default:
  373. // do nothing
  374. }
  375. return $header;
  376. }
  377. /**
  378. * Check wheater the Mail part has a specific header.
  379. *
  380. * @param string $name
  381. * @return boolean
  382. */
  383. public function headerExists($name)
  384. {
  385. $name = strtolower($name);
  386. if(isset($this->_headers[$name])) {
  387. return true;
  388. } else {
  389. return false;
  390. }
  391. }
  392. /**
  393. * Get a specific field from a header like content type or all fields as array
  394. *
  395. * If the header occurs more than once, only the value from the first header
  396. * is returned.
  397. *
  398. * Throws a Zend_Mail_Exception if the requested header does not exist. If
  399. * the specific header field does not exist, returns null.
  400. *
  401. * @param string $name name of header, like in getHeader()
  402. * @param string $wantedPart the wanted part, default is first, if null an array with all parts is returned
  403. * @param string $firstName key name for the first part
  404. * @return string|array wanted part or all parts as array($firstName => firstPart, partname => value)
  405. * @throws Zend_Exception, Zend_Mail_Exception
  406. */
  407. public function getHeaderField($name, $wantedPart = 0, $firstName = 0) {
  408. return Zend_Mime_Decode::splitHeaderField(current($this->getHeader($name, 'array')), $wantedPart, $firstName);
  409. }
  410. /**
  411. * Getter for mail headers - name is matched in lowercase
  412. *
  413. * This getter is short for Zend_Mail_Part::getHeader($name, 'string')
  414. *
  415. * @see Zend_Mail_Part::getHeader()
  416. *
  417. * @param string $name header name
  418. * @return string value of header
  419. * @throws Zend_Mail_Exception
  420. */
  421. public function __get($name)
  422. {
  423. return $this->getHeader($name, 'string');
  424. }
  425. /**
  426. * Isset magic method proxy to hasHeader
  427. *
  428. * This method is short syntax for Zend_Mail_Part::hasHeader($name);
  429. *
  430. * @see Zend_Mail_Part::hasHeader
  431. *
  432. * @param string
  433. * @return boolean
  434. */
  435. public function __isset($name)
  436. {
  437. return $this->headerExists($name);
  438. }
  439. /**
  440. * magic method to get content of part
  441. *
  442. * @return string content
  443. */
  444. public function __toString()
  445. {
  446. return $this->getContent();
  447. }
  448. /**
  449. * implements RecursiveIterator::hasChildren()
  450. *
  451. * @return bool current element has children/is multipart
  452. */
  453. public function hasChildren()
  454. {
  455. $current = $this->current();
  456. return $current && $current instanceof Zend_Mail_Part && $current->isMultipart();
  457. }
  458. /**
  459. * implements RecursiveIterator::getChildren()
  460. *
  461. * @return Zend_Mail_Part same as self::current()
  462. */
  463. public function getChildren()
  464. {
  465. return $this->current();
  466. }
  467. /**
  468. * implements Iterator::valid()
  469. *
  470. * @return bool check if there's a current element
  471. */
  472. public function valid()
  473. {
  474. if ($this->_countParts === null) {
  475. $this->countParts();
  476. }
  477. return $this->_iterationPos && $this->_iterationPos <= $this->_countParts;
  478. }
  479. /**
  480. * implements Iterator::next()
  481. *
  482. * @return null
  483. */
  484. public function next()
  485. {
  486. ++$this->_iterationPos;
  487. }
  488. /**
  489. * implements Iterator::key()
  490. *
  491. * @return string key/number of current part
  492. */
  493. public function key()
  494. {
  495. return $this->_iterationPos;
  496. }
  497. /**
  498. * implements Iterator::current()
  499. *
  500. * @return Zend_Mail_Part current part
  501. */
  502. public function current()
  503. {
  504. return $this->getPart($this->_iterationPos);
  505. }
  506. /**
  507. * implements Iterator::rewind()
  508. *
  509. * @return null
  510. */
  511. public function rewind()
  512. {
  513. $this->countParts();
  514. $this->_iterationPos = 1;
  515. }
  516. /**
  517. * Ensure headers do not contain invalid characters
  518. *
  519. * @param array $headers
  520. * @param bool $assertNames
  521. */
  522. protected function _validateHeaders(array $headers, $assertNames = true)
  523. {
  524. foreach ($headers as $name => $value) {
  525. if ($assertNames) {
  526. Zend_Mail_Header_HeaderName::assertValid($name);
  527. }
  528. if (is_array($value)) {
  529. $this->_validateHeaders($value, false);
  530. continue;
  531. }
  532. Zend_Mail_Header_HeaderValue::assertValid($value);
  533. }
  534. }
  535. }