Source for file Message.class.php

Documentation is available at Message.class.php

  1. <?php
  2. /**
  3. * Message.class.php
  4. *
  5. * This file contains functions needed to handle mime messages.
  6. *
  7. * @copyright 2003-2020 The SquirrelMail Project Team
  8. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  9. * @version $Id: Message.class.php 14840 2020年01月07日 07:42:38Z pdontthink $
  10. * @package squirrelmail
  11. * @subpackage mime
  12. * @since 1.3.2
  13. */
  14. /**
  15. * The object that contains a message.
  16. *
  17. * message is the object that contains messages. It is a recursive object in
  18. * that through the $entities variable, it can contain more objects of type
  19. * message. See documentation in mime.txt for a better description of how this
  20. * works.
  21. * @package squirrelmail
  22. * @subpackage mime
  23. * @since 1.3.0
  24. */
  25. class Message {
  26. /**
  27. * rfc822header object
  28. * @var object
  29. */
  30. var $rfc822_header = '';
  31. /**
  32. * Headers from original email in reply
  33. * @var string
  34. */
  35. /**
  36. * MessageHeader object
  37. * @var object
  38. */
  39. var $mime_header = '';
  40. /**
  41. * @var mixed
  42. */
  43. var $flags = '';
  44. /**
  45. * Media type
  46. * @var string
  47. */
  48. var $type0='';
  49. /**
  50. * Media subtype
  51. * @var string
  52. */
  53. var $type1='';
  54. /**
  55. * Nested mime parts
  56. * @var array
  57. */
  58. var $entities = array();
  59. /**
  60. * Message part id
  61. * @var string
  62. */
  63. var $entity_id = '';
  64. /**
  65. * Parent message part id
  66. * @var string
  67. */
  68. /**
  69. * @var mixed
  70. */
  71. var $entity ;
  72. /**
  73. * @var mixed
  74. */
  75. var $parent = '';
  76. /**
  77. * @var string
  78. */
  79. var $decoded_body='';
  80. /**
  81. * Message \seen status
  82. * @var boolean
  83. */
  84. var $is_seen = 0;
  85. /**
  86. * Message \answered status
  87. * @var boolean
  88. */
  89. var $is_answered = 0;
  90. /**
  91. * Message \deleted status
  92. * @var boolean
  93. */
  94. var $is_deleted = 0;
  95. /**
  96. * Message \flagged status
  97. * @var boolean
  98. */
  99. var $is_flagged = 0;
  100. /**
  101. * Message mdn status
  102. * @var boolean
  103. */
  104. var $is_mdnsent = 0;
  105. /**
  106. * Message text body
  107. * @var string
  108. */
  109. var $body_part = '';
  110. /**
  111. * Message part offset
  112. * for fetching body parts out of raw messages
  113. * @var integer
  114. */
  115. var $offset = 0;
  116. /**
  117. * Message part length
  118. * for fetching body parts out of raw messages
  119. * @var integer
  120. */
  121. var $length = 0;
  122. /**
  123. * Local attachment filename location where the tempory attachment is
  124. * stored. For use in delivery class.
  125. * @var string
  126. */
  127. var $att_local_name = '';
  128. /**
  129. * @param string $ent entity id
  130. */
  131. function setEnt ($ent) {
  132. $this->entity_id= $ent;
  133. }
  134. /**
  135. * Add nested message part
  136. * @param object $msg
  137. */
  138. function addEntity ($msg) {
  139. $this->entities [] = $msg;
  140. }
  141. /**
  142. * Get file name used for mime part
  143. * @return string file name
  144. * @since 1.3.2
  145. */
  146. function getFilename () {
  147. $filename = '';
  148. $header = $this->header;
  149. if (is_object ($header->disposition)) {
  150. $filename = $header->disposition->getProperty('filename');
  151. if (trim ($filename) == '') {
  152. $name = decodeHeader ($header->disposition->getProperty('name'));
  153. if (!trim ($name)) {
  154. $name = $header->getParameter('name');
  155. if(!trim ($name)) {
  156. if (!trim ( $header->id )) {
  157. $filename = 'untitled-[' . $this->entity_id . ']' . '.' . strtolower ($header->type1);
  158. } else {
  159. $filename = 'cid: ' . $header->id . '.' . strtolower ($header->type1);
  160. }
  161. } else {
  162. $filename = $name;
  163. }
  164. } else {
  165. $filename = $name;
  166. }
  167. }
  168. } else {
  169. $filename = $header->getParameter('filename');
  170. if (!trim ($filename)) {
  171. $filename = $header->getParameter('name');
  172. if (!trim ($filename)) {
  173. if (!trim ( $header->id )) {
  174. $filename = 'untitled-[' . $this->entity_id . ']' . '.' . strtolower ($header->type1);
  175. } else {
  176. $filename = 'cid: ' . $header->id . '.' . strtolower ($header->type1);
  177. }
  178. }
  179. }
  180. }
  181. return $filename;
  182. }
  183. /**
  184. * Add header object to message object.
  185. * WARNING: Unfinished code. Don't expect it to work in older sm versions.
  186. * @param mixed $read array or string with message headers
  187. * @todo FIXME: rfc822header->parseHeader() does not return rfc822header object
  188. */
  189. function addRFC822Header ($read) {
  190. $header = new Rfc822Header ();
  191. $this->rfc822_header = $header->parseHeader($read);
  192. }
  193. /**
  194. * @param string $ent
  195. * @return mixed (object or string?)
  196. */
  197. function getEntity ($ent) {
  198. $cur_ent = $this->entity_id ;
  199. $msg = $this;
  200. if (($cur_ent == '') || ($cur_ent == '0')) {
  201. $cur_ent_a = array();
  202. } else {
  203. $cur_ent_a = explode ('.', $this->entity_id );
  204. }
  205. $ent_a = explode ('.', $ent);
  206. for ($i = 0,$entCount = count ($ent_a) - 1; $i < $entCount; ++$i) {
  207. if (isset($cur_ent_a[$i]) && ($cur_ent_a[$i] != $ent_a[$i])) {
  208. $msg = $msg->parent;
  209. $cur_ent_a = explode ('.', $msg->entity_id);
  210. --$i;
  211. } else if (!isset($cur_ent_a[$i])) {
  212. if (isset($msg->entities[($ent_a[$i]-1)])) {
  213. $msg = $msg->entities[($ent_a[$i]-1)];
  214. } else {
  215. $msg = $msg->entities[0];
  216. }
  217. }
  218. if (($msg->type0 == 'message') && ($msg->type1 == 'rfc822')) {
  219. /*this is a header for a message/rfc822 entity */
  220. $msg = $msg->entities[0];
  221. }
  222. }
  223. if (($msg->type0 == 'message') && ($msg->type1 == 'rfc822')) {
  224. /*this is a header for a message/rfc822 entity */
  225. $msg = $msg->entities[0];
  226. }
  227. if (isset($msg->entities[($ent_a[$entCount])-1])) {
  228. if (is_object ($msg->entities[($ent_a[$entCount])-1])) {
  229. $msg = $msg->entities[($ent_a[$entCount]-1)];
  230. }
  231. }
  232. return $msg;
  233. }
  234. /**
  235. * Set message body
  236. * @param string $s message body
  237. */
  238. function setBody ($s) {
  239. $this->body_part = $s;
  240. }
  241. /**
  242. * Clean message object
  243. */
  244. function clean_up () {
  245. $msg = $this;
  246. $msg->body_part = '';
  247. foreach ($msg->entities as $m) {
  248. $m->clean_up();
  249. }
  250. }
  251. /**
  252. * @return string
  253. */
  254. function getMailbox () {
  255. $msg = $this;
  256. while (is_object ($msg->parent)) {
  257. $msg = $msg->parent;
  258. }
  259. return $msg->mailbox;
  260. }
  261. /*
  262. * Bodystructure parser, a recursive function for generating the
  263. * entity-tree with all the mime-parts.
  264. *
  265. * It follows RFC2060 and stores all the described fields in the
  266. * message object.
  267. *
  268. * Question/Bugs:
  269. *
  270. * Ask for me (Marc Groot Koerkamp, [email protected])
  271. * @param string $read
  272. * @param integer $i
  273. * @param mixed $sub_msg
  274. * @return object Message object
  275. * @todo define argument and return types
  276. */
  277. static function parseStructure ($read, &$i, $sub_msg = '') {
  278. $msg = Message ::parseBodyStructure ($read, $i, $sub_msg);
  279. if($msg) $msg->setEntIds($msg,false,0);
  280. return $msg;
  281. }
  282. /**
  283. * @param object $msg
  284. * @param mixed $init
  285. * @param integer $i
  286. * @todo document me
  287. * @since 1.4.0
  288. */
  289. function setEntIds (&$msg,$init=false,$i=0) {
  290. $iCnt = count ($msg->entities);
  291. if ($init !==false) {
  292. $iEntSub = $i+1;
  293. if ($msg->parent->type0 == 'message' &&
  294. $msg->parent->type1 == 'rfc822' &&
  295. $msg->type0 == 'multipart') {
  296. $iEntSub = '0';
  297. }
  298. if ($init) {
  299. $msg->entity_id = "$init.$iEntSub";
  300. } else {
  301. $msg->entity_id = $iEntSub;
  302. }
  303. } else if ($iCnt) {
  304. $msg->entity_id='0';
  305. } else {
  306. $msg->entity_id='1';
  307. }
  308. for ($i=0;$i<$iCnt;++$i) {
  309. $msg->entities[$i]->parent =& $msg;
  310. if (strrchr ($msg->entity_id, '.') != '.0') {
  311. $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->entity_id,$i);
  312. } else {
  313. $msg->entities[$i]->setEntIds($msg->entities[$i],$msg->parent->entity_id,$i);
  314. }
  315. }
  316. }
  317. /**
  318. * @param string $read
  319. * @param integer $i
  320. * @param mixed $sub_msg
  321. * @return object Message object
  322. * @todo document me
  323. * @since 1.4.0 (code was part of parseStructure() in 1.3.x)
  324. */
  325. static function parseBodyStructure ($read, &$i, $sub_msg = '') {
  326. $arg_no = 0;
  327. $arg_a = array();
  328. if ($sub_msg) {
  329. $message = $sub_msg;
  330. } else {
  331. $message = new Message ();
  332. }
  333. for ($cnt = strlen ($read); $i < $cnt; ++$i) {
  334. $char = strtoupper ($read{$i});
  335. switch ($char) {
  336. case '(':
  337. switch($arg_no) {
  338. case 0:
  339. if (!isset($msg)) {
  340. $msg = new Message ();
  341. $hdr = new MessageHeader ();
  342. $hdr->type0 = 'text';
  343. $hdr->type1 = 'plain';
  344. $hdr->encoding = '7bit';
  345. $msg->header = $hdr;
  346. } else {
  347. $msg->header->type0 = 'multipart';
  348. $msg->type0 = 'multipart';
  349. while ($read{$i} == '(') {
  350. $msg->addEntity($msg->parseBodyStructure($read, $i, $msg));
  351. }
  352. }
  353. break;
  354. case 1:
  355. /* multipart properties */
  356. ++$i;
  357. $arg_a[] = $msg->parseProperties($read, $i);
  358. ++$arg_no;
  359. break;
  360. case 2:
  361. if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
  362. ++$i;
  363. $arg_a[] = $msg->parseDisposition($read, $i);
  364. } else { /* properties */
  365. $arg_a[] = $msg->parseProperties($read, $i);
  366. }
  367. ++$arg_no;
  368. break;
  369. case 3:
  370. if (isset($msg->type0) && ($msg->type0 == 'multipart')) {
  371. ++$i;
  372. $arg_a[]= $msg->parseLanguage($read, $i);
  373. }
  374. case 7:
  375. if (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')) {
  376. $msg->header->type0 = $arg_a[0];
  377. $msg->header->type1 = $arg_a[1];
  378. $msg->type0 = $arg_a[0];
  379. $msg->type1 = $arg_a[1];
  380. $rfc822_hdr = new Rfc822Header ();
  381. $msg->rfc822_header = $msg->parseEnvelope($read, $i, $rfc822_hdr);
  382. while (($i < $cnt) && ($read{$i} != '(')) {
  383. ++$i;
  384. }
  385. $msg->addEntity($msg->parseBodyStructure($read, $i,$msg));
  386. }
  387. break;
  388. case 8:
  389. ++$i;
  390. $arg_a[] = $msg->parseDisposition($read, $i);
  391. ++$arg_no;
  392. break;
  393. case 9:
  394. ++$i;
  395. if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
  396. $arg_a[] = $msg->parseDisposition($read, $i);
  397. } else {
  398. $arg_a[] = $msg->parseLanguage($read, $i);
  399. }
  400. ++$arg_no;
  401. break;
  402. case 10:
  403. if (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822'))) {
  404. ++$i;
  405. $arg_a[] = $msg->parseLanguage($read, $i);
  406. } else {
  407. $i = $msg->parseParenthesis($read, $i);
  408. $arg_a[] = ''; /* not yet described in rfc2060 */
  409. }
  410. ++$arg_no;
  411. break;
  412. default:
  413. /* unknown argument, skip this part */
  414. $i = $msg->parseParenthesis($read, $i);
  415. $arg_a[] = '';
  416. ++$arg_no;
  417. break;
  418. } /* switch */
  419. break;
  420. case '"':
  421. /* inside an entity -> start processing */
  422. $arg_s = $msg->parseQuote($read, $i);
  423. ++$arg_no;
  424. if ($arg_no < 3) {
  425. $arg_s = strtolower ($arg_s); /* type0 and type1 */
  426. }
  427. $arg_a[] = $arg_s;
  428. break;
  429. case 'n':
  430. case 'N':
  431. /* probably NIL argument */
  432. $tmpnil = strtoupper (substr ($read, $i, 4));
  433. if ($tmpnil == 'NIL ' || $tmpnil == 'NIL)') {
  434. $arg_a[] = '';
  435. ++$arg_no;
  436. $i += 2;
  437. }
  438. break;
  439. case '{':
  440. /* process the literal value */
  441. $arg_a[] = $msg->parseLiteral($read, $i);
  442. ++$arg_no;
  443. break;
  444. case '0':
  445. case is_numeric ($read{$i}):
  446. /* process integers */
  447. if ($read{$i} == ' ') { break; }
  448. ++$arg_no;
  449. if (preg_match ('/^([0-9]+).*/',substr ($read,$i), $regs)) {
  450. $i += strlen ($regs[1])-1;
  451. $arg_a[] = $regs[1];
  452. } else {
  453. $arg_a[] = 0;
  454. }
  455. break;
  456. case ')':
  457. $multipart = (isset($msg->type0) && ($msg->type0 == 'multipart'));
  458. if (!$multipart) {
  459. $shifted_args = (($arg_a[0] == 'text') || (($arg_a[0] == 'message') && ($arg_a[1] == 'rfc822')));
  460. $hdr->type0 = $arg_a[0];
  461. $hdr->type1 = $arg_a[1];
  462. $msg->type0 = $arg_a[0];
  463. $msg->type1 = $arg_a[1];
  464. $arr = $arg_a[2];
  465. if (is_array ($arr)) {
  466. $hdr->parameters = $arg_a[2];
  467. }
  468. $hdr->id = str_replace ('<', '', str_replace ('>', '', $arg_a[3]));
  469. $hdr->description = $arg_a[4];
  470. $hdr->encoding = strtolower ($arg_a[5]);
  471. $hdr->entity_id = $msg->entity_id;
  472. $hdr->size = $arg_a[6];
  473. if ($shifted_args) {
  474. $hdr->lines = $arg_a[7];
  475. $s = 1;
  476. } else {
  477. $s = 0;
  478. }
  479. $hdr->md5 = (isset($arg_a[7+$s]) ? $arg_a[7+$s] : $hdr->md5);
  480. $hdr->disposition = (isset($arg_a[8+$s]) ? $arg_a[8+$s] : $hdr->disposition);
  481. $hdr->language = (isset($arg_a[9+$s]) ? $arg_a[9+$s] : $hdr->language);
  482. $msg->header = $hdr;
  483. } else {
  484. $hdr->type0 = 'multipart';
  485. $hdr->type1 = $arg_a[0];
  486. $msg->type0 = 'multipart';
  487. $msg->type1 = $arg_a[0];
  488. $hdr->parameters = (isset($arg_a[1]) ? $arg_a[1] : $hdr->parameters);
  489. $hdr->disposition = (isset($arg_a[2]) ? $arg_a[2] : $hdr->disposition);
  490. $hdr->language = (isset($arg_a[3]) ? $arg_a[3] : $hdr->language);
  491. $msg->header = $hdr;
  492. }
  493. return $msg;
  494. default: break;
  495. } /* switch */
  496. } /* for */
  497. } /* parsestructure */
  498. /**
  499. * @param string $read
  500. * @param integer $i
  501. * @return array
  502. */
  503. function parseProperties ($read, &$i) {
  504. $properties = array();
  505. $prop_name = '';
  506. for (; $read{$i} != ')'; ++$i) {
  507. $arg_s = '';
  508. if ($read{$i} == '"') {
  509. $arg_s = $this->parseQuote ($read, $i);
  510. } else if ($read{$i} == '{') {
  511. $arg_s = $this->parseLiteral ($read, $i);
  512. }
  513. if ($arg_s != '') {
  514. if ($prop_name == '') {
  515. $prop_name = strtolower ($arg_s);
  516. $properties[$prop_name] = '';
  517. } else if ($prop_name != '') {
  518. $properties[$prop_name] = $arg_s;
  519. $prop_name = '';
  520. }
  521. }
  522. }
  523. return $properties;
  524. }
  525. /**
  526. * @param string $read
  527. * @param integer $i
  528. * @param object $hdr MessageHeader object
  529. * @return object MessageHeader object
  530. */
  531. function parseEnvelope ($read, &$i, $hdr) {
  532. $arg_no = 0;
  533. $arg_a = array();
  534. ++$i;
  535. for ($cnt = strlen ($read); ($i < $cnt) && ($read{$i} != ')'); ++$i) {
  536. $char = strtoupper ($read{$i});
  537. switch ($char) {
  538. case '"':
  539. $arg_a[] = $this->parseQuote ($read, $i);
  540. ++$arg_no;
  541. break;
  542. case '{':
  543. $arg_a[] = $this->parseLiteral ($read, $i);
  544. /* temp bugfix (SM 1.5 will have a working clean version)
  545. too much work to implement that version right now */
  546. // --$i;
  547. ++$arg_no;
  548. break;
  549. case 'N':
  550. /* probably NIL argument */
  551. if (strtoupper (substr ($read, $i, 3)) == 'NIL') {
  552. $arg_a[] = '';
  553. ++$arg_no;
  554. $i += 2;
  555. }
  556. break;
  557. case '(':
  558. /* Address structure (with group support)
  559. * Note: Group support is useless on SMTP connections
  560. * because the protocol doesn't support it
  561. */
  562. $addr_a = array();
  563. $group = '';
  564. $a=0;
  565. for (; $i < $cnt && $read{$i} != ')'; ++$i) {
  566. if ($read{$i} == '(') {
  567. $addr = $this->parseAddress ($read, $i);
  568. if (($addr->host == '') && ($addr->mailbox != '')) {
  569. /* start of group */
  570. $group = $addr->mailbox;
  571. $group_addr = $addr;
  572. $j = $a;
  573. } else if ($group && ($addr->host == '') && ($addr->mailbox == '')) {
  574. /* end group */
  575. if ($a == ($j+1)) { /* no group members */
  576. $group_addr->group = $group;
  577. $group_addr->mailbox = '';
  578. $group_addr->personal = "$group: Undisclosed recipients;";
  579. $addr_a[] = $group_addr;
  580. $group ='';
  581. }
  582. } else {
  583. $addr->group = $group;
  584. $addr_a[] = $addr;
  585. }
  586. ++$a;
  587. }
  588. }
  589. $arg_a[] = $addr_a;
  590. break;
  591. default: break;
  592. }
  593. }
  594. if (count ($arg_a) > 9) {
  595. $d = strtr ($arg_a[0], array(' ' => ' '));
  596. $d_parts = explode (' ', $d);
  597. if (!$arg_a[1]) $arg_a[1] = _ ("(no subject)");
  598. $hdr->date = getTimeStamp ($d_parts); /* argument 1: date */
  599. $hdr->date_unparsed = strtr ($d,'<>',' '); /* original date */
  600. $hdr->subject = $arg_a[1]; /* argument 2: subject */
  601. $hdr->from = is_array ($arg_a[2]) ? $arg_a[2][0] : ''; /* argument 3: from */
  602. $hdr->sender = is_array ($arg_a[3]) ? $arg_a[3][0] : ''; /* argument 4: sender */
  603. $hdr->reply_to = is_array ($arg_a[4]) ? $arg_a[4][0] : ''; /* argument 5: reply-to */
  604. $hdr->to = $arg_a[5]; /* argument 6: to */
  605. $hdr->cc = $arg_a[6]; /* argument 7: cc */
  606. $hdr->bcc = $arg_a[7]; /* argument 8: bcc */
  607. $hdr->in_reply_to = $arg_a[8]; /* argument 9: in-reply-to */
  608. $hdr->message_id = $arg_a[9]; /* argument 10: message-id */
  609. }
  610. return $hdr;
  611. }
  612. /**
  613. * @param string $read
  614. * @param integer $i
  615. * @return string
  616. * @todo document me
  617. */
  618. function parseLiteral ($read, &$i) {
  619. $lit_cnt = '';
  620. ++$i;
  621. $iPos = strpos ($read,'}',$i);
  622. if ($iPos) {
  623. $lit_cnt = substr ($read, $i, $iPos - $i);
  624. $i += strlen ($lit_cnt) + 3; /* skip } + \r + \n */
  625. /* Now read the literal */
  626. $s = ($lit_cnt ? substr ($read,$i,$lit_cnt): '');
  627. $i += $lit_cnt;
  628. /* temp bugfix (SM 1.5 will have a working clean version)
  629. too much work to implement that version right now */
  630. --$i;
  631. } else { /* should never happen */
  632. $i += 3; /* } + \r + \n */
  633. $s = '';
  634. }
  635. return $s;
  636. }
  637. /**
  638. * function parseQuote
  639. *
  640. * This extract the string value from a quoted string. After the end-quote
  641. * character is found it returns the string. The offset $i when calling
  642. * this function points to the first double quote. At the end it points to
  643. * The ending quote. This function takes care of escaped double quotes.
  644. * "some \"string\""
  645. * ^ ^
  646. * initial $i end position $i
  647. *
  648. * @param string $read
  649. * @param integer $i offset in $read
  650. * @return string string inbetween the double quotes
  651. * @author Marc Groot Koerkamp
  652. */
  653. function parseQuote ($read, &$i) {
  654. $s = '';
  655. $iPos = ++$i;
  656. $iPosStart = $iPos;
  657. while (true) {
  658. $iPos = strpos ($read,'"',$iPos);
  659. if (!$iPos) break;
  660. if ($iPos && $read{$iPos -1} != '\\') {
  661. $s = substr ($read,$i,($iPos-$i));
  662. $i = $iPos;
  663. break;
  664. } else if ($iPos > 1 && $read{$iPos -1} == '\\' && $read{$iPos-2} == '\\') {
  665. // This is an unique situation where the fast detection of the string
  666. // fails. If the quote string ends with \\ then we need to iterate
  667. // through the entire string to make sure we detect the unexcaped
  668. // double quotes correctly.
  669. $s = '';
  670. $bEscaped = false;
  671. $k = 0;
  672. for ($j=$iPosStart,$iCnt=strlen ($read);$j<$iCnt;++$j) {
  673. $cChar = $read{$j};
  674. switch ($cChar) {
  675. case '\\':
  676. $bEscaped = !$bEscaped;
  677. $s .= $cChar;
  678. break;
  679. case '"':
  680. if ($bEscaped) {
  681. $s .= $cChar;
  682. $bEscaped = false;
  683. } else {
  684. $i = $j;
  685. break 3;
  686. }
  687. break;
  688. default:
  689. if ($bEscaped) {
  690. $bEscaped = false;
  691. }
  692. $s .= $cChar;
  693. break;
  694. }
  695. }
  696. }
  697. ++$iPos;
  698. if ($iPos > strlen ($read)) {
  699. break;
  700. }
  701. }
  702. return $s;
  703. }
  704. /**
  705. * @param string $read
  706. * @param integer $i
  707. * @return object AddressStructure object
  708. */
  709. function parseAddress ($read, &$i) {
  710. $arg_a = array();
  711. for (; $read{$i} != ')'; ++$i) {
  712. $char = strtoupper ($read{$i});
  713. switch ($char) {
  714. case '"': $arg_a[] = $this->parseQuote ($read, $i); break;
  715. case '{': $arg_a[] = $this->parseLiteral ($read, $i); break;
  716. case 'n':
  717. case 'N':
  718. if (strtoupper (substr ($read, $i, 3)) == 'NIL') {
  719. $arg_a[] = '';
  720. $i += 2;
  721. }
  722. break;
  723. default: break;
  724. }
  725. }
  726. if (count ($arg_a) == 4) {
  727. $adr = new AddressStructure ();
  728. $adr->personal = $arg_a[0];
  729. $adr->adl = $arg_a[1];
  730. $adr->mailbox = $arg_a[2];
  731. $adr->host = $arg_a[3];
  732. } else {
  733. $adr = '';
  734. }
  735. return $adr;
  736. }
  737. /**
  738. * @param string $read
  739. * @param integer $i
  740. * @param object Disposition object or empty string
  741. */
  742. function parseDisposition ($read, &$i) {
  743. $arg_a = array();
  744. for (; $read{$i} != ')'; ++$i) {
  745. switch ($read{$i}) {
  746. case '"': $arg_a[] = $this->parseQuote ($read, $i); break;
  747. case '{': $arg_a[] = $this->parseLiteral ($read, $i); break;
  748. case '(': $arg_a[] = $this->parseProperties ($read, $i); break;
  749. default: break;
  750. }
  751. }
  752. if (isset($arg_a[0])) {
  753. $disp = new Disposition ($arg_a[0]);
  754. if (isset($arg_a[1])) {
  755. $disp->properties = $arg_a[1];
  756. }
  757. }
  758. return (is_object ($disp) ? $disp : '');
  759. }
  760. /**
  761. * @param string $read
  762. * @param integer $i
  763. * @return object Language object or empty string
  764. */
  765. function parseLanguage ($read, &$i) {
  766. /* no idea how to process this one without examples */
  767. $arg_a = array();
  768. for (; $read{$i} != ')'; ++$i) {
  769. switch ($read{$i}) {
  770. case '"': $arg_a[] = $this->parseQuote ($read, $i); break;
  771. case '{': $arg_a[] = $this->parseLiteral ($read, $i); break;
  772. case '(': $arg_a[] = $this->parseProperties ($read, $i); break;
  773. default: break;
  774. }
  775. }
  776. if (isset($arg_a[0])) {
  777. $lang = new Language ($arg_a[0]);
  778. if (isset($arg_a[1])) {
  779. $lang->properties = $arg_a[1];
  780. }
  781. }
  782. return (is_object ($lang) ? $lang : '');
  783. }
  784. /**
  785. * Parse message text enclosed in parenthesis
  786. * @param string $read
  787. * @param integer $i
  788. * @return integer
  789. */
  790. function parseParenthesis ($read, $i) {
  791. for ($i++; $read{$i} != ')'; ++$i) {
  792. switch ($read{$i}) {
  793. case '"': $this->parseQuote ($read, $i); break;
  794. case '{': $this->parseLiteral ($read, $i); break;
  795. case '(': $this->parseProperties ($read, $i); break;
  796. default: break;
  797. }
  798. }
  799. return $i;
  800. }
  801. /**
  802. * Function to fill the message structure in case the
  803. * bodystructure is not available
  804. * NOT FINISHED YET
  805. * @param string $read
  806. * @param string $type0 message part type
  807. * @param string $type1 message part subtype
  808. * @return string (only when type0 is not message or multipart)
  809. */
  810. function parseMessage ($read, $type0, $type1) {
  811. switch ($type0) {
  812. case 'message':
  813. $rfc822_header = true;
  814. $mime_header = false;
  815. break;
  816. case 'multipart':
  817. $rfc822_header = false;
  818. $mime_header = true;
  819. break;
  820. default: return $read;
  821. }
  822. for ($i = 1; $i < $count; ++$i) {
  823. $line = trim ($body[$i]);
  824. if (($mime_header || $rfc822_header) &&
  825. (preg_match ("/^.*boundary=\"?(.+(?=\")|.+).*/i", $line, $reg))) {
  826. $bnd = $reg[1];
  827. $bndreg = $bnd;
  828. $bndreg = str_replace ("\\", "\\\\", $bndreg);
  829. $bndreg = str_replace ("?", "\\?", $bndreg);
  830. $bndreg = str_replace ("+", "\\+", $bndreg);
  831. $bndreg = str_replace (".", "\\.", $bndreg);
  832. $bndreg = str_replace ("/", "\\/", $bndreg);
  833. $bndreg = str_replace ("-", "\\-", $bndreg);
  834. $bndreg = str_replace ("(", "\\(", $bndreg);
  835. $bndreg = str_replace (")", "\\)", $bndreg);
  836. } else if ($rfc822_header && $line == '') {
  837. $rfc822_header = false;
  838. if ($msg->type0 == 'multipart') {
  839. $mime_header = true;
  840. }
  841. }
  842. if ((($line{0} == '-') || $rfc822_header) && isset($boundaries[0])) {
  843. $cnt = count ($boundaries)-1;
  844. $bnd = $boundaries[$cnt]['bnd'];
  845. $bndreg = $boundaries[$cnt]['bndreg'];
  846. $regstr = '/^--'."($bndreg)".".*".'/';
  847. if (preg_match ($regstr, $line, $reg)) {
  848. $bndlen = strlen ($reg[1]);
  849. $bndend = false;
  850. if (strlen ($line) > ($bndlen + 3)) {
  851. if (($line{$bndlen+2} == '-') && ($line{$bndlen+3} == '-')) {
  852. $bndend = true;
  853. }
  854. }
  855. if ($bndend) {
  856. /* calc offset and return $msg */
  857. //$entStr = CalcEntity("$entStr", -1);
  858. array_pop ($boundaries);
  859. $mime_header = true;
  860. $bnd_end = true;
  861. } else {
  862. $mime_header = true;
  863. $bnd_end = false;
  864. //$entStr = CalcEntity("$entStr", 0);
  865. ++$content_indx;
  866. }
  867. } else {
  868. if ($header) { }
  869. }
  870. }
  871. }
  872. }
  873. /**
  874. * @param array $entity
  875. * @param array $alt_order
  876. * @param boolean $strict
  877. * @return array
  878. */
  879. function findDisplayEntity ($entity = array(), $alt_order = array('text/plain', 'text/html'), $strict=false) {
  880. $found = false;
  881. if ($this->type0 == 'multipart') {
  882. if($this->type1 == 'alternative') {
  883. $msg = $this->findAlternativeEntity ($alt_order);
  884. if ( ! is_null ($msg) ) {
  885. if (count ($msg->entities) == 0) {
  886. $entity[] = $msg->entity_id;
  887. } else {
  888. $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
  889. }
  890. $found = true;
  891. }
  892. } else if ($this->type1 == 'related') { /* RFC 2387 */
  893. $msgs = $this->findRelatedEntity ();
  894. foreach ($msgs as $msg) {
  895. if (count ($msg->entities) == 0) {
  896. $entity[] = $msg->entity_id;
  897. } else {
  898. $entity = $msg->findDisplayEntity($entity, $alt_order, $strict);
  899. }
  900. }
  901. if (count ($msgs) > 0) {
  902. $found = true;
  903. }
  904. } else { /* Treat as multipart/mixed */
  905. foreach ($this->entities as $ent) {
  906. if(!(is_object ($ent->header->disposition) && strtolower ($ent->header->disposition->name) == 'attachment') &&
  907. (!isset($ent->header->parameters['filename'])) &&
  908. (!isset($ent->header->parameters['name'])) &&
  909. (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) {
  910. $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
  911. $found = true;
  912. }
  913. }
  914. }
  915. } else { /* If not multipart, then just compare with each entry from $alt_order */
  916. $type = $this->type0.'/'.$this->type1 ;
  917. // $alt_order[] = "message/rfc822";
  918. foreach ($alt_order as $alt) {
  919. if( ($alt == $type) && isset($this->entity_id ) ) {
  920. if ((count ($this->entities ) == 0) &&
  921. (!isset($this->header->parameters['filename'])) &&
  922. (!isset($this->header->parameters['name'])) &&
  923. (isset($this->header->disposition) && is_object ($this->header->disposition) &&
  924. strtolower ($this->header->disposition->name) != 'attachment')) {
  925. $entity[] = $this->entity_id ;
  926. $found = true;
  927. }
  928. }
  929. }
  930. }
  931. if(!$found) {
  932. foreach ($this->entities as $ent) {
  933. if(!(is_object ($ent->header->disposition) && strtolower ($ent->header->disposition->name) == 'attachment') &&
  934. (($ent->type0 != 'message') && ($ent->type1 != 'rfc822'))) {
  935. $entity = $ent->findDisplayEntity($entity, $alt_order, $strict);
  936. $found = true;
  937. }
  938. }
  939. }
  940. if(!$strict && !$found) {
  941. if (($this->type0 == 'text') &&
  942. in_array ($this->type1 , array('plain', 'html', 'message')) &&
  943. isset($this->entity_id )) {
  944. if (count ($this->entities ) == 0) {
  945. if (!is_object ($this->header->disposition) || strtolower ($this->header->disposition->name) != 'attachment') {
  946. $entity[] = $this->entity_id ;
  947. }
  948. }
  949. }
  950. }
  951. return $entity;
  952. }
  953. /**
  954. * @param array $alt_order
  955. * @return entity
  956. */
  957. function findAlternativeEntity ($alt_order) {
  958. /* If we are dealing with alternative parts then we */
  959. /* choose the best viewable message supported by SM. */
  960. $best_view = 0;
  961. $entity = null;
  962. foreach($this->entities as $ent) {
  963. $type = $ent->header->type0 . '/' . $ent->header->type1;
  964. if ($type == 'multipart/related') {
  965. $type = $ent->header->getParameter('type');
  966. // Mozilla bug. Mozilla does not provide the parameter type.
  967. if (!$type) $type = 'text/html';
  968. }
  969. $altCount = count ($alt_order);
  970. for ($j = $best_view; $j < $altCount; ++$j) {
  971. if (($alt_order[$j] == $type) && ($j >= $best_view)) {
  972. $best_view = $j;
  973. $entity = $ent;
  974. }
  975. }
  976. }
  977. return $entity;
  978. }
  979. /**
  980. * @return array
  981. */
  982. function findRelatedEntity () {
  983. $msgs = array();
  984. $related_type = $this->header->getParameter('type');
  985. // Mozilla bug. Mozilla does not provide the parameter type.
  986. if (!$related_type) $related_type = 'text/html';
  987. $entCount = count ($this->entities );
  988. for ($i = 0; $i < $entCount; ++$i) {
  989. $type = $this->entities [$i]->header->type0.'/'.$this->entities [$i]->header->type1;
  990. if ($related_type == $type) {
  991. $msgs[] = $this->entities [$i];
  992. }
  993. }
  994. return $msgs;
  995. }
  996. /**
  997. * @param array $exclude_id
  998. * @param array $result
  999. * @return array
  1000. */
  1001. function getAttachments ($exclude_id=array(), $result = array()) {
  1002. /*
  1003. if (($this->type0 == 'message') &&
  1004. ($this->type1 == 'rfc822') &&
  1005. ($this->entity_id) ) {
  1006. $this = $this->entities[0];
  1007. }
  1008. */
  1009. if (count ($this->entities )) {
  1010. foreach ($this->entities as $entity) {
  1011. $exclude = false;
  1012. foreach ($exclude_id as $excl) {
  1013. if ($entity->entity_id === $excl) {
  1014. $exclude = true;
  1015. }
  1016. }
  1017. if (!$exclude) {
  1018. if ($entity->type0 == 'multipart') {
  1019. $result = $entity->getAttachments($exclude_id, $result);
  1020. } else if ($entity->type0 != 'multipart') {
  1021. $result[] = $entity;
  1022. }
  1023. }
  1024. }
  1025. } else {
  1026. $exclude = false;
  1027. foreach ($exclude_id as $excl) {
  1028. $exclude = $exclude || ($this->entity_id == $excl);
  1029. }
  1030. if (!$exclude) {
  1031. $result[] = $this;
  1032. }
  1033. }
  1034. return $result;
  1035. }
  1036. /**
  1037. * Add attachment to message object
  1038. * @param string $type attachment type
  1039. * @param string $name attachment name
  1040. * @param string $location path to attachment
  1041. */
  1042. function initAttachment ($type, $name, $location) {
  1043. $attachment = new Message ();
  1044. $mime_header = new MessageHeader ();
  1045. $mime_header->setParameter('name', $name);
  1046. $pos = strpos ($type, '/');
  1047. if ($pos > 0) {
  1048. $mime_header->type0 = substr ($type, 0, $pos);
  1049. $mime_header->type1 = substr ($type, $pos+1);
  1050. } else {
  1051. $mime_header->type0 = $type;
  1052. }
  1053. $attachment->att_local_name = $location;
  1054. $disposition = new Disposition ('attachment');
  1055. $disposition->properties['filename'] = $name;
  1056. $mime_header->disposition = $disposition;
  1057. $attachment->mime_header = $mime_header;
  1058. $this->entities []=$attachment;
  1059. }
  1060. /**
  1061. * Delete all attachments from this object from disk.
  1062. * @since 1.4.6
  1063. */
  1064. function purgeAttachments () {
  1065. if ($this->att_local_name ) {
  1066. global $username, $attachment_dir ;
  1067. $hashed_attachment_dir = getHashedDir ($username, $attachment_dir);
  1068. if ( file_exists ($hashed_attachment_dir . '/' . $this->att_local_name ) ) {
  1069. unlink ($hashed_attachment_dir . '/' . $this->att_local_name );
  1070. }
  1071. }
  1072. // recursively delete attachments from entities contained in this object
  1073. for ($i=0, $entCount=count ($this->entities );$i< $entCount; ++$i) {
  1074. $this->entities [$i]->purgeAttachments();
  1075. }
  1076. }
  1077. }

Documentation generated on 2020年1月13日 04:24:56 +0100 by phpDocumentor 1.4.3

AltStyle によって変換されたページ (->オリジナル) /