PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/Settings/MailScanner/core/MailRecord.php

https://bitbucket.org/yousef_fadila/vtiger
PHP | 318 lines | 193 code | 39 blank | 86 comment | 52 complexity | d0e629892e92210efbdeae21c74fa80b MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /*********************************************************************************
  3. ** The contents of this file are subject to the vtiger CRM Public License Version 1.0
  4. * ("License"); You may not use this file except in compliance with the License
  5. * The Original Code is: vtiger CRM Open Source
  6. * The Initial Developer of the Original Code is vtiger.
  7. * Portions created by vtiger are Copyright (C) vtiger.
  8. * All Rights Reserved.
  9. *
  10. ********************************************************************************/
  11. /**
  12. * This class provides structured way of accessing details of email.
  13. */
  14. class Vtiger_MailRecord {
  15. // FROM address(es) list
  16. var $_from;
  17. // TO address(es) list
  18. var $_to;
  19. //var $_replyto;
  20. // CC address(es) list
  21. var $_cc;
  22. // BCC address(es) list
  23. var $_bcc;
  24. // DATE
  25. var $_date;
  26. // SUBJECT
  27. var $_subject;
  28. // BODY (either HTML / PLAIN message)
  29. var $_body;
  30. // CHARSET of the body content
  31. var $_charset;
  32. // If HTML message was set as body content
  33. var $_isbodyhtml;
  34. // PLAIN message of the original email
  35. var $_plainmessage = false;
  36. // HTML message of the original email
  37. var $_htmlmessage = false;
  38. // ATTACHMENTS list of the email
  39. var $_attachments = false;
  40. // UNIQUEID associated with the email
  41. var $_uniqueid = false;
  42. // Flag to avoid re-parsing the email body.
  43. var $_bodyparsed = false;
  44. /** DEBUG Functionality. */
  45. var $debug = false;
  46. function log($message=false) {
  47. if(!$message) $message = $this->__toString();
  48. global $log;
  49. if($log && $this->debug) { $log->debug($message); }
  50. else if($this->debug) {
  51. echo var_export($message, true) . "\n";
  52. }
  53. }
  54. /**
  55. * String representation of the object.
  56. */
  57. function __toString() {
  58. $tostring = '';
  59. $tostring .= 'FROM: ['. implode(',', $this->_from) . ']';
  60. $tostring .= ',TO: [' . implode(',', $this->_to) . ']';
  61. if(!empty($this->_cc)) $tostring .= ',CC: [' . implode(',', $this->_cc) . ']';
  62. if(!empty($this->_bcc))$tostring .= ',BCC: [' . implode(',', $this->_bcc) . ']';
  63. $tostring .= ',DATE: ['. $this->_date . ']';
  64. $tostring .= ',SUBJECT: ['. $this->_subject . ']';
  65. return $tostring;
  66. }
  67. /**
  68. * Constructor.
  69. */
  70. function __construct($imap, $messageid, $fetchbody=true) {
  71. $this->__parseHeader($imap, $messageid);
  72. if($fetchbody) $this->__parseBody($imap, $messageid);
  73. }
  74. /**
  75. * Get body content as Text.
  76. */
  77. function getBodyText($striptags=true) {
  78. $bodytext = $this->_body;
  79. if($this->_plainmessage) {
  80. $bodytext = $this->_plainmessage;
  81. } else if($this->_isbodyhtml) {
  82. // TODO This conversion can added multiple lines if
  83. // content is displayed directly on HTML page
  84. $bodytext = preg_replace("/<br>/", "\n", $bodytext);
  85. $bodytext = strip_tags($bodytext);
  86. }
  87. return $bodytext;
  88. }
  89. /**
  90. * Get body content as HTML.
  91. */
  92. function getBodyHTML() {
  93. $bodyhtml = $this->_body;
  94. if(!$this->_isbodyhtml) {
  95. $bodyhtml = preg_replace( Array("/\r\n/", "/\n/"), Array('<br>','<br>'), $bodyhtml );
  96. }
  97. return $bodyhtml;
  98. }
  99. /**
  100. * Fetch the mail body from server.
  101. */
  102. function fetchBody($imap, $messageid) {
  103. if(!$this->_bodyparsed) $this->__parseBody($imap, $messageid);
  104. }
  105. /**
  106. * Parse the email id from the mail header text.
  107. * @access private
  108. */
  109. function __getEmailIdList($inarray) {
  110. if(empty($inarray)) return Array();
  111. $emails = Array();
  112. foreach($inarray as $emailinfo) {
  113. $emails[] = $emailinfo->mailbox . '@' . $emailinfo->host;
  114. }
  115. return $emails;
  116. }
  117. /**
  118. * Helper function to convert the encoding of input to target charset.
  119. */
  120. static function __convert_encoding($input, $to, $from = false) {
  121. if(function_exists('mb_convert_encoding')) {
  122. if(!$from) $from = mb_detect_encoding($input);
  123. if(strtolower(trim($to)) == strtolower(trim($from))) {
  124. return $input;
  125. } else {
  126. return mb_convert_encoding($input, $to, $from);
  127. }
  128. }
  129. return $input;
  130. }
  131. /**
  132. * MIME decode function to parse IMAP header or mail information
  133. */
  134. static function __mime_decode($input, &$words=null, $targetEncoding='UTF-8') {
  135. if(is_null($words)) $words = array();
  136. $returnvalue = $input;
  137. if(preg_match_all('/=\?([^\?]+)\?([^\?]+)\?([^\?]+)\?=/', $input, $matches)) {
  138. $totalmatches = count($matches[0]);
  139. for($index = 0; $index < $totalmatches; ++$index) {
  140. $charset = $matches[1][$index];
  141. $encoding= strtoupper($matches[2][$index]); // B - base64 or Q - quoted printable
  142. $data = $matches[3][$index];
  143. if($encoding == 'B') {
  144. $decodevalue = base64_decode($data);
  145. } else if($encoding == 'Q') {
  146. $decodevalue = quoted_printable_decode($data);
  147. }
  148. $value = self::__convert_encoding($decodevalue, $targetEncoding, $charset);
  149. array_push($words, $value);
  150. }
  151. }
  152. if(!empty($words)) {
  153. $returnvalue = implode('', $words);
  154. }
  155. return $returnvalue;
  156. }
  157. /**
  158. * MIME encode function to prepare input to target charset supported by normal IMAP clients.
  159. */
  160. static function __mime_encode($input, $encoding='Q', $charset='iso-8859-1') {
  161. $returnvalue = $input;
  162. $encoded = false;
  163. if(strtoupper($encoding) == 'B' ) {
  164. $returnvalue = self::__convert_encoding($input, $charset);
  165. $returnvalue = base64_encode($returnvalue);
  166. $encoded = true;
  167. } else {
  168. $returnvalue = self::__convert_encoding($input, $charset);
  169. if(function_exists('imap_qprint')) {
  170. $returnvalue = imap_qprint($returnvalue);
  171. $encoded = true;
  172. } else {
  173. // TODO: Handle case when imap_qprint is not available.
  174. }
  175. }
  176. if($encoded) {
  177. $returnvalue = "=?$charset?$encoding?$returnvalue?=";
  178. }
  179. return $returnvalue;
  180. }
  181. /**
  182. * Parse header of the email.
  183. * @access private
  184. */
  185. function __parseHeader($imap, $messageid) {
  186. $this->_from = Array();
  187. $this->_to = Array();
  188. $mailheader = imap_headerinfo($imap, $messageid);
  189. $this->_uniqueid = $mailheader->message_id;
  190. $this->_from = $this->__getEmailIdList($mailheader->from);
  191. $this->_to = $this->__getEmailIdList($mailheader->to);
  192. $this->_cc = $this->__getEmailIdList($mailheader->cc);
  193. $this->_bcc = $this->__getEmailIdList($mailheader->bcc);
  194. $this->_date = $mailheader->udate;
  195. $this->_subject = self::__mime_decode($mailheader->subject);
  196. if(!$this->_subject) $this->_subject = 'Untitled';
  197. }
  198. // Modified: http://in2.php.net/manual/en/function.imap-fetchstructure.php#85685
  199. function __parseBody($imap, $messageid) {
  200. $structure = imap_fetchstructure($imap, $messageid);
  201. $this->_plainmessage = '';
  202. $this->_htmlmessage = '';
  203. $this->_body = '';
  204. $this->_isbodyhtml = false;
  205. if($structure->parts) { /* multipart */
  206. foreach($structure->parts as $partno0=>$p) {
  207. $this->__getpart($imap, $messageid, $p, $partno0+1);
  208. }
  209. } else { /* not multipart */
  210. $this->__getpart($imap, $messageid, $structure, 0);
  211. }
  212. // Set the body (either plain or html content)
  213. if($this->_htmlmessage != '') {
  214. $this->_body = $this->_htmlmessage;
  215. $this->_isbodyhtml = true;
  216. } else {
  217. $this->_body = $this->_plainmessage;
  218. }
  219. if($this->_attachments) {
  220. $this->log("Attachments: ");
  221. $this->log(array_keys($this->_attachments));
  222. }
  223. $this->_bodyparsed = true;
  224. }
  225. // Modified: http://in2.php.net/manual/en/function.imap-fetchstructure.php#85685
  226. function __getpart($imap, $messageid, $p, $partno) {
  227. // $partno = '1', '2', '2.1', '2.1.3', etc if multipart, 0 if not multipart
  228. // DECODE DATA
  229. $data = ($partno)?
  230. imap_fetchbody($imap,$messageid,$partno): // multipart
  231. imap_body($imap,$messageid); // not multipart
  232. // Any part may be encoded, even plain text messages, so check everything.
  233. if ($p->encoding==4) $data = quoted_printable_decode($data);
  234. elseif ($p->encoding==3) $data = base64_decode($data);
  235. // no need to decode 7-bit, 8-bit, or binary
  236. // PARAMETERS
  237. // get all parameters, like charset, filenames of attachments, etc.
  238. $params = array();
  239. if ($p->parameters) {
  240. foreach ($p->parameters as $x) $params[ strtolower( $x->attribute ) ] = $x->value;
  241. }
  242. if ($p->dparameters) {
  243. foreach ($p->dparameters as $x) $params[ strtolower( $x->attribute ) ] = $x->value;
  244. }
  245. // ATTACHMENT
  246. // Any part with a filename is an attachment,
  247. // so an attached text file (type 0) is not mistaken as the message.
  248. if ($params['filename'] || $params['name']) {
  249. // filename may be given as 'Filename' or 'Name' or both
  250. $filename = ($params['filename'])? $params['filename'] : $params['name'];
  251. // filename may be encoded, so see imap_mime_header_decode()
  252. if(!$this->_attachments) $this->_attachments = Array();
  253. $this->_attachments[$filename] = $data; // TODO: this is a problem if two files have same name
  254. }
  255. // TEXT
  256. elseif ($p->type==0 && $data) {
  257. $this->_charset = $params['charset']; // assume all parts are same charset
  258. $data = self::__convert_encoding($data, 'UTF-8', $this->_charset);
  259. // Messages may be split in different parts because of inline attachments,
  260. // so append parts together with blank row.
  261. if (strtolower($p->subtype)=='plain') $this->_plainmessage .= trim($data) ."\n\n";
  262. else $this->_htmlmessage .= $data ."<br><br>";
  263. }
  264. // EMBEDDED MESSAGE
  265. // Many bounce notifications embed the original message as type 2,
  266. // but AOL uses type 1 (multipart), which is not handled here.
  267. // There are no PHP functions to parse embedded messages,
  268. // so this just appends the raw source to the main message.
  269. elseif ($p->type==2 && $data) {
  270. $this->_plainmessage .= trim($data) ."\n\n";
  271. }
  272. // SUBPART RECURSION
  273. if ($p->parts) {
  274. foreach ($p->parts as $partno0=>$p2)
  275. $this->__getpart($imap,$messageid,$p2,$partno.'.'.($partno0+1)); // 1.2, 1.2.1, etc.
  276. }
  277. }
  278. }
  279. ?>