Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Imap.php
Go to the documentation of this file.
1 <?php
32 {
36  const TIMEOUT_CONNECTION = 30;
37 
42  protected $_socket;
43 
48  protected $_tagCount = 0;
49 
58  function __construct($host = '', $port = null, $ssl = false)
59  {
60  if ($host) {
61  $this->connect($host, $port, $ssl);
62  }
63  }
64 
68  public function __destruct()
69  {
70  $this->logout();
71  }
72 
82  public function connect($host, $port = null, $ssl = false)
83  {
84  if ($ssl == 'SSL') {
85  $host = 'ssl://' . $host;
86  }
87 
88  if ($port === null) {
89  $port = $ssl === 'SSL' ? 993 : 143;
90  }
91 
92  $errno = 0;
93  $errstr = '';
94  $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
95  if (!$this->_socket) {
99  #require_once 'Zend/Mail/Protocol/Exception.php';
100  throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
101  ' (errno = ' . $errno . ' )');
102  }
103 
104  if (!$this->_assumedNextLine('* OK')) {
108  #require_once 'Zend/Mail/Protocol/Exception.php';
109  throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection');
110  }
111 
112  if ($ssl === 'TLS') {
113  $result = $this->requestAndResponse('STARTTLS');
114  // TODO: Add STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT in the future when it is supported by PHP
115  $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
116  if (!$result) {
120  #require_once 'Zend/Mail/Protocol/Exception.php';
121  throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
122  }
123  }
124  }
125 
132  protected function _nextLine()
133  {
134  $line = @fgets($this->_socket);
135  if ($line === false) {
139  #require_once 'Zend/Mail/Protocol/Exception.php';
140  throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?');
141  }
142 
143  return $line;
144  }
145 
154  protected function _assumedNextLine($start)
155  {
156  $line = $this->_nextLine();
157  return strpos($line, $start) === 0;
158  }
159 
167  protected function _nextTaggedLine(&$tag)
168  {
169  $line = $this->_nextLine();
170 
171  // seperate tag from line
172  list($tag, $line) = explode(' ', $line, 2);
173 
174  return $line;
175  }
176 
184  protected function _decodeLine($line)
185  {
186  $tokens = array();
187  $stack = array();
188 
189  /*
190  We start to decode the response here. The unterstood tokens are:
191  literal
192  "literal" or also "lit\\er\"al"
193  {bytes}<NL>literal
194  (literals*)
195  All tokens are returned in an array. Literals in braces (the last unterstood
196  token in the list) are returned as an array of tokens. I.e. the following response:
197  "foo" baz {3}<NL>bar ("f\\\"oo" bar)
198  would be returned as:
199  array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
200 
201  // TODO: add handling of '[' and ']' to parser for easier handling of response text
202  */
203  // replace any trailling <NL> including spaces with a single space
204  $line = rtrim($line) . ' ';
205  while (($pos = strpos($line, ' ')) !== false) {
206  $token = substr($line, 0, $pos);
207  while ($token[0] == '(') {
208  array_push($stack, $tokens);
209  $tokens = array();
210  $token = substr($token, 1);
211  }
212  if ($token[0] == '"') {
213  if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
214  $tokens[] = $matches[1];
215  $line = substr($line, strlen($matches[0]));
216  continue;
217  }
218  }
219  if ($token[0] == '{') {
220  $endPos = strpos($token, '}');
221  $chars = substr($token, 1, $endPos - 1);
222  if (is_numeric($chars)) {
223  $token = '';
224  while (strlen($token) < $chars) {
225  $token .= $this->_nextLine();
226  }
227  $line = '';
228  if (strlen($token) > $chars) {
229  $line = substr($token, $chars);
230  $token = substr($token, 0, $chars);
231  } else {
232  $line .= $this->_nextLine();
233  }
234  $tokens[] = $token;
235  $line = trim($line) . ' ';
236  continue;
237  }
238  }
239  if ($stack && $token[strlen($token) - 1] == ')') {
240  // closing braces are not seperated by spaces, so we need to count them
241  $braces = strlen($token);
242  $token = rtrim($token, ')');
243  // only count braces if more than one
244  $braces -= strlen($token) + 1;
245  // only add if token had more than just closing braces
246  if (rtrim($token) != '') {
247  $tokens[] = rtrim($token);
248  }
249  $token = $tokens;
250  $tokens = array_pop($stack);
251  // special handline if more than one closing brace
252  while ($braces-- > 0) {
253  $tokens[] = $token;
254  $token = $tokens;
255  $tokens = array_pop($stack);
256  }
257  }
258  $tokens[] = $token;
259  $line = substr($line, $pos + 1);
260  }
261 
262  // maybe the server forgot to send some closing braces
263  while ($stack) {
264  $child = $tokens;
265  $tokens = array_pop($stack);
266  $tokens[] = $child;
267  }
268 
269  return $tokens;
270  }
271 
284  public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
285  {
286  $line = $this->_nextTaggedLine($tag);
287  if (!$dontParse) {
288  $tokens = $this->_decodeLine($line);
289  } else {
290  $tokens = $line;
291  }
292 
293  // if tag is wanted tag we might be at the end of a multiline response
294  return $tag == $wantedTag;
295  }
296 
308  public function readResponse($tag, $dontParse = false)
309  {
310  $lines = array();
311  while (!$this->readLine($tokens, $tag, $dontParse)) {
312  $lines[] = $tokens;
313  }
314 
315  if ($dontParse) {
316  // last to chars are still needed for response code
317  $tokens = array(substr($tokens, 0, 2));
318  }
319  // last line has response code
320  if ($tokens[0] == 'OK') {
321  return $lines ? $lines : true;
322  } else if ($tokens[0] == 'NO'){
323  return false;
324  }
325  return null;
326  }
327 
337  public function sendRequest($command, $tokens = array(), &$tag = null)
338  {
339  if (!$tag) {
341  $tag = 'TAG' . $this->_tagCount;
342  }
343 
344  $line = $tag . ' ' . $command;
345 
346  foreach ($tokens as $token) {
347  if (is_array($token)) {
348  if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) {
352  #require_once 'Zend/Mail/Protocol/Exception.php';
353  throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
354  }
355  if (!$this->_assumedNextLine('+ ')) {
359  #require_once 'Zend/Mail/Protocol/Exception.php';
360  throw new Zend_Mail_Protocol_Exception('cannot send literal string');
361  }
362  $line = $token[1];
363  } else {
364  $line .= ' ' . $token;
365  }
366  }
367 
368  if (@fputs($this->_socket, $line . "\r\n") === false) {
372  #require_once 'Zend/Mail/Protocol/Exception.php';
373  throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
374  }
375  }
376 
386  public function requestAndResponse($command, $tokens = array(), $dontParse = false)
387  {
388  $this->sendRequest($command, $tokens, $tag);
389  $response = $this->readResponse($tag, $dontParse);
390 
391  return $response;
392  }
393 
401  public function escapeString($string)
402  {
403  if (func_num_args() < 2) {
404  if (strpos($string, "\n") !== false) {
405  return array('{' . strlen($string) . '}', $string);
406  } else {
407  return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
408  }
409  }
410  $result = array();
411  foreach (func_get_args() as $string) {
412  $result[] = $this->escapeString($string);
413  }
414  return $result;
415  }
416 
423  public function escapeList($list)
424  {
425  $result = array();
426  foreach ($list as $k => $v) {
427  if (!is_array($v)) {
428 // $result[] = $this->escapeString($v);
429  $result[] = $v;
430  continue;
431  }
432  $result[] = $this->escapeList($v);
433  }
434  return '(' . implode(' ', $result) . ')';
435  }
436 
445  public function login($user, $password)
446  {
447  return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
448  }
449 
455  public function logout()
456  {
457  $result = false;
458  if ($this->_socket) {
459  try {
460  $result = $this->requestAndResponse('LOGOUT', array(), true);
461  } catch (Zend_Mail_Protocol_Exception $e) {
462  // ignoring exception
463  }
464  fclose($this->_socket);
465  $this->_socket = null;
466  }
467  return $result;
468  }
469 
470 
477  public function capability()
478  {
479  $response = $this->requestAndResponse('CAPABILITY');
480 
481  if (!$response) {
482  return $response;
483  }
484 
485  $capabilities = array();
486  foreach ($response as $line) {
487  $capabilities = array_merge($capabilities, $line);
488  }
489  return $capabilities;
490  }
491 
502  public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
503  {
504  $this->sendRequest($command, array($this->escapeString($box)), $tag);
505 
506  $result = array();
507  while (!$this->readLine($tokens, $tag)) {
508  if ($tokens[0] == 'FLAGS') {
509  array_shift($tokens);
510  $result['flags'] = $tokens;
511  continue;
512  }
513  switch ($tokens[1]) {
514  case 'EXISTS':
515  case 'RECENT':
516  $result[strtolower($tokens[1])] = $tokens[0];
517  break;
518  case '[UIDVALIDITY':
519  $result['uidvalidity'] = (int)$tokens[2];
520  break;
521  default:
522  // ignore
523  }
524  }
525 
526  if ($tokens[0] != 'OK') {
527  return false;
528  }
529  return $result;
530  }
531 
539  public function select($box = 'INBOX')
540  {
541  return $this->examineOrSelect('SELECT', $box);
542  }
543 
551  public function examine($box = 'INBOX')
552  {
553  return $this->examineOrSelect('EXAMINE', $box);
554  }
555 
570  public function fetch($items, $from, $to = null)
571  {
572  if (is_array($from)) {
573  $set = implode(',', $from);
574  } else if ($to === null) {
575  $set = (int)$from;
576  } else if ($to === INF) {
577  $set = (int)$from . ':*';
578  } else {
579  $set = (int)$from . ':' . (int)$to;
580  }
581 
582  $items = (array)$items;
583  $itemList = $this->escapeList($items);
584 
585  $this->sendRequest('FETCH', array($set, $itemList), $tag);
586 
587  $result = array();
588  while (!$this->readLine($tokens, $tag)) {
589  // ignore other responses
590  if ($tokens[1] != 'FETCH') {
591  continue;
592  }
593  // ignore other messages
594  if ($to === null && !is_array($from) && $tokens[0] != $from) {
595  continue;
596  }
597  // if we only want one item we return that one directly
598  if (count($items) == 1) {
599  if ($tokens[2][0] == $items[0]) {
600  $data = $tokens[2][1];
601  } else {
602  // maybe the server send an other field we didn't wanted
603  $count = count($tokens[2]);
604  // we start with 2, because 0 was already checked
605  for ($i = 2; $i < $count; $i += 2) {
606  if ($tokens[2][$i] != $items[0]) {
607  continue;
608  }
609  $data = $tokens[2][$i + 1];
610  break;
611  }
612  }
613  } else {
614  $data = array();
615  while (key($tokens[2]) !== null) {
616  $data[current($tokens[2])] = next($tokens[2]);
617  next($tokens[2]);
618  }
619  }
620  // if we want only one message we can ignore everything else and just return
621  if ($to === null && !is_array($from) && $tokens[0] == $from) {
622  // we still need to read all lines
623  while (!$this->readLine($tokens, $tag));
624  return $data;
625  }
626  $result[$tokens[0]] = $data;
627  }
628 
629  if ($to === null && !is_array($from)) {
633  #require_once 'Zend/Mail/Protocol/Exception.php';
634  throw new Zend_Mail_Protocol_Exception('the single id was not found in response');
635  }
636 
637  return $result;
638  }
639 
650  public function listMailbox($reference = '', $mailbox = '*')
651  {
652  $result = array();
653  $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
654  if (!$list || $list === true) {
655  return $result;
656  }
657 
658  foreach ($list as $item) {
659  if (count($item) != 4 || $item[0] != 'LIST') {
660  continue;
661  }
662  $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
663  }
664 
665  return $result;
666  }
667 
680  public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
681  {
682  $item = 'FLAGS';
683  if ($mode == '+' || $mode == '-') {
684  $item = $mode . $item;
685  }
686  if ($silent) {
687  $item .= '.SILENT';
688  }
689 
690  $flags = $this->escapeList($flags);
691  $set = (int)$from;
692  if ($to != null) {
693  $set .= ':' . ($to == INF ? '*' : (int)$to);
694  }
695 
696  $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
697 
698  if ($silent) {
699  return $result ? true : false;
700  }
701 
702  $tokens = $result;
703  $result = array();
704  foreach ($tokens as $token) {
705  if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
706  continue;
707  }
708  $result[$token[0]] = $token[2][1];
709  }
710 
711  return $result;
712  }
713 
724  public function append($folder, $message, $flags = null, $date = null)
725  {
726  $tokens = array();
727  $tokens[] = $this->escapeString($folder);
728  if ($flags !== null) {
729  $tokens[] = $this->escapeList($flags);
730  }
731  if ($date !== null) {
732  $tokens[] = $this->escapeString($date);
733  }
734  $tokens[] = $this->escapeString($message);
735 
736  return $this->requestAndResponse('APPEND', $tokens, true);
737  }
738 
748  public function copy($folder, $from, $to = null)
749  {
750  $set = (int)$from;
751  if ($to != null) {
752  $set .= ':' . ($to == INF ? '*' : (int)$to);
753  }
754 
755  return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true);
756  }
757 
764  public function create($folder)
765  {
766  return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true);
767  }
768 
776  public function rename($old, $new)
777  {
778  return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
779  }
780 
787  public function delete($folder)
788  {
789  return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true);
790  }
791 
797  public function expunge()
798  {
799  // TODO: parse response?
800  return $this->requestAndResponse('EXPUNGE');
801  }
802 
808  public function noop()
809  {
810  // TODO: parse response
811  return $this->requestAndResponse('NOOP');
812  }
813 
823  public function search(array $params)
824  {
825  $response = $this->requestAndResponse('SEARCH', $params);
826  if (!$response) {
827  return $response;
828  }
829 
830  foreach ($response as $ids) {
831  if ($ids[0] == 'SEARCH') {
832  array_shift($ids);
833  return $ids;
834  }
835  }
836  return array();
837  }
838 
839 }
$old
Definition: website.php:27
$response
Definition: 404.php:11
connect($host, $port=null, $ssl=false)
Definition: Imap.php:82
rename($old, $new)
Definition: Imap.php:776
readResponse($tag, $dontParse=false)
Definition: Imap.php:308
fsockopen(&$errorNumber, &$errorMessage)
Definition: http_mock.php:37
readLine(&$tokens=array(), $wantedTag=' *', $dontParse=false)
Definition: Imap.php:284
escapeString($string)
Definition: Imap.php:401
requestAndResponse($command, $tokens=array(), $dontParse=false)
Definition: Imap.php:386
$count
Definition: recent.phtml:13
store(array $flags, $from, $to=null, $mode=null, $silent=true)
Definition: Imap.php:680
$start
Definition: listing.phtml:18
$message
_assumedNextLine($start)
Definition: Imap.php:154
append($folder, $message, $flags=null, $date=null)
Definition: Imap.php:724
sendRequest($command, $tokens=array(), &$tag=null)
Definition: Imap.php:337
const TIMEOUT_CONNECTION
Definition: Imap.php:36
fetch($items, $from, $to=null)
Definition: Imap.php:570
$user
Definition: dummy_user.php:13
$pos
Definition: list.phtml:42
if($exist=($block->getProductCollection() && $block->getProductCollection() ->getSize())) $mode
Definition: grid.phtml:15
examine($box='INBOX')
Definition: Imap.php:551
__construct($host='', $port=null, $ssl=false)
Definition: Imap.php:58
search(array $params)
Definition: Imap.php:823
listMailbox($reference='', $mailbox=' *')
Definition: Imap.php:650
copy($folder, $from, $to=null)
Definition: Imap.php:748
examineOrSelect($command='EXAMINE', $box='INBOX')
Definition: Imap.php:502
login($user, $password)
Definition: Imap.php:445
_nextTaggedLine(&$tag)
Definition: Imap.php:167
select($box='INBOX')
Definition: Imap.php:539
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
Definition: website.php:18
$i
Definition: gallery.phtml:31
$tokens
Definition: cards_list.phtml:9
$items