PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Zend/Mail/Protocol/Imap.php

https://bitbucket.org/sunil_nextbits/magento2
PHP | 838 lines | 423 code | 71 blank | 344 comment | 114 complexity | d79c4717ef8ea46e792865e55d00bffd MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Mail
  17. * @subpackage Protocol
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Imap.php 20096 2010-01-06 02:05:09Z bkarwin $
  21. */
  22. /**
  23. * @category Zend
  24. * @package Zend_Mail
  25. * @subpackage Protocol
  26. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  27. * @license http://framework.zend.com/license/new-bsd New BSD License
  28. */
  29. class Zend_Mail_Protocol_Imap
  30. {
  31. /**
  32. * Default timeout in seconds for initiating session
  33. */
  34. const TIMEOUT_CONNECTION = 30;
  35. /**
  36. * socket to imap server
  37. * @var resource|null
  38. */
  39. protected $_socket;
  40. /**
  41. * counter for request tag
  42. * @var int
  43. */
  44. protected $_tagCount = 0;
  45. /**
  46. * Public constructor
  47. *
  48. * @param string $host hostname or IP address of IMAP server, if given connect() is called
  49. * @param int|null $port port of IMAP server, null for default (143 or 993 for ssl)
  50. * @param bool $ssl use ssl? 'SSL', 'TLS' or false
  51. * @throws Zend_Mail_Protocol_Exception
  52. */
  53. function __construct($host = '', $port = null, $ssl = false)
  54. {
  55. if ($host) {
  56. $this->connect($host, $port, $ssl);
  57. }
  58. }
  59. /**
  60. * Public destructor
  61. */
  62. public function __destruct()
  63. {
  64. $this->logout();
  65. }
  66. /**
  67. * Open connection to IMAP server
  68. *
  69. * @param string $host hostname or IP address of IMAP server
  70. * @param int|null $port of IMAP server, default is 143 (993 for ssl)
  71. * @param string|bool $ssl use 'SSL', 'TLS' or false
  72. * @return string welcome message
  73. * @throws Zend_Mail_Protocol_Exception
  74. */
  75. public function connect($host, $port = null, $ssl = false)
  76. {
  77. if ($ssl == 'SSL') {
  78. $host = 'ssl://' . $host;
  79. }
  80. if ($port === null) {
  81. $port = $ssl === 'SSL' ? 993 : 143;
  82. }
  83. $errno = 0;
  84. $errstr = '';
  85. $this->_socket = @fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
  86. if (!$this->_socket) {
  87. /**
  88. * @see Zend_Mail_Protocol_Exception
  89. */
  90. #require_once 'Zend/Mail/Protocol/Exception.php';
  91. throw new Zend_Mail_Protocol_Exception('cannot connect to host; error = ' . $errstr .
  92. ' (errno = ' . $errno . ' )');
  93. }
  94. if (!$this->_assumedNextLine('* OK')) {
  95. /**
  96. * @see Zend_Mail_Protocol_Exception
  97. */
  98. #require_once 'Zend/Mail/Protocol/Exception.php';
  99. throw new Zend_Mail_Protocol_Exception('host doesn\'t allow connection');
  100. }
  101. if ($ssl === 'TLS') {
  102. $result = $this->requestAndResponse('STARTTLS');
  103. $result = $result && stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  104. if (!$result) {
  105. /**
  106. * @see Zend_Mail_Protocol_Exception
  107. */
  108. #require_once 'Zend/Mail/Protocol/Exception.php';
  109. throw new Zend_Mail_Protocol_Exception('cannot enable TLS');
  110. }
  111. }
  112. }
  113. /**
  114. * get the next line from socket with error checking, but nothing else
  115. *
  116. * @return string next line
  117. * @throws Zend_Mail_Protocol_Exception
  118. */
  119. protected function _nextLine()
  120. {
  121. $line = @fgets($this->_socket);
  122. if ($line === false) {
  123. /**
  124. * @see Zend_Mail_Protocol_Exception
  125. */
  126. #require_once 'Zend/Mail/Protocol/Exception.php';
  127. throw new Zend_Mail_Protocol_Exception('cannot read - connection closed?');
  128. }
  129. return $line;
  130. }
  131. /**
  132. * get next line and assume it starts with $start. some requests give a simple
  133. * feedback so we can quickly check if we can go on.
  134. *
  135. * @param string $start the first bytes we assume to be in the next line
  136. * @return bool line starts with $start
  137. * @throws Zend_Mail_Protocol_Exception
  138. */
  139. protected function _assumedNextLine($start)
  140. {
  141. $line = $this->_nextLine();
  142. return strpos($line, $start) === 0;
  143. }
  144. /**
  145. * get next line and split the tag. that's the normal case for a response line
  146. *
  147. * @param string $tag tag of line is returned by reference
  148. * @return string next line
  149. * @throws Zend_Mail_Protocol_Exception
  150. */
  151. protected function _nextTaggedLine(&$tag)
  152. {
  153. $line = $this->_nextLine();
  154. // seperate tag from line
  155. list($tag, $line) = explode(' ', $line, 2);
  156. return $line;
  157. }
  158. /**
  159. * split a given line in tokens. a token is literal of any form or a list
  160. *
  161. * @param string $line line to decode
  162. * @return array tokens, literals are returned as string, lists as array
  163. * @throws Zend_Mail_Protocol_Exception
  164. */
  165. protected function _decodeLine($line)
  166. {
  167. $tokens = array();
  168. $stack = array();
  169. /*
  170. We start to decode the response here. The unterstood tokens are:
  171. literal
  172. "literal" or also "lit\\er\"al"
  173. {bytes}<NL>literal
  174. (literals*)
  175. All tokens are returned in an array. Literals in braces (the last unterstood
  176. token in the list) are returned as an array of tokens. I.e. the following response:
  177. "foo" baz {3}<NL>bar ("f\\\"oo" bar)
  178. would be returned as:
  179. array('foo', 'baz', 'bar', array('f\\\"oo', 'bar'));
  180. // TODO: add handling of '[' and ']' to parser for easier handling of response text
  181. */
  182. // replace any trailling <NL> including spaces with a single space
  183. $line = rtrim($line) . ' ';
  184. while (($pos = strpos($line, ' ')) !== false) {
  185. $token = substr($line, 0, $pos);
  186. while ($token[0] == '(') {
  187. array_push($stack, $tokens);
  188. $tokens = array();
  189. $token = substr($token, 1);
  190. }
  191. if ($token[0] == '"') {
  192. if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) {
  193. $tokens[] = $matches[1];
  194. $line = substr($line, strlen($matches[0]));
  195. continue;
  196. }
  197. }
  198. if ($token[0] == '{') {
  199. $endPos = strpos($token, '}');
  200. $chars = substr($token, 1, $endPos - 1);
  201. if (is_numeric($chars)) {
  202. $token = '';
  203. while (strlen($token) < $chars) {
  204. $token .= $this->_nextLine();
  205. }
  206. $line = '';
  207. if (strlen($token) > $chars) {
  208. $line = substr($token, $chars);
  209. $token = substr($token, 0, $chars);
  210. } else {
  211. $line .= $this->_nextLine();
  212. }
  213. $tokens[] = $token;
  214. $line = trim($line) . ' ';
  215. continue;
  216. }
  217. }
  218. if ($stack && $token[strlen($token) - 1] == ')') {
  219. // closing braces are not seperated by spaces, so we need to count them
  220. $braces = strlen($token);
  221. $token = rtrim($token, ')');
  222. // only count braces if more than one
  223. $braces -= strlen($token) + 1;
  224. // only add if token had more than just closing braces
  225. if (rtrim($token) != '') {
  226. $tokens[] = rtrim($token);
  227. }
  228. $token = $tokens;
  229. $tokens = array_pop($stack);
  230. // special handline if more than one closing brace
  231. while ($braces-- > 0) {
  232. $tokens[] = $token;
  233. $token = $tokens;
  234. $tokens = array_pop($stack);
  235. }
  236. }
  237. $tokens[] = $token;
  238. $line = substr($line, $pos + 1);
  239. }
  240. // maybe the server forgot to send some closing braces
  241. while ($stack) {
  242. $child = $tokens;
  243. $tokens = array_pop($stack);
  244. $tokens[] = $child;
  245. }
  246. return $tokens;
  247. }
  248. /**
  249. * read a response "line" (could also be more than one real line if response has {..}<NL>)
  250. * and do a simple decode
  251. *
  252. * @param array|string $tokens decoded tokens are returned by reference, if $dontParse
  253. * is true the unparsed line is returned here
  254. * @param string $wantedTag check for this tag for response code. Default '*' is
  255. * continuation tag.
  256. * @param bool $dontParse if true only the unparsed line is returned $tokens
  257. * @return bool if returned tag matches wanted tag
  258. * @throws Zend_Mail_Protocol_Exception
  259. */
  260. public function readLine(&$tokens = array(), $wantedTag = '*', $dontParse = false)
  261. {
  262. $line = $this->_nextTaggedLine($tag);
  263. if (!$dontParse) {
  264. $tokens = $this->_decodeLine($line);
  265. } else {
  266. $tokens = $line;
  267. }
  268. // if tag is wanted tag we might be at the end of a multiline response
  269. return $tag == $wantedTag;
  270. }
  271. /**
  272. * read all lines of response until given tag is found (last line of response)
  273. *
  274. * @param string $tag the tag of your request
  275. * @param string|array $filter you can filter the response so you get only the
  276. * given response lines
  277. * @param bool $dontParse if true every line is returned unparsed instead of
  278. * the decoded tokens
  279. * @return null|bool|array tokens if success, false if error, null if bad request
  280. * @throws Zend_Mail_Protocol_Exception
  281. */
  282. public function readResponse($tag, $dontParse = false)
  283. {
  284. $lines = array();
  285. while (!$this->readLine($tokens, $tag, $dontParse)) {
  286. $lines[] = $tokens;
  287. }
  288. if ($dontParse) {
  289. // last to chars are still needed for response code
  290. $tokens = array(substr($tokens, 0, 2));
  291. }
  292. // last line has response code
  293. if ($tokens[0] == 'OK') {
  294. return $lines ? $lines : true;
  295. } else if ($tokens[0] == 'NO'){
  296. return false;
  297. }
  298. return null;
  299. }
  300. /**
  301. * send a request
  302. *
  303. * @param string $command your request command
  304. * @param array $tokens additional parameters to command, use escapeString() to prepare
  305. * @param string $tag provide a tag otherwise an autogenerated is returned
  306. * @return null
  307. * @throws Zend_Mail_Protocol_Exception
  308. */
  309. public function sendRequest($command, $tokens = array(), &$tag = null)
  310. {
  311. if (!$tag) {
  312. ++$this->_tagCount;
  313. $tag = 'TAG' . $this->_tagCount;
  314. }
  315. $line = $tag . ' ' . $command;
  316. foreach ($tokens as $token) {
  317. if (is_array($token)) {
  318. if (@fputs($this->_socket, $line . ' ' . $token[0] . "\r\n") === false) {
  319. /**
  320. * @see Zend_Mail_Protocol_Exception
  321. */
  322. #require_once 'Zend/Mail/Protocol/Exception.php';
  323. throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
  324. }
  325. if (!$this->_assumedNextLine('+ ')) {
  326. /**
  327. * @see Zend_Mail_Protocol_Exception
  328. */
  329. #require_once 'Zend/Mail/Protocol/Exception.php';
  330. throw new Zend_Mail_Protocol_Exception('cannot send literal string');
  331. }
  332. $line = $token[1];
  333. } else {
  334. $line .= ' ' . $token;
  335. }
  336. }
  337. if (@fputs($this->_socket, $line . "\r\n") === false) {
  338. /**
  339. * @see Zend_Mail_Protocol_Exception
  340. */
  341. #require_once 'Zend/Mail/Protocol/Exception.php';
  342. throw new Zend_Mail_Protocol_Exception('cannot write - connection closed?');
  343. }
  344. }
  345. /**
  346. * send a request and get response at once
  347. *
  348. * @param string $command command as in sendRequest()
  349. * @param array $tokens parameters as in sendRequest()
  350. * @param bool $dontParse if true unparsed lines are returned instead of tokens
  351. * @return mixed response as in readResponse()
  352. * @throws Zend_Mail_Protocol_Exception
  353. */
  354. public function requestAndResponse($command, $tokens = array(), $dontParse = false)
  355. {
  356. $this->sendRequest($command, $tokens, $tag);
  357. $response = $this->readResponse($tag, $dontParse);
  358. return $response;
  359. }
  360. /**
  361. * escape one or more literals i.e. for sendRequest
  362. *
  363. * @param string|array $string the literal/-s
  364. * @return string|array escape literals, literals with newline ar returned
  365. * as array('{size}', 'string');
  366. */
  367. public function escapeString($string)
  368. {
  369. if (func_num_args() < 2) {
  370. if (strpos($string, "\n") !== false) {
  371. return array('{' . strlen($string) . '}', $string);
  372. } else {
  373. return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $string) . '"';
  374. }
  375. }
  376. $result = array();
  377. foreach (func_get_args() as $string) {
  378. $result[] = $this->escapeString($string);
  379. }
  380. return $result;
  381. }
  382. /**
  383. * escape a list with literals or lists
  384. *
  385. * @param array $list list with literals or lists as PHP array
  386. * @return string escaped list for imap
  387. */
  388. public function escapeList($list)
  389. {
  390. $result = array();
  391. foreach ($list as $k => $v) {
  392. if (!is_array($v)) {
  393. // $result[] = $this->escapeString($v);
  394. $result[] = $v;
  395. continue;
  396. }
  397. $result[] = $this->escapeList($v);
  398. }
  399. return '(' . implode(' ', $result) . ')';
  400. }
  401. /**
  402. * Login to IMAP server.
  403. *
  404. * @param string $user username
  405. * @param string $password password
  406. * @return bool success
  407. * @throws Zend_Mail_Protocol_Exception
  408. */
  409. public function login($user, $password)
  410. {
  411. return $this->requestAndResponse('LOGIN', $this->escapeString($user, $password), true);
  412. }
  413. /**
  414. * logout of imap server
  415. *
  416. * @return bool success
  417. */
  418. public function logout()
  419. {
  420. $result = false;
  421. if ($this->_socket) {
  422. try {
  423. $result = $this->requestAndResponse('LOGOUT', array(), true);
  424. } catch (Zend_Mail_Protocol_Exception $e) {
  425. // ignoring exception
  426. }
  427. fclose($this->_socket);
  428. $this->_socket = null;
  429. }
  430. return $result;
  431. }
  432. /**
  433. * Get capabilities from IMAP server
  434. *
  435. * @return array list of capabilities
  436. * @throws Zend_Mail_Protocol_Exception
  437. */
  438. public function capability()
  439. {
  440. $response = $this->requestAndResponse('CAPABILITY');
  441. if (!$response) {
  442. return $response;
  443. }
  444. $capabilities = array();
  445. foreach ($response as $line) {
  446. $capabilities = array_merge($capabilities, $line);
  447. }
  448. return $capabilities;
  449. }
  450. /**
  451. * Examine and select have the same response. The common code for both
  452. * is in this method
  453. *
  454. * @param string $command can be 'EXAMINE' or 'SELECT' and this is used as command
  455. * @param string $box which folder to change to or examine
  456. * @return bool|array false if error, array with returned information
  457. * otherwise (flags, exists, recent, uidvalidity)
  458. * @throws Zend_Mail_Protocol_Exception
  459. */
  460. public function examineOrSelect($command = 'EXAMINE', $box = 'INBOX')
  461. {
  462. $this->sendRequest($command, array($this->escapeString($box)), $tag);
  463. $result = array();
  464. while (!$this->readLine($tokens, $tag)) {
  465. if ($tokens[0] == 'FLAGS') {
  466. array_shift($tokens);
  467. $result['flags'] = $tokens;
  468. continue;
  469. }
  470. switch ($tokens[1]) {
  471. case 'EXISTS':
  472. case 'RECENT':
  473. $result[strtolower($tokens[1])] = $tokens[0];
  474. break;
  475. case '[UIDVALIDITY':
  476. $result['uidvalidity'] = (int)$tokens[2];
  477. break;
  478. default:
  479. // ignore
  480. }
  481. }
  482. if ($tokens[0] != 'OK') {
  483. return false;
  484. }
  485. return $result;
  486. }
  487. /**
  488. * change folder
  489. *
  490. * @param string $box change to this folder
  491. * @return bool|array see examineOrselect()
  492. * @throws Zend_Mail_Protocol_Exception
  493. */
  494. public function select($box = 'INBOX')
  495. {
  496. return $this->examineOrSelect('SELECT', $box);
  497. }
  498. /**
  499. * examine folder
  500. *
  501. * @param string $box examine this folder
  502. * @return bool|array see examineOrselect()
  503. * @throws Zend_Mail_Protocol_Exception
  504. */
  505. public function examine($box = 'INBOX')
  506. {
  507. return $this->examineOrSelect('EXAMINE', $box);
  508. }
  509. /**
  510. * fetch one or more items of one or more messages
  511. *
  512. * @param string|array $items items to fetch from message(s) as string (if only one item)
  513. * or array of strings
  514. * @param int $from message for items or start message if $to !== null
  515. * @param int|null $to if null only one message ($from) is fetched, else it's the
  516. * last message, INF means last message avaible
  517. * @return string|array if only one item of one message is fetched it's returned as string
  518. * if items of one message are fetched it's returned as (name => value)
  519. * if one items of messages are fetched it's returned as (msgno => value)
  520. * if items of messages are fetchted it's returned as (msgno => (name => value))
  521. * @throws Zend_Mail_Protocol_Exception
  522. */
  523. public function fetch($items, $from, $to = null)
  524. {
  525. if (is_array($from)) {
  526. $set = implode(',', $from);
  527. } else if ($to === null) {
  528. $set = (int)$from;
  529. } else if ($to === INF) {
  530. $set = (int)$from . ':*';
  531. } else {
  532. $set = (int)$from . ':' . (int)$to;
  533. }
  534. $items = (array)$items;
  535. $itemList = $this->escapeList($items);
  536. $this->sendRequest('FETCH', array($set, $itemList), $tag);
  537. $result = array();
  538. while (!$this->readLine($tokens, $tag)) {
  539. // ignore other responses
  540. if ($tokens[1] != 'FETCH') {
  541. continue;
  542. }
  543. // ignore other messages
  544. if ($to === null && !is_array($from) && $tokens[0] != $from) {
  545. continue;
  546. }
  547. // if we only want one item we return that one directly
  548. if (count($items) == 1) {
  549. if ($tokens[2][0] == $items[0]) {
  550. $data = $tokens[2][1];
  551. } else {
  552. // maybe the server send an other field we didn't wanted
  553. $count = count($tokens[2]);
  554. // we start with 2, because 0 was already checked
  555. for ($i = 2; $i < $count; $i += 2) {
  556. if ($tokens[2][$i] != $items[0]) {
  557. continue;
  558. }
  559. $data = $tokens[2][$i + 1];
  560. break;
  561. }
  562. }
  563. } else {
  564. $data = array();
  565. while (key($tokens[2]) !== null) {
  566. $data[current($tokens[2])] = next($tokens[2]);
  567. next($tokens[2]);
  568. }
  569. }
  570. // if we want only one message we can ignore everything else and just return
  571. if ($to === null && !is_array($from) && $tokens[0] == $from) {
  572. // we still need to read all lines
  573. while (!$this->readLine($tokens, $tag));
  574. return $data;
  575. }
  576. $result[$tokens[0]] = $data;
  577. }
  578. if ($to === null && !is_array($from)) {
  579. /**
  580. * @see Zend_Mail_Protocol_Exception
  581. */
  582. #require_once 'Zend/Mail/Protocol/Exception.php';
  583. throw new Zend_Mail_Protocol_Exception('the single id was not found in response');
  584. }
  585. return $result;
  586. }
  587. /**
  588. * get mailbox list
  589. *
  590. * this method can't be named after the IMAP command 'LIST', as list is a reserved keyword
  591. *
  592. * @param string $reference mailbox reference for list
  593. * @param string $mailbox mailbox name match with wildcards
  594. * @return array mailboxes that matched $mailbox as array(globalName => array('delim' => .., 'flags' => ..))
  595. * @throws Zend_Mail_Protocol_Exception
  596. */
  597. public function listMailbox($reference = '', $mailbox = '*')
  598. {
  599. $result = array();
  600. $list = $this->requestAndResponse('LIST', $this->escapeString($reference, $mailbox));
  601. if (!$list || $list === true) {
  602. return $result;
  603. }
  604. foreach ($list as $item) {
  605. if (count($item) != 4 || $item[0] != 'LIST') {
  606. continue;
  607. }
  608. $result[$item[3]] = array('delim' => $item[2], 'flags' => $item[1]);
  609. }
  610. return $result;
  611. }
  612. /**
  613. * set flags
  614. *
  615. * @param array $flags flags to set, add or remove - see $mode
  616. * @param int $from message for items or start message if $to !== null
  617. * @param int|null $to if null only one message ($from) is fetched, else it's the
  618. * last message, INF means last message avaible
  619. * @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
  620. * @param bool $silent if false the return values are the new flags for the wanted messages
  621. * @return bool|array new flags if $silent is false, else true or false depending on success
  622. * @throws Zend_Mail_Protocol_Exception
  623. */
  624. public function store(array $flags, $from, $to = null, $mode = null, $silent = true)
  625. {
  626. $item = 'FLAGS';
  627. if ($mode == '+' || $mode == '-') {
  628. $item = $mode . $item;
  629. }
  630. if ($silent) {
  631. $item .= '.SILENT';
  632. }
  633. $flags = $this->escapeList($flags);
  634. $set = (int)$from;
  635. if ($to != null) {
  636. $set .= ':' . ($to == INF ? '*' : (int)$to);
  637. }
  638. $result = $this->requestAndResponse('STORE', array($set, $item, $flags), $silent);
  639. if ($silent) {
  640. return $result ? true : false;
  641. }
  642. $tokens = $result;
  643. $result = array();
  644. foreach ($tokens as $token) {
  645. if ($token[1] != 'FETCH' || $token[2][0] != 'FLAGS') {
  646. continue;
  647. }
  648. $result[$token[0]] = $token[2][1];
  649. }
  650. return $result;
  651. }
  652. /**
  653. * append a new message to given folder
  654. *
  655. * @param string $folder name of target folder
  656. * @param string $message full message content
  657. * @param array $flags flags for new message
  658. * @param string $date date for new message
  659. * @return bool success
  660. * @throws Zend_Mail_Protocol_Exception
  661. */
  662. public function append($folder, $message, $flags = null, $date = null)
  663. {
  664. $tokens = array();
  665. $tokens[] = $this->escapeString($folder);
  666. if ($flags !== null) {
  667. $tokens[] = $this->escapeList($flags);
  668. }
  669. if ($date !== null) {
  670. $tokens[] = $this->escapeString($date);
  671. }
  672. $tokens[] = $this->escapeString($message);
  673. return $this->requestAndResponse('APPEND', $tokens, true);
  674. }
  675. /**
  676. * copy message set from current folder to other folder
  677. *
  678. * @param string $folder destination folder
  679. * @param int|null $to if null only one message ($from) is fetched, else it's the
  680. * last message, INF means last message avaible
  681. * @return bool success
  682. * @throws Zend_Mail_Protocol_Exception
  683. */
  684. public function copy($folder, $from, $to = null)
  685. {
  686. $set = (int)$from;
  687. if ($to != null) {
  688. $set .= ':' . ($to == INF ? '*' : (int)$to);
  689. }
  690. return $this->requestAndResponse('COPY', array($set, $this->escapeString($folder)), true);
  691. }
  692. /**
  693. * create a new folder (and parent folders if needed)
  694. *
  695. * @param string $folder folder name
  696. * @return bool success
  697. */
  698. public function create($folder)
  699. {
  700. return $this->requestAndResponse('CREATE', array($this->escapeString($folder)), true);
  701. }
  702. /**
  703. * rename an existing folder
  704. *
  705. * @param string $old old name
  706. * @param string $new new name
  707. * @return bool success
  708. */
  709. public function rename($old, $new)
  710. {
  711. return $this->requestAndResponse('RENAME', $this->escapeString($old, $new), true);
  712. }
  713. /**
  714. * remove a folder
  715. *
  716. * @param string $folder folder name
  717. * @return bool success
  718. */
  719. public function delete($folder)
  720. {
  721. return $this->requestAndResponse('DELETE', array($this->escapeString($folder)), true);
  722. }
  723. /**
  724. * permanently remove messages
  725. *
  726. * @return bool success
  727. */
  728. public function expunge()
  729. {
  730. // TODO: parse response?
  731. return $this->requestAndResponse('EXPUNGE');
  732. }
  733. /**
  734. * send noop
  735. *
  736. * @return bool success
  737. */
  738. public function noop()
  739. {
  740. // TODO: parse response
  741. return $this->requestAndResponse('NOOP');
  742. }
  743. /**
  744. * do a search request
  745. *
  746. * This method is currently marked as internal as the API might change and is not
  747. * safe if you don't take precautions.
  748. *
  749. * @internal
  750. * @return array message ids
  751. */
  752. public function search(array $params)
  753. {
  754. $response = $this->requestAndResponse('SEARCH', $params);
  755. if (!$response) {
  756. return $response;
  757. }
  758. foreach ($response as $ids) {
  759. if ($ids[0] == 'SEARCH') {
  760. array_shift($ids);
  761. return $ids;
  762. }
  763. }
  764. return array();
  765. }
  766. }