simple-imap.class.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * Copyright 2016-2019 Johannes Keßler
  4. *
  5. * simple IMAP SSL/TLS email connection based on the imap PHP functions
  6. * the code supports SSL/TLS and IMAP only
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.
  20. *
  21. */
  22. /**
  23. * Class SimpleImap
  24. * read and manage email messages over imap. Sending not included. Use PHPMailer instead.
  25. */
  26. class SimpleImap {
  27. private $_connection;
  28. private $_server = EMAIL_SERVER;
  29. private $_user = EMAIL_SERVER_USER;
  30. private $_pass = EMAIL_SERVER_PASS;
  31. private $_port = EMAIL_SERVER_PORT_IMAP;
  32. private $_mailbox = EMAIL_SERVER_MAILBOX;
  33. private $_connectionstring = '';
  34. function __construct() {
  35. # create the mailboxstring
  36. $this->_connectionstring = '{'.$this->_server.':'.$this->_port.'/imap/ssl}';
  37. }
  38. function __destruct() {
  39. imap_close($this->_connection);
  40. }
  41. /**
  42. * connect to the e-mail server
  43. * with this code SSL/TLS only
  44. *
  45. * @see http://ca.php.net/manual/en/function.imap-open.php
  46. * @throws Exception
  47. */
  48. public function connect() {
  49. if(empty($this->_server)) {
  50. throw new Exception('Missing EMAIL_SERVER');
  51. }
  52. if(empty($this->_port)) {
  53. throw new Exception('Missing EMAIL_SERVER_PORT');
  54. }
  55. if(empty($this->_user)) {
  56. throw new Exception('Missing EMAIL_SERVER_USER');
  57. }
  58. # create the connection
  59. $this->_connection = imap_open($this->_connectionstring.$this->_mailbox, $this->_user, $this->_pass);
  60. if(!$this->_connection) {
  61. throw new Exception('Failed IMAP connection: '.var_export(imap_last_error(),true));
  62. }
  63. }
  64. /**
  65. * process the given mailbox and check for the special messages
  66. * return the body and headers from the found message
  67. * @param string $subjectmarker
  68. * @return array emailId => array(body, header);
  69. * @throws Exception
  70. */
  71. function messageWithValidSubject($subjectmarker) {
  72. $ret = array();
  73. $messagecount = imap_num_msg($this->_connection);
  74. if($messagecount === false) {
  75. throw new Exception('Can not read the messages in given mailbox');
  76. }
  77. $processedmessagescount = 0;
  78. for($i = 1; $i <= $messagecount; $i++) {
  79. $subject = $this->_extractSubject($i);
  80. if(!empty($subject)) {
  81. # check the special stuff
  82. $markerextract = substr($subject, 0, strlen($subjectmarker));
  83. if($markerextract == $subjectmarker) {
  84. $processedmessagescount++;
  85. # valid message
  86. # get the body
  87. $ret[$i]['body'] = $this->_extractBody($i);
  88. $ret[$i]['header'] = $this->emailHeaders($i);
  89. $ret[$i]['header_rfc822'] = $this->emailHeaders_rfc822($i);
  90. $ret[$i]['header_array'] = $this->emailHeadersAsArray($i);
  91. # @see https://www.php.net/manual/en/function.imap-uid.php
  92. $ret[$i]['uid'] = imap_uid($this->_connection,$i);
  93. }
  94. }
  95. }
  96. # log messages processed to all messages
  97. error_log("INFO Read ".$messagecount." messages");
  98. error_log("INFO Processed ".$processedmessagescount." messages");
  99. return $ret;
  100. }
  101. /**
  102. * the the current stats about the mail connection and INBOX
  103. * kinda debug only
  104. *
  105. * @see http://ca.php.net/manual/en/function.imap-status.php
  106. */
  107. public function mailboxStatus() {
  108. if($this->_connection !== false) {
  109. $status = imap_status($this->_connection, $this->_connectionstring.$this->_mailbox, SA_ALL);
  110. if(DEBUG === true) {
  111. var_dump("messages " . $status->messages);
  112. var_dump("recent " . $status->recent);
  113. var_dump("unseen " . $status->unseen);
  114. var_dump("uidnext " . $status->uidnext);
  115. var_dump("uidvalidity " . $status->uidvalidity);
  116. }
  117. $list = imap_getmailboxes($this->_connection, $this->_connectionstring, "*");
  118. if (is_array($list)) {
  119. foreach ($list as $key => $val) {
  120. echo "($key) ";
  121. echo imap_utf7_decode($val->name) . ",";
  122. echo "'" . $val->delimiter . "',";
  123. echo $val->attributes . "<br />\n";
  124. }
  125. } else {
  126. error_log("ERROR imap_getmailboxes failed: ".var_export(imap_last_error()));
  127. }
  128. }
  129. }
  130. /**
  131. * This function causes a fetch of the complete, unfiltered RFC2822 format header of the specified message.
  132. * @param $messagenum Int
  133. * @return string
  134. */
  135. public function emailHeaders($messagenum) {
  136. return imap_fetchheader($this->_connection, $messagenum);
  137. }
  138. /**
  139. * return the email headers by given emailid
  140. * @param $messagenum
  141. * @return object
  142. */
  143. public function emailHeaders_rfc822($messagenum) {
  144. return imap_rfc822_parse_headers($this->emailHeaders($messagenum));
  145. }
  146. /**
  147. * Email headers parsed as an array
  148. * @param $messagenum
  149. * @return array
  150. */
  151. public function emailHeadersAsArray($messagenum) {
  152. preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m', $this->emailHeaders($messagenum), $matches );
  153. return array_combine( $matches[1], $matches[2]);
  154. }
  155. /**
  156. * Move given message to given folder
  157. * @param $messageUid This is the message Uid as an int
  158. * @param string $folder This is the target folder. Default is EMAIL_ARCHIVE_FOLDER
  159. */
  160. public function moveMessage($messageUid,$folder=EMAIL_ARCHIVE_FOLDER) {
  161. if(!empty($messageUid) && !empty($folder)) {
  162. $messageUid = (string)$messageUid;
  163. imap_setflag_full($this->_connection,$messageUid,"\SEEN", ST_UID);
  164. imap_mail_move($this->_connection, $messageUid, $folder,CP_UID);
  165. imap_expunge($this->_connection);
  166. }
  167. }
  168. /**
  169. * extract the subject from the email headers and decode
  170. * A subject can be split into multiple parts...
  171. *
  172. * @param int $messagenum
  173. * @return string
  174. */
  175. private function _extractSubject($messagenum) {
  176. $ret = '';
  177. $headerinfo = $this->emailHeaders_rfc822($messagenum);
  178. $subjectArr = imap_mime_header_decode($headerinfo->subject);
  179. foreach ($subjectArr as $el) {
  180. $ret .= $el->text;
  181. }
  182. return $ret;
  183. }
  184. /**
  185. * extract the body of the given message
  186. * @see http://php.net/manual/en/function.imap-fetchstructure.php
  187. *
  188. * @param int $messagenum
  189. * @return string
  190. */
  191. private function _extractBody($messagenum) {
  192. $ret = '';
  193. $emailstructure = imap_fetchstructure($this->_connection, $messagenum);
  194. # simple or multipart?
  195. if(isset($emailstructure->parts)) {
  196. exit("multipart todo");
  197. }
  198. else {
  199. $body = imap_body($this->_connection, $messagenum);
  200. }
  201. # encoding
  202. switch ($emailstructure->encoding) {
  203. case ENC8BIT: # 1 8BIT
  204. $ret = quoted_printable_decode(imap_8bit($body));
  205. break;
  206. case ENCBINARY: # 2 BINARY
  207. $ret = imap_binary($body);
  208. break;
  209. case ENCBASE64: # 3 BASE64
  210. $ret = imap_base64($body);
  211. break;
  212. case ENCQUOTEDPRINTABLE: # 4 QUOTED-PRINTABLE
  213. $ret = quoted_printable_decode($body);
  214. break;
  215. case ENC7BIT: # 0 7BIT
  216. $ret = imap_qprint($body);
  217. break;
  218. case ENCOTHER: # 5 OTHER
  219. default: # UNKNOWN
  220. $ret = $body;
  221. }
  222. return $ret;
  223. }
  224. /**
  225. * close the imap connection
  226. */
  227. function close() {
  228. imap_close($this->_connection);
  229. }
  230. }