PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/Core/Emails/Network/ImapSocket.php

http://github.com/infinitas/infinitas
PHP | 417 lines | 312 code | 58 blank | 47 comment | 33 complexity | 8711384721bb9f72e0d1df4f533a2550 MD5 | raw file
  1. <?php
  2. App::uses('EmailSocket', 'Emails.Network');
  3. /**
  4. * A pop3 driver for the email socket to recive emails without the php-imap extention
  5. *
  6. * This class implements the methods of the pop3 protocol using the EmailSocket
  7. * class to do the communication between the servers. See the links for more
  8. * information about the pop3 protocol
  9. *
  10. * @link http://en.wikipedia.org/wiki/Internet_Message_Access_Protocol
  11. */
  12. class ImapSocket extends EmailSocket {
  13. /**
  14. * counter for message sending
  15. *
  16. * All messages are given a suffix of Axxxxx where x is an int starting at 0
  17. *
  18. * @var integer
  19. */
  20. protected $_counter = 0;
  21. /**
  22. * current mailbox
  23. *
  24. * @var string
  25. */
  26. protected $_mailbox = null;
  27. /**
  28. * mailbox flags
  29. *
  30. * @var array
  31. */
  32. protected $_flags = array();
  33. /**
  34. * @copydoc EmailSocket::login()
  35. */
  36. public function login() {
  37. if (!parent::login()) {
  38. return false;
  39. }
  40. if (!$this->write(sprintf('LOGIN %s %s', $this->config['username'], $this->config['password']), 'isOk')) {
  41. $this->error(sprintf('There seems to be a problem with the username (%s)', $this->config['username']));
  42. return false;
  43. }
  44. $this->_getMailboxes();
  45. $this->_selectMailbox();
  46. $this->_getStats();
  47. $this->_getList();
  48. return true;
  49. }
  50. protected function _selectMailbox($mailbox = 'INBOX') {
  51. $mailbox = $this->write(sprintf('SELECT %s', $mailbox), 'cleanMailbox');
  52. $this->_mailbox = $mailbox;
  53. }
  54. protected function _cleanMailbox($data) {
  55. if (empty($this->_flags[$this->_mailbox])) {
  56. $this->_flags[$this->_mailbox] = $this->_getFlags($data);
  57. }
  58. }
  59. protected function _getFlags($data) {
  60. preg_match_all('/([a-z]+)/i', current(explode("\n", $data, 2)), $flags);
  61. if (!empty($flags[1])) {
  62. foreach ($flags[1] as $k => $flag) {
  63. if (in_array($flag, array('FLAGS', 'FETCH'))) {
  64. unset($flags[1][$k]);
  65. }
  66. }
  67. sort($flags[1]);
  68. return $flags[1];
  69. }
  70. return array();
  71. }
  72. public function getMail($id) {
  73. if (empty($this->_emails[$this->_mailbox][$id])) {
  74. return false;
  75. }
  76. $email = $this->write(sprintf('FETCH %s BODY[text]', $id), 'getMail');
  77. $header = $this->write(sprintf('FETCH %s BODY.PEEK[HEADER.FIELDS (SUBJECT DATE FROM TO)]', $id), 'getHeader');
  78. $email['headers'] = array(
  79. 'dkim_signature' => '',
  80. 'domainkey_signature' => '',
  81. 'spam_status' => '',
  82. 'spam_level' => '',
  83. 'delivery_date' => array(
  84. 'date_time' => '',
  85. 'time_zone' => ''
  86. ),
  87. 'date' => $header['date'],
  88. 'received' => '',
  89. 'from' => $header['from'],
  90. 'subject' => $header['subject'],
  91. 'to' => $header['to']
  92. );
  93. $email['sizeReadable'] = convert($email['details']['html']['size']);
  94. exit;
  95. return array_merge($this->_emails[$this->_mailbox][$id], $email);
  96. }
  97. protected function _getMail($data) {
  98. debug($data);
  99. $id = array();
  100. preg_match('/--([a-f0-9]+)/', $data, $id);
  101. if (empty($id[1])) {
  102. return array();
  103. }
  104. $parts = array();
  105. list($id, $parts[0], $parts[1]) = explode($id[0], $data);
  106. preg_match('/\{([0-9]+)\}/', $id, $id);
  107. $return = array();
  108. foreach ($parts as $part) {
  109. list($mime, $part) = explode("\n", trim($part), 2);
  110. $part = trim($part);
  111. $_mime = $_charset = array();
  112. preg_match('/text\/([a-z]+);/', $mime, $_mime);
  113. preg_match('/charset=(.*)$/', $mime, $_charset);
  114. $return['details'][$_mime[1]] = array(
  115. 'content' => $part,
  116. 'charset' => !empty($_charset[1]) ? trim($_charset[1]) : null,
  117. 'type' => !empty($_mime[1]) ? trim($_mime[1]) : null,
  118. 'size' => strlen($part)
  119. );
  120. $return[$_mime[1]] = $part;
  121. }
  122. return $return;
  123. }
  124. protected function _getHeader($data) {
  125. $data = explode("\n", $data);
  126. $return = array();
  127. foreach ($data as $k => &$v) {
  128. $v = trim($v);
  129. if (empty($v) || $v == ')') {
  130. unset($data[$k]);
  131. continue;
  132. }
  133. $array = array(
  134. 'from' => '/^From: (.*)$/',
  135. 'date' => '/^Date: (.*)$/',
  136. 'subject' => '/^Subject: (.*)$/',
  137. 'to' => '/^To: (.*)$/'
  138. );
  139. foreach ($array as $type => $regex) {
  140. $matches = array();
  141. if (preg_match($regex, $v, $matches)) {
  142. $matches[1] = trim($matches[1]);
  143. $return[$type] = $matches[1];
  144. if ($type == 'date') {
  145. $timezone = array();
  146. preg_match('/(\+[0-9]+)$/', $matches[1], $timezone);
  147. $return[$type] = array(
  148. 'date_time' => date('Y-m-d H:i:s', strtotime($matches[1])),
  149. 'time_zone' => $timezone[1]
  150. );
  151. } else if ($type == 'from') {
  152. $email = array();
  153. preg_match('/<(.*)>$/', $matches[1], $email);
  154. $return[$type] = array(
  155. 'name' => trim(str_replace(sprintf('<%s>', $email[1]), '', $matches[1])),
  156. 'email' => $email[1]
  157. );
  158. }
  159. }
  160. }
  161. }
  162. return array_merge(array(
  163. 'from' => null,
  164. 'date' => array(),
  165. 'subject' => null,
  166. 'to' => null
  167. ), $return);
  168. }
  169. /**
  170. * @copydoc EmailSocket::logout()
  171. */
  172. public function logout() {
  173. if (!$this->Socket->isConnected()) {
  174. $this->_errors[] = 'Can not logout, no connection';
  175. return true;
  176. }
  177. $quit = $this->write('QUIT', 'isOk');
  178. if (!$quit) {
  179. $this->_errors[] = 'Could not log out';
  180. }
  181. return $quit;
  182. }
  183. protected function _getStats() {
  184. foreach ($this->write('UID SEARCH ALL', 'mailIds') as $k => $mailId) {
  185. $this->_emails[$this->_mailbox][$k+1] = array(
  186. 'uuid' => $mailId,
  187. 'id' => $mailId,
  188. 'message_number' => $k + 1
  189. );
  190. }
  191. $this->mailStats['totalCount'] = count($this->_emails[$this->_mailbox]);
  192. $this->mailStats['totalSize'] = 0;
  193. unset($stats);
  194. if ($this->mailStats['totalSize'] > 0) {
  195. $this->mailStats['totalSizeReadable'] = convert($this->mailStats['totalSize']);
  196. }
  197. return true;
  198. }
  199. protected function _mailIds($data) {
  200. $mailIds = array_filter(explode(' ', str_replace('* SEARCH', '', current(explode("\n", $data, 2)))));
  201. array_walk($mailIds, function(&$row) {
  202. $row = trim($row);
  203. });
  204. return $mailIds;
  205. }
  206. protected function _cleanSize($data) {
  207. return 0;
  208. }
  209. protected function _getList() {
  210. $i = 0;
  211. foreach ($this->_emails[$this->_mailbox] as $mailId => &$mail) {
  212. $mail['message_number'] = $i++;
  213. $mail['flags'] = $this->write(sprintf('FETCH %s (flags)', $i), 'getFlags');
  214. $mail['id'] = $mailId;
  215. $mail['size'] = $this->write(sprintf('FETCH %d RFC822.SIZE', $mailId), 'cleanSize');
  216. $mail['sizeReadable'] = 0;
  217. $mail['uid'] = '';
  218. }
  219. return true;
  220. }
  221. protected function _list($data) {
  222. $data = explode("\n", $data);
  223. array_shift($data);
  224. foreach ($data as $k => &$v) {
  225. $v = trim($v);
  226. if (empty($v) || $v == ')') {
  227. unset($data[$k]);
  228. continue;
  229. }
  230. $array = array(
  231. 'From',
  232. 'Date',
  233. 'Subject',
  234. 'To'
  235. );
  236. var_dump($v);
  237. exit;
  238. }
  239. var_dump($data);
  240. exit;
  241. }
  242. protected function _getCapabilities() {
  243. $cache = $this->readCache('capabilities');
  244. if ($cache) {
  245. $this->_capabilities = $cache;
  246. return true;
  247. }
  248. $capabilities = $this->write('CAPABILITY', 'cleanCapabilities');
  249. if (empty($capabilities)) {
  250. return false;
  251. }
  252. foreach ($capabilities as $capability) {
  253. switch($capability) {
  254. case 'IMAP4rev1':
  255. case 'UNSELECT':
  256. case 'IDLE':
  257. case 'NAMESPACE':
  258. case 'QUOTA':
  259. case 'ID':
  260. case 'XLIST':
  261. case 'X-GM-EXT-1':
  262. case 'XYZZY':
  263. case 'SASL-IR':
  264. case 'XOAUTH':
  265. case 'XOAUTH2':
  266. $this->_capabilities[$capability] = 1;
  267. break;
  268. }
  269. }
  270. return $this->writeCache('capabilities', $this->_capabilities);
  271. }
  272. protected function _cleanCapabilities($data) {
  273. if (empty($this->_cap)) {
  274. $this->_cap = explode(' ', current(explode("\n", $data, 2)));
  275. if (empty($this->_cap)) {
  276. return array();
  277. }
  278. array_walk($this->_cap, function(&$cap) {
  279. $cap = trim($cap);
  280. });
  281. foreach ($this->_cap as $k => $v) {
  282. if (in_array($v, array('*', 'CAPABILITY'))) {
  283. unset($this->_cap[$k]);
  284. continue;
  285. }
  286. if (strstr($v, 'AUTH=')) {
  287. $this->_cap[] = str_replace('AUTH=', '', $v);
  288. unset($this->_cap[$k]);
  289. continue;
  290. }
  291. }
  292. }
  293. return $this->_cap;
  294. }
  295. public function write($data, $method = false, $size = 1024) {
  296. $data = sprintf('%s %s', $this->_counter(), $data);
  297. return parent::write($data, $method, $size);
  298. }
  299. public function read($size = 1024, $method = false) {
  300. $data = null;
  301. $count = 0;
  302. $counter = $this->_counter;
  303. $match = false;
  304. while(!$match) {
  305. $data = $this->_read($size, false);
  306. $match = preg_match(sprintf('/%s /', $counter), $data);
  307. if (!$match) {
  308. $this->noop();
  309. }
  310. }
  311. $_method = '_' . $method;
  312. if ($method && is_callable(array($this, $_method))) {
  313. return $this->{$_method}($data);
  314. }
  315. return $data;
  316. }
  317. protected function _counter() {
  318. return 'A' . str_pad($this->_counter++, 4, '0', STR_PAD_LEFT);
  319. }
  320. /**
  321. * get a list of available mail boxes
  322. *
  323. * @param string $ref
  324. * @param string $wildcard
  325. *
  326. * @return void
  327. */
  328. protected function _getMailboxes($ref = '""', $wildcard = '%') {
  329. $this->_mailboxes = $this->write(sprintf('LIST %s %s', $ref, $wildcard), 'cleanMailboxes');
  330. }
  331. protected function _cleanMailboxes($data) {
  332. $matches = array();
  333. preg_match_all('/"\/" "(.*)"/', $data, $matches);
  334. if (!empty($matches[1])) {
  335. return $matches[1];
  336. }
  337. return array();
  338. }
  339. /**
  340. * send NOOP command, optional sleep time
  341. *
  342. * @param integer $sleep the microtime to sleep for (1/1000000 of second)
  343. *
  344. * @return string
  345. */
  346. public function noop($sleep = 100000) {
  347. $noop = $this->write('NOOP', false, 0);
  348. if ($sleep) {
  349. usleep($sleep);
  350. }
  351. return $noop;
  352. }
  353. public function undoDeletes() {
  354. throw new Exception(__FUNCTION__);
  355. }
  356. protected function _isOk($data) {
  357. return (bool)preg_match('/A[0-9]+ OK /', $data);
  358. }
  359. }