PageRenderTime 23ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Part.php

https://gitlab.com/wuhang2003/rainloop-webmail
PHP | 665 lines | 457 code | 95 blank | 113 comment | 54 complexity | f1e0facfd48896a9b103acd8d824f048 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of MailSo.
  4. *
  5. * (c) 2014 Usenko Timur
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace MailSo\Mime;
  11. /**
  12. * @category MailSo
  13. * @package Mime
  14. */
  15. class Part
  16. {
  17. const POS_HEADERS = 1;
  18. const POS_BODY = 2;
  19. const POS_SUBPARTS = 3;
  20. const POS_CLOSE_BOUNDARY = 4;
  21. const DEFAUL_BUFFER = 8192;
  22. /**
  23. * @var string
  24. */
  25. public static $DefaultCharset = \MailSo\Base\Enumerations\Charset::ISO_8859_1;
  26. /**
  27. * @var string
  28. */
  29. public static $ForceCharset = '';
  30. /**
  31. * @var \MailSo\Mime\HeaderCollection
  32. */
  33. public $Headers;
  34. /**
  35. * @var resource
  36. */
  37. public $Body;
  38. /**
  39. * @var \MailSo\Mime\PartCollection
  40. */
  41. public $SubParts;
  42. /**
  43. * @var array
  44. */
  45. public $LineParts;
  46. /**
  47. * @var string
  48. */
  49. private $sBoundary;
  50. /**
  51. * @var string
  52. */
  53. private $sParentCharset;
  54. /**
  55. * @var int
  56. */
  57. private $iParseBuffer;
  58. /**
  59. * @access private
  60. */
  61. private function __construct()
  62. {
  63. $this->iParseBuffer = \MailSo\Mime\Part::DEFAUL_BUFFER;
  64. $this->Reset();
  65. }
  66. /**
  67. * @return \MailSo\Mime\Part
  68. */
  69. public static function NewInstance()
  70. {
  71. return new self();
  72. }
  73. /**
  74. * @return \MailSo\Mime\Part
  75. */
  76. public function Reset()
  77. {
  78. \MailSo\Base\ResourceRegistry::CloseMemoryResource($this->Body);
  79. $this->Body = null;
  80. $this->Headers = HeaderCollection::NewInstance();
  81. $this->SubParts = PartCollection::NewInstance();
  82. $this->LineParts = array();
  83. $this->sBoundary = '';
  84. $this->sParentCharset = \MailSo\Base\Enumerations\Charset::ISO_8859_1;
  85. return $this;
  86. }
  87. /**
  88. * @return string
  89. */
  90. public function Boundary()
  91. {
  92. return $this->sBoundary;
  93. }
  94. /**
  95. * @return string
  96. */
  97. public function ParentCharset()
  98. {
  99. return (0 < \strlen($this->sCharset)) ? $this->sParentCharset : self::$DefaultCharset;
  100. }
  101. /**
  102. * @param string $sParentCharset
  103. * @return \MailSo\Mime\Part
  104. */
  105. public function SetParentCharset($sParentCharset)
  106. {
  107. $this->sParentCharset = $sParentCharset;
  108. return $this;
  109. }
  110. /**
  111. * @param string $sBoundary
  112. * @return \MailSo\Mime\Part
  113. */
  114. public function SetBoundary($sBoundary)
  115. {
  116. $this->sBoundary = $sBoundary;
  117. return $this;
  118. }
  119. /**
  120. * @param int $iParseBuffer
  121. * @return \MailSo\Mime\Part
  122. */
  123. public function SetParseBuffer($iParseBuffer)
  124. {
  125. $this->iParseBuffer = $iParseBuffer;
  126. return $this;
  127. }
  128. /**
  129. * @return string
  130. */
  131. public function HeaderCharset()
  132. {
  133. return ($this->Headers) ? trim(strtolower($this->Headers->ParameterValue(
  134. \MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
  135. \MailSo\Mime\Enumerations\Parameter::CHARSET))) : '';
  136. }
  137. /**
  138. * @return string
  139. */
  140. public function HeaderBoundary()
  141. {
  142. return ($this->Headers) ? trim($this->Headers->ParameterValue(
  143. \MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
  144. \MailSo\Mime\Enumerations\Parameter::BOUNDARY)) : '';
  145. }
  146. /**
  147. * @return string
  148. */
  149. public function ContentType()
  150. {
  151. return ($this->Headers) ?
  152. trim(strtolower($this->Headers->ValueByName(
  153. \MailSo\Mime\Enumerations\Header::CONTENT_TYPE))) : '';
  154. }
  155. /**
  156. * @return string
  157. */
  158. public function ContentTransferEncoding()
  159. {
  160. return ($this->Headers) ?
  161. trim(strtolower($this->Headers->ValueByName(
  162. \MailSo\Mime\Enumerations\Header::CONTENT_TRANSFER_ENCODING))) : '';
  163. }
  164. /**
  165. * @return string
  166. */
  167. public function ContentID()
  168. {
  169. return ($this->Headers) ? trim($this->Headers->ValueByName(
  170. \MailSo\Mime\Enumerations\Header::CONTENT_ID)) : '';
  171. }
  172. /**
  173. * @return string
  174. */
  175. public function ContentLocation()
  176. {
  177. return ($this->Headers) ? trim($this->Headers->ValueByName(
  178. \MailSo\Mime\Enumerations\Header::CONTENT_LOCATION)) : '';
  179. }
  180. /**
  181. * @return bool
  182. */
  183. public function IsFlowedFormat()
  184. {
  185. $bResult = false;
  186. if ($this->Headers)
  187. {
  188. $bResult = 'flowed' === \trim(\strtolower($this->Headers->ParameterValue(
  189. \MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
  190. \MailSo\Mime\Enumerations\Parameter::FORMAT)));
  191. if ($bResult && \in_array(\strtolower($this->MailEncodingName()), array('base64', 'quoted-printable')))
  192. {
  193. $bResult = false;
  194. }
  195. }
  196. return $bResult;
  197. }
  198. /**
  199. * @return string
  200. */
  201. public function FileName()
  202. {
  203. $sResult = '';
  204. if ($this->Headers)
  205. {
  206. $sResult = trim($this->Headers->ParameterValue(
  207. \MailSo\Mime\Enumerations\Header::CONTENT_DISPOSITION,
  208. \MailSo\Mime\Enumerations\Parameter::FILENAME));
  209. if (0 === strlen($sResult))
  210. {
  211. $sResult = trim($this->Headers->ParameterValue(
  212. \MailSo\Mime\Enumerations\Header::CONTENT_TYPE,
  213. \MailSo\Mime\Enumerations\Parameter::NAME));
  214. }
  215. }
  216. return $sResult;
  217. }
  218. /**
  219. * @param string $sFileName
  220. * @return \MailSo\Mime\Part
  221. */
  222. public function ParseFromFile($sFileName)
  223. {
  224. $rStreamHandle = (@file_exists($sFileName)) ? @fopen($sFileName, 'rb') : false;
  225. if (is_resource($rStreamHandle))
  226. {
  227. $this->ParseFromStream($rStreamHandle);
  228. if (is_resource($rStreamHandle))
  229. {
  230. fclose($rStreamHandle);
  231. }
  232. }
  233. return $this;
  234. }
  235. /**
  236. * @param string $sRawMessage
  237. * @return \MailSo\Mime\Part
  238. */
  239. public function ParseFromString($sRawMessage)
  240. {
  241. $rStreamHandle = (0 < strlen($sRawMessage)) ?
  242. \MailSo\Base\ResourceRegistry::CreateMemoryResource() : false;
  243. if (is_resource($rStreamHandle))
  244. {
  245. fwrite($rStreamHandle, $sRawMessage);
  246. unset($sRawMessage);
  247. fseek($rStreamHandle, 0);
  248. $this->ParseFromStream($rStreamHandle);
  249. \MailSo\Base\ResourceRegistry::CloseMemoryResource($rStreamHandle);
  250. }
  251. return $this;
  252. }
  253. /**
  254. * @param resource $rStreamHandle
  255. * @return \MailSo\Mime\Part
  256. */
  257. public function ParseFromStream($rStreamHandle)
  258. {
  259. $this->Reset();
  260. $oParserClass = new \MailSo\Mime\Parser\ParserMemory();
  261. $oMimePart = null;
  262. $bIsOef = false;
  263. $iOffset = 0;
  264. $sBuffer = '';
  265. $sPrevBuffer = '';
  266. $aBoundaryStack = array();
  267. $oParserClass->StartParse($this);
  268. $this->LineParts[] =& $this;
  269. $this->ParseFromStreamRecursion($rStreamHandle, $oParserClass, $iOffset,
  270. $sPrevBuffer, $sBuffer, $aBoundaryStack, $bIsOef);
  271. $sFirstNotNullCharset = null;
  272. foreach ($this->LineParts as /* @var $oMimePart \MailSo\Mime\Part */ &$oMimePart)
  273. {
  274. $sCharset = $oMimePart->HeaderCharset();
  275. if (0 < strlen($sCharset))
  276. {
  277. $sFirstNotNullCharset = $sCharset;
  278. break;
  279. }
  280. }
  281. $sForceCharset = self::$ForceCharset;
  282. if (0 < strlen($sForceCharset))
  283. {
  284. foreach ($this->LineParts as /* @var $oMimePart \MailSo\Mime\Part */ &$oMimePart)
  285. {
  286. $oMimePart->SetParentCharset($sForceCharset);
  287. $oMimePart->Headers->SetParentCharset($sForceCharset);
  288. }
  289. }
  290. else
  291. {
  292. $sFirstNotNullCharset = (null !== $sFirstNotNullCharset)
  293. ? $sFirstNotNullCharset : self::$DefaultCharset;
  294. foreach ($this->LineParts as /* @var $oMimePart \MailSo\Mime\Part */ &$oMimePart)
  295. {
  296. $sHeaderCharset = $oMimePart->HeaderCharset();
  297. $oMimePart->SetParentCharset((0 < strlen($sHeaderCharset)) ? $sHeaderCharset : $sFirstNotNullCharset);
  298. $oMimePart->Headers->SetParentCharset($sHeaderCharset);
  299. }
  300. }
  301. $oParserClass->EndParse($this);
  302. return $this;
  303. }
  304. /**
  305. * @param resource $rStreamHandle
  306. * @return \MailSo\Mime\Part
  307. */
  308. public function ParseFromStreamRecursion($rStreamHandle, &$oCallbackClass, &$iOffset,
  309. &$sPrevBuffer, &$sBuffer, &$aBoundaryStack, &$bIsOef, $bNotFirstRead = false)
  310. {
  311. $oCallbackClass->StartParseMimePart($this);
  312. $iPos = 0;
  313. $iParsePosition = self::POS_HEADERS;
  314. $sCurrentBoundary = '';
  315. $bIsBoundaryCheck = false;
  316. $aHeadersLines = array();
  317. while (true)
  318. {
  319. if (!$bNotFirstRead)
  320. {
  321. $sPrevBuffer = $sBuffer;
  322. $sBuffer = '';
  323. }
  324. if (!$bIsOef && !feof($rStreamHandle))
  325. {
  326. if (!$bNotFirstRead)
  327. {
  328. $sBuffer = @fread($rStreamHandle, $this->iParseBuffer);
  329. if (false === $sBuffer)
  330. {
  331. break;
  332. }
  333. $oCallbackClass->ReadBuffer($sBuffer);
  334. }
  335. else
  336. {
  337. $bNotFirstRead = false;
  338. }
  339. }
  340. else if ($bIsOef && 0 === strlen($sBuffer))
  341. {
  342. break;
  343. }
  344. else
  345. {
  346. $bIsOef = true;
  347. }
  348. while (true)
  349. {
  350. $sCurrentLine = $sPrevBuffer.$sBuffer;
  351. if (self::POS_HEADERS === $iParsePosition)
  352. {
  353. $iEndLen = 4;
  354. $iPos = strpos($sCurrentLine, "\r\n\r\n", $iOffset);
  355. if (false === $iPos)
  356. {
  357. $iEndLen = 2;
  358. $iPos = strpos($sCurrentLine, "\n\n", $iOffset);
  359. }
  360. if (false !== $iPos)
  361. {
  362. $aHeadersLines[] = substr($sCurrentLine, $iOffset, $iPos + $iEndLen - $iOffset);
  363. $this->Headers->Parse(implode($aHeadersLines))->SetParentCharset($this->HeaderCharset());
  364. $aHeadersLines = array();
  365. $oCallbackClass->InitMimePartHeader();
  366. $sBoundary = $this->HeaderBoundary();
  367. if (0 < strlen($sBoundary))
  368. {
  369. $sBoundary = '--'.$sBoundary;
  370. $sCurrentBoundary = $sBoundary;
  371. array_unshift($aBoundaryStack, $sBoundary);
  372. }
  373. $iOffset = $iPos + $iEndLen;
  374. $iParsePosition = self::POS_BODY;
  375. continue;
  376. }
  377. else
  378. {
  379. $iBufferLen = strlen($sPrevBuffer);
  380. if ($iBufferLen > $iOffset)
  381. {
  382. $aHeadersLines[] = substr($sPrevBuffer, $iOffset);
  383. $iOffset = 0;
  384. }
  385. else
  386. {
  387. $iOffset -= $iBufferLen;
  388. }
  389. break;
  390. }
  391. }
  392. else if (self::POS_BODY === $iParsePosition)
  393. {
  394. $iPos = false;
  395. $sBoundaryLen = 0;
  396. $bIsBoundaryEnd = false;
  397. $bCurrentPartBody = false;
  398. $bIsBoundaryCheck = 0 < count($aBoundaryStack);
  399. foreach ($aBoundaryStack as $sKey => $sBoundary)
  400. {
  401. if (false !== ($iPos = strpos($sCurrentLine, $sBoundary, $iOffset)))
  402. {
  403. if ($sCurrentBoundary === $sBoundary)
  404. {
  405. $bCurrentPartBody = true;
  406. }
  407. $sBoundaryLen = strlen($sBoundary);
  408. if ('--' === substr($sCurrentLine, $iPos + $sBoundaryLen, 2))
  409. {
  410. $sBoundaryLen += 2;
  411. $bIsBoundaryEnd = true;
  412. unset($aBoundaryStack[$sKey]);
  413. $sCurrentBoundary = (isset($aBoundaryStack[$sKey + 1]))
  414. ? $aBoundaryStack[$sKey + 1] : '';
  415. }
  416. break;
  417. }
  418. }
  419. if (false !== $iPos)
  420. {
  421. $oCallbackClass->WriteBody(substr($sCurrentLine, $iOffset, $iPos - $iOffset));
  422. $iOffset = $iPos;
  423. if ($bCurrentPartBody)
  424. {
  425. $iParsePosition = self::POS_SUBPARTS;
  426. continue;
  427. }
  428. $oCallbackClass->EndParseMimePart($this);
  429. return true;
  430. }
  431. else
  432. {
  433. $iBufferLen = strlen($sPrevBuffer);
  434. if ($iBufferLen > $iOffset)
  435. {
  436. $oCallbackClass->WriteBody(substr($sPrevBuffer, $iOffset));
  437. $iOffset = 0;
  438. }
  439. else
  440. {
  441. $iOffset -= $iBufferLen;
  442. }
  443. break;
  444. }
  445. }
  446. else if (self::POS_SUBPARTS === $iParsePosition)
  447. {
  448. $iPos = false;
  449. $sBoundaryLen = 0;
  450. $bIsBoundaryEnd = false;
  451. $bCurrentPartBody = false;
  452. $bIsBoundaryCheck = 0 < count($aBoundaryStack);
  453. foreach ($aBoundaryStack as $sKey => $sBoundary)
  454. {
  455. if (false !== ($iPos = strpos($sCurrentLine, $sBoundary, $iOffset)))
  456. {
  457. if ($sCurrentBoundary === $sBoundary)
  458. {
  459. $bCurrentPartBody = true;
  460. }
  461. $sBoundaryLen = strlen($sBoundary);
  462. if ('--' === substr($sCurrentLine, $iPos + $sBoundaryLen, 2))
  463. {
  464. $sBoundaryLen += 2;
  465. $bIsBoundaryEnd = true;
  466. unset($aBoundaryStack[$sKey]);
  467. $sCurrentBoundary = (isset($aBoundaryStack[$sKey + 1]))
  468. ? $aBoundaryStack[$sKey + 1] : '';
  469. }
  470. break;
  471. }
  472. }
  473. if (false !== $iPos && $bCurrentPartBody)
  474. {
  475. $iOffset = $iPos + $sBoundaryLen;
  476. $oSubPart = self::NewInstance();
  477. $oSubPart
  478. ->SetParseBuffer($this->iParseBuffer)
  479. ->ParseFromStreamRecursion($rStreamHandle, $oCallbackClass,
  480. $iOffset, $sPrevBuffer, $sBuffer, $aBoundaryStack, $bIsOef, true);
  481. $this->SubParts->Add($oSubPart);
  482. $this->LineParts[] =& $oSubPart;
  483. //$iParsePosition = self::POS_HEADERS;
  484. unset($oSubPart);
  485. }
  486. else
  487. {
  488. $oCallbackClass->EndParseMimePart($this);
  489. return true;
  490. }
  491. }
  492. }
  493. }
  494. if (0 < strlen($sPrevBuffer))
  495. {
  496. if (self::POS_HEADERS === $iParsePosition)
  497. {
  498. $aHeadersLines[] = ($iOffset < strlen($sPrevBuffer))
  499. ? substr($sPrevBuffer, $iOffset)
  500. : $sPrevBuffer;
  501. $this->Headers->Parse(implode($aHeadersLines))->SetParentCharset($this->HeaderCharset());
  502. $aHeadersLines = array();
  503. $oCallbackClass->InitMimePartHeader();
  504. }
  505. else if (self::POS_BODY === $iParsePosition)
  506. {
  507. if (!$bIsBoundaryCheck)
  508. {
  509. $oCallbackClass->WriteBody(($iOffset < strlen($sPrevBuffer))
  510. ? substr($sPrevBuffer, $iOffset) : $sPrevBuffer);
  511. }
  512. }
  513. }
  514. else
  515. {
  516. if (self::POS_HEADERS === $iParsePosition && 0 < count($aHeadersLines))
  517. {
  518. $this->Headers->Parse(implode($aHeadersLines))->SetParentCharset($this->HeaderCharset());
  519. $aHeadersLines = array();
  520. $oCallbackClass->InitMimePartHeader();
  521. }
  522. }
  523. $oCallbackClass->EndParseMimePart($this);
  524. return $this;
  525. }
  526. /**
  527. * @return resorce
  528. */
  529. public function Rewind()
  530. {
  531. if ($this->Body && \is_resource($this->Body))
  532. {
  533. $aMeta = \stream_get_meta_data($this->Body);
  534. if (isset($aMeta['seekable']) && $aMeta['seekable'])
  535. {
  536. \rewind($this->Body);
  537. }
  538. }
  539. }
  540. /**
  541. * @return resorce
  542. */
  543. public function ToStream()
  544. {
  545. $this->Rewind();
  546. $aSubStreams = array(
  547. $this->Headers->ToEncodedString().
  548. \MailSo\Mime\Enumerations\Constants::CRLF.
  549. \MailSo\Mime\Enumerations\Constants::CRLF,
  550. null === $this->Body ? '' : $this->Body,
  551. \MailSo\Mime\Enumerations\Constants::CRLF
  552. );
  553. if (0 < $this->SubParts->Count())
  554. {
  555. $sBoundary = $this->HeaderBoundary();
  556. if (0 < strlen($sBoundary))
  557. {
  558. $aSubStreams[] = '--'.$sBoundary.\MailSo\Mime\Enumerations\Constants::CRLF;
  559. $rSubPartsStream = $this->SubParts->ToStream($sBoundary);
  560. if (is_resource($rSubPartsStream))
  561. {
  562. $aSubStreams[] = $rSubPartsStream;
  563. }
  564. $aSubStreams[] = \MailSo\Mime\Enumerations\Constants::CRLF.
  565. '--'.$sBoundary.'--'.\MailSo\Mime\Enumerations\Constants::CRLF;
  566. }
  567. }
  568. return \MailSo\Base\StreamWrappers\SubStreams::CreateStream($aSubStreams);
  569. }
  570. }