simple-imap.class.php 8.1 KB

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