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

/vendor/pear/Net/IMAPProtocol.php

http://akelosframework.googlecode.com/
PHP | 3048 lines | 2063 code | 451 blank | 534 comment | 213 complexity | 810ff8c93bf8d5d3289cc6b619cba3ee MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
  17. // +----------------------------------------------------------------------+
  18. require_once 'Net/Socket.php';
  19. /**
  20. * Provides an implementation of the IMAP protocol using PEAR's
  21. * Net_Socket:: class.
  22. *
  23. * @package Net_IMAP/Protocol
  24. * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
  25. */
  26. class Net_IMAPProtocol {
  27. /**
  28. * The auth methods this class support
  29. * @var array
  30. */
  31. var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN');
  32. /**
  33. * The auth methods this class support
  34. * @var array
  35. */
  36. var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
  37. /**
  38. * _serverAuthMethods
  39. * @var boolean
  40. */
  41. var $_serverAuthMethods = null;
  42. /**
  43. * The the current mailbox
  44. * @var string
  45. */
  46. var $currentMailbox = "INBOX" ;
  47. /**
  48. * The socket resource being used to connect to the IMAP server.
  49. * @var resource
  50. */
  51. var $_socket = null;
  52. /**
  53. * To allow class debuging
  54. * @var boolean
  55. */
  56. var $_debug = false;
  57. var $dbgDialog = '';
  58. /**
  59. * Command Number
  60. * @var int
  61. */
  62. var $_cmd_counter = 1;
  63. /**
  64. * Command Number for IMAP commands
  65. * @var int
  66. */
  67. var $_lastCmdID = 1;
  68. /**
  69. * Command Number
  70. * @var boolean
  71. */
  72. var $_unParsedReturn = false;
  73. /**
  74. * _connected: checks if there is a connection made to a imap server or not
  75. * @var boolean
  76. */
  77. var $_connected = false;
  78. /**
  79. * Capabilities
  80. * @var boolean
  81. */
  82. var $_serverSupportedCapabilities = null;
  83. /**
  84. * Use UTF-7 funcionallity
  85. * @var boolean
  86. */
  87. //var $_useUTF_7 = false;
  88. var $_useUTF_7 = true;
  89. /**
  90. * Constructor
  91. *
  92. * Instantiates a new Net_IMAP object.
  93. *
  94. * @since 1.0
  95. */
  96. function Net_IMAPProtocol()
  97. {
  98. $this->_socket = new Net_Socket();
  99. /*
  100. * Include the Auth_SASL package. If the package is not available,
  101. * we disable the authentication methods that depend upon it.
  102. */
  103. if ((@include_once 'Auth/SASL.php') == false) {
  104. foreach($this->supportedSASLAuthMethods as $SASLMethod){
  105. $pos = array_search( $SASLMethod , $this->supportedAuthMethods);
  106. unset($this->supportedAuthMethods[$pos]);
  107. }
  108. }
  109. }
  110. /**
  111. * Attempt to connect to the IMAP server.
  112. *
  113. * @return mixed Returns a PEAR_Error with an error message on any
  114. * kind of failure, or true on success.
  115. * @access public
  116. * @since 1.0
  117. */
  118. function cmdConnect($host= "localhost" , $port = 143)
  119. {
  120. if( $this->_connected ){
  121. return new PEAR_Error( 'already connected, logout first!' );
  122. }
  123. if ( PEAR::isError( $this->_socket->connect( $host , $port ) ) ) {
  124. return new PEAR_Error( 'unable to open socket' );
  125. }
  126. if ( PEAR::isError( $this->_getRawResponse() ) ) {
  127. return new PEAR_Error( 'unable to open socket' );
  128. }
  129. $this->_connected = true;
  130. return true;
  131. }
  132. /**
  133. * get the cmd ID
  134. *
  135. * @return string Returns the CmdID and increment the counter
  136. *
  137. * @access private
  138. * @since 1.0
  139. */
  140. function _getCmdId()
  141. {
  142. $this->_lastCmdID = "A000" . $this->_cmd_counter ;
  143. $this->_cmd_counter++;
  144. return $this->_lastCmdID;
  145. }
  146. /**
  147. * get the last cmd ID
  148. *
  149. * @return string Returns the last cmdId
  150. *
  151. * @access public
  152. * @since 1.0
  153. */
  154. function getLastCmdId()
  155. {
  156. return $this->_lastCmdID;
  157. }
  158. /**
  159. * get current mailbox name
  160. *
  161. * @return string Returns the current mailbox
  162. *
  163. * @access public
  164. * @since 1.0
  165. */
  166. function getCurrentMailbox()
  167. {
  168. return $this->currentMailbox;
  169. }
  170. /**
  171. * Sets the debuging information on or off
  172. *
  173. * @param boolean True or false
  174. *
  175. * @return nothing
  176. * @access public
  177. * @since 1.0
  178. */
  179. function setDebug($debug = true)
  180. {
  181. $this->_debug = $debug;
  182. }
  183. function getDebugDialog()
  184. {
  185. return $this->dbgDialog;
  186. }
  187. /**
  188. * Send the given string of data to the server.
  189. *
  190. * @param string $data The string of data to send.
  191. *
  192. * @return mixed True on success or a PEAR_Error object on failure.
  193. *
  194. * @access private
  195. * @since 1.0
  196. */
  197. function _send($data)
  198. {
  199. if($this->_socket->eof() ){
  200. return new PEAR_Error( 'Failed to write to socket: (connection lost!) ' );
  201. }
  202. if ( PEAR::isError( $error = $this->_socket->write( $data ) ) ) {
  203. return new PEAR_Error( 'Failed to write to socket: ' .
  204. $error->getMessage() );
  205. }
  206. if( $this->_debug ){
  207. // C: means this data was sent by the client (this class)
  208. echo "C: $data";
  209. $this->dbgDialog.="C: $data";
  210. }
  211. return true;
  212. }
  213. /**
  214. * Receive the given string of data from the server.
  215. *
  216. * @return mixed a line of response on success or a PEAR_Error object on failure.
  217. *
  218. * @access private
  219. * @since 1.0
  220. */
  221. function _recvLn()
  222. {
  223. if (PEAR::isError( $this->lastline = $this->_socket->gets( 8192 ) ) ) {
  224. return new PEAR_Error('Failed to write to socket: ' .
  225. $this->lastline->getMessage() );
  226. }
  227. if($this->_debug){
  228. // S: means this data was sent by the IMAP Server
  229. echo "S: " . $this->lastline . "" ;
  230. $this->dbgDialog.="S: " . $this->lastline . "" ;
  231. }
  232. if( $this->lastline == '' ){
  233. return new PEAR_Error('Failed to receive from the socket: ' );
  234. }
  235. return $this->lastline;
  236. }
  237. /**
  238. * Send a command to the server with an optional string of arguments.
  239. * A carriage return / linefeed (CRLF) sequence will be appended to each
  240. * command string before it is sent to the IMAP server.
  241. *
  242. * @param string $commandId The IMAP cmdID to send to the server.
  243. * @param string $command The IMAP command to send to the server.
  244. * @param string $args A string of optional arguments to append
  245. * to the command.
  246. *
  247. * @return mixed The result of the _send() call.
  248. *
  249. * @access private
  250. * @since 1.0
  251. */
  252. function _putCMD($commandId , $command, $args = '')
  253. {
  254. if ( !empty( $args ) ) {
  255. return $this->_send( $commandId . " " . $command . ' ' . $args . "\r\n" );
  256. }
  257. return $this->_send( $commandId . " " . $command . "\r\n" );
  258. }
  259. /**
  260. * Get a response from the server with an optional string of commandID.
  261. * A carriage return / linefeed (CRLF) sequence will be appended to each
  262. * command string before it is sent to the IMAP server.
  263. *
  264. * @param string $commandid The IMAP commandid retrive from the server.
  265. *
  266. * @return string The result response.
  267. *
  268. * @access private
  269. */
  270. function _getRawResponse($commandId = '*')
  271. {
  272. $arguments = '';
  273. while ( !PEAR::isError( $this->_recvLn() ) ) {
  274. $reply_code = strtok( $this->lastline , ' ' );
  275. $arguments.= $this->lastline;
  276. if ( !(strcmp( $commandId , $reply_code ) ) ) {
  277. return $arguments;
  278. }
  279. }
  280. return $arguments;
  281. }
  282. /**
  283. * get the "returning of the unparsed response" feature status
  284. *
  285. * @return boolean return if the unparsed response is returned or not
  286. *
  287. * @access public
  288. * @since 1.0
  289. *
  290. */
  291. function getUnparsedResponse()
  292. {
  293. return $this->_unParsedReturn;
  294. }
  295. /**
  296. * set the "returning of the unparsed response" feature on or off
  297. *
  298. * @param boolean $status: true: feature is on
  299. * @return nothing
  300. *
  301. * @access public
  302. * @since 1.0
  303. */
  304. function setUnparsedResponse($status)
  305. {
  306. $this->_unParsedReturn = $status;
  307. }
  308. /**
  309. * Attempt to login to the iMAP server.
  310. *
  311. * @param string The userid to authenticate as.
  312. * @param string The password to authenticate with.
  313. *
  314. * @return array Returns an array containing the response
  315. *
  316. * @access public
  317. * @since 1.0
  318. */
  319. function cmdLogin($uid , $pwd)
  320. {
  321. $param="\"$uid\" \"$pwd\"";
  322. return $this->_genericCommand('LOGIN', $param);
  323. }
  324. /**
  325. * Attempt to authenticate to the iMAP server.
  326. * @param string The userid to authenticate as.
  327. * @param string The password to authenticate with.
  328. * @param string The cmdID.
  329. *
  330. * @return array Returns an array containing the response
  331. *
  332. * @access public
  333. * @since 1.0
  334. */
  335. function cmdAuthenticate($uid , $pwd , $userMethod = null)
  336. {
  337. if( !$this->_connected ){
  338. return new PEAR_Error('not connected!');
  339. }
  340. $cmdid = $this->_getCmdId();
  341. if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
  342. return $method;
  343. }
  344. switch ($method) {
  345. case 'DIGEST-MD5':
  346. $result = $this->_authDigest_MD5( $uid , $pwd , $cmdid );
  347. break;
  348. case 'CRAM-MD5':
  349. $result = $this->_authCRAM_MD5( $uid , $pwd ,$cmdid );
  350. break;
  351. case 'LOGIN':
  352. $result = $this->_authLOGIN( $uid , $pwd , $cmdid );
  353. break;
  354. default :
  355. $result = new PEAR_Error( "$method is not a supported authentication method" );
  356. break;
  357. }
  358. $args = $this->_getRawResponse( $cmdid );
  359. return $this->_genericImapResponseParser( $args , $cmdid );
  360. }
  361. /* Authenticates the user using the DIGEST-MD5 method.
  362. *
  363. * @param string The userid to authenticate as.
  364. * @param string The password to authenticate with.
  365. * @param string The cmdID.
  366. *
  367. * @return array Returns an array containing the response
  368. *
  369. * @access private
  370. * @since 1.0
  371. */
  372. function _authDigest_MD5($uid , $pwd , $cmdid)
  373. {
  374. if ( PEAR::isError($error = $this->_putCMD( $cmdid ,"AUTHENTICATE" , "DIGEST-MD5") ) ) {
  375. return $error;
  376. }
  377. if (PEAR::isError( $args = $this->_recvLn() ) ) {
  378. return $args;
  379. }
  380. $this->_getNextToken( $args , $plus );
  381. $this->_getNextToken( $args , $space );
  382. $this->_getNextToken( $args , $challenge );
  383. $challenge = base64_decode( $challenge );
  384. $digest = &Auth_SASL::factory('digestmd5');
  385. $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,"localhost", "imap"));
  386. if ( PEAR::isError( $error = $this->_send("$auth_str\r\n"))) {
  387. return $error;
  388. }
  389. if ( PEAR::isError( $args = $this->_recvLn() )) {
  390. return $args;
  391. }
  392. /*
  393. * We don't use the protocol's third step because IMAP doesn't allow
  394. * subsequent authentication, so we just silently ignore it.
  395. */
  396. if ( PEAR::isError( $error = $this->_send( "\r\n" ) ) ) {
  397. return $error;
  398. }
  399. }
  400. /* Authenticates the user using the CRAM-MD5 method.
  401. *
  402. * @param string The userid to authenticate as.
  403. * @param string The password to authenticate with.
  404. * @param string The cmdID.
  405. *
  406. * @return array Returns an array containing the response
  407. *
  408. * @access private
  409. * @since 1.0
  410. */
  411. function _authCRAM_MD5($uid, $pwd, $cmdid)
  412. {
  413. if ( PEAR::isError($error = $this->_putCMD( $cmdid ,"AUTHENTICATE" , "CRAM-MD5") ) ) {
  414. return $error;
  415. }
  416. if ( PEAR::isError( $args = $this->_recvLn() ) ) {
  417. return $args;
  418. }
  419. $this->_getNextToken( $args , $plus );
  420. $this->_getNextToken( $args , $space );
  421. $this->_getNextToken( $args , $challenge );
  422. $challenge = base64_decode( $challenge );
  423. $cram = &Auth_SASL::factory('crammd5');
  424. $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
  425. if ( PEAR::isError( $error = $this->_send( $auth_str."\r\n" ) ) ) {
  426. return $error;
  427. }
  428. }
  429. /* Authenticates the user using the LOGIN method.
  430. *
  431. * @param string The userid to authenticate as.
  432. * @param string The password to authenticate with.
  433. * @param string The cmdID.
  434. *
  435. * @return array Returns an array containing the response
  436. *
  437. * @access private
  438. * @since 1.0
  439. */
  440. function _authLOGIN($uid, $pwd, $cmdid)
  441. {
  442. if (PEAR::isError($error = $this->_putCMD($cmdid,"AUTHENTICATE", "LOGIN"))) {
  443. return $error;
  444. }
  445. if (PEAR::isError($args = $this->_recvLn() )) {
  446. return $args;
  447. }
  448. $this->_getNextToken( $args , $plus );
  449. $this->_getNextToken( $args , $space );
  450. $this->_getNextToken( $args , $challenge );
  451. $challenge = base64_decode( $challenge );
  452. $auth_str = base64_encode( "$uid" );
  453. if ( PEAR::isError( $error = $this->_send( $auth_str."\r\n" ) ) ) {
  454. return $error;
  455. }
  456. if (PEAR::isError( $args = $this->_recvLn() ) ) {
  457. return $args;
  458. }
  459. $auth_str = base64_encode( "$pwd" );
  460. if ( PEAR::isError($error = $this->_send( $auth_str."\r\n" ) ) ) {
  461. return $error;
  462. }
  463. }
  464. /**
  465. * Returns the name of the best authentication method that the server
  466. * has advertised.
  467. *
  468. * @param string if !=null,authenticate with this method ($userMethod).
  469. *
  470. * @return mixed Returns a string containing the name of the best
  471. * supported authentication method or a PEAR_Error object
  472. * if a failure condition is encountered.
  473. * @access private
  474. * @since 1.0
  475. */
  476. function _getBestAuthMethod($userMethod = null)
  477. {
  478. $this->cmdCapability();
  479. if($userMethod != null ){
  480. $methods = array();
  481. $methods[] = $userMethod;
  482. }else{
  483. $methods = $this->supportedAuthMethods;
  484. }
  485. if( ($methods != null) && ($this->_serverAuthMethods != null)){
  486. foreach ( $methods as $method ) {
  487. if ( in_array( $method , $this->_serverAuthMethods ) ) {
  488. return $method;
  489. }
  490. }
  491. $serverMethods=implode(',' ,$this->_serverAuthMethods);
  492. $myMethods=implode(',' ,$this->supportedAuthMethods);
  493. return new PEAR_Error("$method NOT supported authentication method!. This IMAP server " .
  494. "supports these methods: $serverMethods, but I support $myMethods");
  495. }else{
  496. return new PEAR_Error("This IMAP server don't support any Auth methods");
  497. }
  498. }
  499. /**
  500. * Attempt to disconnect from the iMAP server.
  501. *
  502. * @return array Returns an array containing the response
  503. *
  504. * @access public
  505. * @since 1.0
  506. */
  507. function cmdLogout()
  508. {
  509. if( !$this->_connected ){
  510. return new PEAR_Error( 'not connected!' );
  511. }
  512. $cmdid = $this->_getCmdId();
  513. if ( PEAR::isError( $error = $this->_putCMD( $cmdid , 'LOGOUT' ) ) ) {
  514. return $error;
  515. }
  516. if ( PEAR::isError($args = $this->_getRawResponse() ) ) {
  517. return $args;
  518. }
  519. if (PEAR::isError( $this->_socket->disconnect() ) ) {
  520. return new PEAR_Error('socket disconnect failed');
  521. }
  522. return $args;
  523. // not for now
  524. //return $this->_genericImapResponseParser($args,$cmdid);
  525. }
  526. /**
  527. * Send the NOOP command.
  528. *
  529. * @return array Returns an array containing the response
  530. *
  531. * @access public
  532. * @since 1.0
  533. */
  534. function cmdNoop()
  535. {
  536. return $this->_genericCommand('NOOP');
  537. }
  538. /**
  539. * Send the CHECK command.
  540. *
  541. * @return array Returns an array containing the response
  542. *
  543. * @access public
  544. * @since 1.0
  545. */
  546. function cmdCheck()
  547. {
  548. return $this->_genericCommand('CHECK');
  549. }
  550. /**
  551. * Send the Select Mailbox Command
  552. *
  553. * @param string The mailbox to select.
  554. *
  555. * @return array Returns an array containing the response
  556. *
  557. * @access public
  558. * @since 1.0
  559. */
  560. function cmdSelect($mailbox)
  561. {
  562. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  563. if( !PEAR::isError( $ret= $this->_genericCommand('SELECT', $mailbox_name) ) ){
  564. $this->currentMailbox = $mailbox;
  565. }
  566. return $ret;
  567. }
  568. /**
  569. * Send the EXAMINE Mailbox Command
  570. *
  571. * @param string The mailbox to examine.
  572. * @return array Returns an array containing the response
  573. *
  574. * @access public
  575. * @since 1.0
  576. */
  577. function cmdExamine($mailbox)
  578. {
  579. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  580. $ret=$this->_genericCommand('EXAMINE', $mailbox_name);
  581. $parsed='';
  582. if(isset( $ret["PARSED"] ) ){
  583. for($i=0;$i<count($ret["PARSED"]); $i++){ $command=$ret["PARSED"][$i]["EXT"];
  584. $parsed[key($command)]=$command[key($command)];
  585. }
  586. }
  587. return array("PARSED"=>$parsed,"RESPONSE"=>$ret["RESPONSE"]);
  588. }
  589. /**
  590. * Send the CREATE Mailbox Command
  591. *
  592. * @param string The mailbox to create.
  593. * @return array Returns an array containing the response
  594. *
  595. * @access public
  596. * @since 1.0
  597. */
  598. function cmdCreate($mailbox)
  599. {
  600. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  601. return $this->_genericCommand('CREATE', $mailbox_name);
  602. }
  603. /**
  604. * Send the RENAME Mailbox Command
  605. *
  606. * @param string The old mailbox name.
  607. * @param string The new (renamed) mailbox name.
  608. *
  609. * @return array Returns an array containing the response
  610. *
  611. * @access public
  612. * @since 1.0
  613. */
  614. function cmdRename($mailbox, $new_mailbox)
  615. {
  616. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  617. $new_mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($new_mailbox) );
  618. return $this->_genericCommand('RENAME', "$mailbox_name $new_mailbox_name" );
  619. }
  620. /**
  621. * Send the DELETE Mailbox Command
  622. *
  623. * @param string The mailbox name to delete.
  624. *
  625. * @return array Returns an array containing the response
  626. *
  627. * @access public
  628. * @since 1.0
  629. */
  630. function cmdDelete($mailbox)
  631. {
  632. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  633. return $this->_genericCommand('DELETE', $mailbox_name);
  634. }
  635. /**
  636. * Send the SUSCRIBE Mailbox Command
  637. *
  638. * @param string The mailbox name to suscribe.
  639. *
  640. * @return array Returns an array containing the response
  641. *
  642. * @access public
  643. * @since 1.0
  644. */
  645. function cmdSubscribe($mailbox)
  646. {
  647. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  648. return $this->_genericCommand('SUBSCRIBE', $mailbox_name );
  649. }
  650. /**
  651. * Send the UNSUSCRIBE Mailbox Command
  652. *
  653. * @return mixed Returns a PEAR_Error with an error message on any
  654. * kind of failure, or true on success.
  655. * @access public
  656. * @since 1.0
  657. */
  658. function cmdUnsubscribe($mailbox)
  659. {
  660. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  661. return $this->_genericCommand('UNSUBSCRIBE', $mailbox_name );
  662. }
  663. /**
  664. * Send the FETCH Command
  665. *
  666. * @return mixed Returns a PEAR_Error with an error message on any
  667. * kind of failure, or true on success.
  668. * @access public
  669. * @since 1.0
  670. */
  671. function cmdFetch($msgset, $fetchparam)
  672. {
  673. return $this->_genericCommand('FETCH' , "$msgset $fetchparam" );
  674. }
  675. /**
  676. * Send the CAPABILITY Command
  677. *
  678. * @return mixed Returns a PEAR_Error with an error message on any
  679. * kind of failure, or true on success.
  680. * @access public
  681. * @since 1.0
  682. */
  683. function cmdCapability()
  684. {
  685. $ret = $this->_genericCommand( 'CAPABILITY' );
  686. if(isset( $ret["PARSED"] ) ){
  687. $ret["PARSED"]=$ret["PARSED"][0]["EXT"]["CAPABILITY"];
  688. //fill the $this->_serverAuthMethods and $this->_serverSupportedCapabilities arrays
  689. foreach( $ret["PARSED"]["CAPABILITIES"] as $auth_method ){
  690. if( strtoupper( substr( $auth_method , 0 ,5 ) ) == "AUTH=" )
  691. $this->_serverAuthMethods[] = substr( $auth_method , 5 );
  692. }
  693. // Keep the capabilities response to use ir later
  694. $this->_serverSupportedCapabilities = $ret["PARSED"]["CAPABILITIES"];
  695. }
  696. return $ret;
  697. }
  698. /**
  699. * Send the STATUS Mailbox Command
  700. *
  701. * @param string $mailbox the mailbox name
  702. * @param string $request the request status it could be:
  703. * MESSAGES | RECENT | UIDNEXT
  704. * UIDVALIDITY | UNSEEN
  705. * @return array Returns a Parsed Response
  706. *
  707. * @access public
  708. * @since 1.0
  709. */
  710. function cmdStatus($mailbox, $request)
  711. {
  712. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  713. if( $request!="MESSAGES" && $request!="RECENT" && $request!="UIDNEXT" &&
  714. $request!="UIDVALIDITY" && $request!="UNSEEN" ){
  715. // TODO: fix this error!
  716. $this->_prot_error("request '$request' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__, false );
  717. }
  718. $ret = $this->_genericCommand('STATUS', "$mailbox_name ($request)" );
  719. if(isset( $ret["PARSED"] ) ){
  720. $ret['PARSED']=$ret["PARSED"][count($ret['PARSED'])-1]["EXT"];
  721. }
  722. return $ret;
  723. }
  724. /**
  725. * Send the LIST Command
  726. *
  727. * @return mixed Returns a PEAR_Error with an error message on any
  728. * kind of failure, or true on success.
  729. * @access public
  730. * @since 1.0
  731. */
  732. function cmdList($mailbox_base, $mailbox)
  733. {
  734. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  735. $mailbox_base=sprintf("\"%s\"",$this->utf_7_encode($mailbox_base) );
  736. return $this->_genericCommand('LIST', "$mailbox_base $mailbox_name" );
  737. }
  738. /**
  739. * Send the LSUB Command
  740. *
  741. * @return mixed Returns a PEAR_Error with an error message on any
  742. * kind of failure, or true on success.
  743. * @access public
  744. * @since 1.0
  745. */
  746. function cmdLsub($mailbox_base, $mailbox)
  747. {
  748. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  749. $mailbox_base=sprintf("\"%s\"",$this->utf_7_encode($mailbox_base) );
  750. return $this->_genericCommand('LSUB', "$mailbox_base $mailbox_name" );
  751. }
  752. /**
  753. * Send the APPEND Command
  754. *
  755. * @return mixed Returns a PEAR_Error with an error message on any
  756. * kind of failure, or true on success.
  757. * @access public
  758. * @since 1.0
  759. */
  760. function cmdAppend($mailbox, $msg , $flags_list = '' ,$time = '')
  761. {
  762. if(!$this->_connected){
  763. return new PEAR_Error('not connected!');
  764. }
  765. $cmdid=$this->_getCmdId();
  766. $msg_size=strlen($msg);
  767. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  768. // TODO:
  769. // Falta el codigo para que flags list y time hagan algo!!
  770. if( $this->hasCapability( "LITERAL+" ) == true ){
  771. $param=sprintf("%s %s%s{%s+}\r\n%s",$mailbox_name,$flags_list,$time,$msg_size,$msg);
  772. if (PEAR::isError($error = $this->_putCMD($cmdid , 'APPEND' , $param ) ) ) {
  773. return $error;
  774. }
  775. }else{
  776. $param=sprintf("%s %s%s{%s}\r\n",$mailbox_name,$flags_list,$time,$msg_size);
  777. if (PEAR::isError($error = $this->_putCMD($cmdid , 'APPEND' , $param ) ) ) {
  778. return $error;
  779. }
  780. if (PEAR::isError($error = $this->_recvLn() ) ) {
  781. return $error;
  782. }
  783. if (PEAR::isError($error = $this->_send( $msg ) ) ) {
  784. return $error;
  785. }
  786. }
  787. $args=$this->_getRawResponse($cmdid);
  788. $ret = $this->_genericImapResponseParser($args,$cmdid);
  789. return $ret;
  790. }
  791. /**
  792. * Send the CLOSE command.
  793. *
  794. * @return mixed Returns a PEAR_Error with an error message on any
  795. * kind of failure, or true on success.
  796. * @access public
  797. * @since 1.0
  798. */
  799. function cmdClose()
  800. {
  801. return $this->_genericCommand('CLOSE');
  802. }
  803. /**
  804. * Send the EXPUNGE command.
  805. *
  806. * @return mixed Returns a PEAR_Error with an error message on any
  807. * kind of failure, or true on success.
  808. * @access public
  809. * @since 1.0
  810. */
  811. function cmdExpunge()
  812. {
  813. $ret=$this->_genericCommand('EXPUNGE');
  814. if(isset( $ret["PARSED"] ) ){
  815. $parsed=$ret["PARSED"];
  816. unset($ret["PARSED"]);
  817. foreach($parsed as $command){
  818. if( strtoupper($command["COMMAND"]) == 'EXPUNGE' ){
  819. $ret["PARSED"][$command["COMMAND"]][]=$command["NRO"];
  820. }else{
  821. $ret["PARSED"][$command["COMMAND"]]=$command["NRO"];
  822. }
  823. }
  824. }
  825. return $ret;
  826. }
  827. /**
  828. * Send the SEARCH command.
  829. *
  830. * @return mixed Returns a PEAR_Error with an error message on any
  831. * kind of failure, or true on success.
  832. * @access public
  833. * @since 1.0
  834. */
  835. function cmdSearch($search_cmd)
  836. {
  837. /* if($_charset != '' )
  838. $_charset = "[$_charset] ";
  839. $param=sprintf("%s%s",$charset,$search_cmd);
  840. */
  841. $ret = $this->_genericCommand('SEARCH', $search_cmd );
  842. if(isset( $ret["PARSED"] ) ){
  843. $ret["PARSED"]=$ret["PARSED"][0]["EXT"];
  844. }
  845. return $ret;
  846. }
  847. /**
  848. * Send the STORE command.
  849. *
  850. * @param string $message_set the sessage_set
  851. * @param string $dataitem: the way we store the flags
  852. * FLAGS: replace the flags whith $value
  853. * FLAGS.SILENT: replace the flags whith $value but don't return untagged responses
  854. *
  855. * +FLAGS: Add the flags whith $value
  856. * +FLAGS.SILENT: Add the flags whith $value but don't return untagged responses
  857. *
  858. * -FLAGS: Remove the flags whith $value
  859. * -FLAGS.SILENT: Remove the flags whith $value but don't return untagged responses
  860. *
  861. * @param string $value
  862. * @return mixed Returns a PEAR_Error with an error message on any
  863. * kind of failure, or true on success.
  864. * @access public
  865. * @since 1.0
  866. */
  867. function cmdStore($message_set, $dataitem, $value)
  868. {
  869. /* As said in RFC2060...
  870. C: A003 STORE 2:4 +FLAGS (\Deleted)
  871. S: * 2 FETCH FLAGS (\Deleted \Seen)
  872. S: * 3 FETCH FLAGS (\Deleted)
  873. S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
  874. S: A003 OK STORE completed
  875. */
  876. if( $dataitem!="FLAGS" && $dataitem!="FLAGS.SILENT" && $dataitem!="+FLAGS" &&
  877. $dataitem!="+FLAGS.SILENT" && $dataitem!="-FLAGS" && $dataitem!="-FLAGS.SILENT" ){
  878. $this->_prot_error("dataitem '$dataitem' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__ );
  879. }
  880. $param=sprintf("%s %s (%s)",$message_set,$dataitem,$value);
  881. return $this->_genericCommand('STORE', $param );
  882. }
  883. /**
  884. * Send the COPY command.
  885. *
  886. * @return mixed Returns a PEAR_Error with an error message on any
  887. * kind of failure, or true on success.
  888. * @access public
  889. * @since 1.0
  890. */
  891. function cmdCopy($message_set, $mailbox)
  892. {
  893. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  894. return $this->_genericCommand('COPY', sprintf("%s %s",$message_set,$mailbox_name) );
  895. }
  896. function cmdUidFetch($msgset, $fetchparam)
  897. {
  898. return $this->_genericCommand('UID FETCH', sprintf("%s %s",$msgset,$fetchparam) );
  899. }
  900. function cmdUidCopy($message_set, $mailbox)
  901. {
  902. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) );
  903. return $this->_genericCommand('UID COPY', sprintf("%s %s",$message_set,$mailbox_name) );
  904. }
  905. /**
  906. * Send the UID STORE command.
  907. *
  908. * @param string $message_set the sessage_set
  909. * @param string $dataitem: the way we store the flags
  910. * FLAGS: replace the flags whith $value
  911. * FLAGS.SILENT: replace the flags whith $value but don't return untagged responses
  912. *
  913. * +FLAGS: Add the flags whith $value
  914. * +FLAGS.SILENT: Add the flags whith $value but don't return untagged responses
  915. *
  916. * -FLAGS: Remove the flags whith $value
  917. * -FLAGS.SILENT: Remove the flags whith $value but don't return untagged responses
  918. *
  919. * @param string $value
  920. * @return mixed Returns a PEAR_Error with an error message on any
  921. * kind of failure, or true on success.
  922. * @access public
  923. * @since 1.0
  924. */
  925. function cmdUidStore($message_set, $dataitem, $value)
  926. {
  927. /* As said in RFC2060...
  928. C: A003 STORE 2:4 +FLAGS (\Deleted)
  929. S: * 2 FETCH FLAGS (\Deleted \Seen)
  930. S: * 3 FETCH FLAGS (\Deleted)
  931. S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
  932. S: A003 OK STORE completed
  933. */
  934. if( $dataitem!="FLAGS" && $dataitem!="FLAGS.SILENT" && $dataitem!="+FLAGS" &&
  935. $dataitem!="+FLAGS.SILENT" && $dataitem!="-FLAGS" && $dataitem!="-FLAGS.SILENT" ){
  936. $this->_prot_error("dataitem '$dataitem' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__ );
  937. }
  938. return $this->_genericCommand('UID STORE', sprintf("%s %s (%s)",$message_set,$dataitem,$value) );
  939. }
  940. /**
  941. * Send the SEARCH command.
  942. *
  943. * @return mixed Returns a PEAR_Error with an error message on any
  944. * kind of failure, or true on success.
  945. * @access public
  946. * @since 1.0
  947. */
  948. function cmdUidSearch($search_cmd)
  949. {
  950. $ret=$this->_genericCommand('UID SEARCH', sprintf("%s",$search_cmd) );
  951. if(isset( $ret["PARSED"] ) ){
  952. $ret["PARSED"]=$ret["PARSED"][0]["EXT"];
  953. }
  954. return $ret;
  955. }
  956. /**
  957. * Send the X command.
  958. *
  959. * @return mixed Returns a PEAR_Error with an error message on any
  960. * kind of failure, or true on success.
  961. * @access public
  962. * @since 1.0
  963. */
  964. function cmdX($atom, $parameters)
  965. {
  966. return $this->_genericCommand("X$atom", $parameters );
  967. }
  968. /********************************************************************
  969. ***
  970. *** HERE ENDS the RFC2060 IMAPS FUNCTIONS
  971. *** AND BEGIN THE EXTENSIONS FUNCTIONS
  972. ***
  973. ********************************************************************/
  974. /********************************************************************
  975. *** RFC2087 IMAP4 QUOTA extension BEGINS HERE
  976. ********************************************************************/
  977. /**
  978. * Send the GETQUOTA command.
  979. *
  980. * @param string $mailbox_name the mailbox name to query for quota data
  981. * @return mixed Returns a PEAR_Error with an error message on any
  982. * kind of failure, or quota data on success
  983. * @access public
  984. * @since 1.0
  985. */
  986. function cmdGetQuota($mailbox_name)
  987. {
  988. //Check if the IMAP server has QUOTA support
  989. if( ! $this->hasQuotaSupport() ){
  990. return new PEAR_Error("This IMAP server does not support QUOTA's! ");
  991. }
  992. $mailbox_name=sprintf("%s",$this->utf_7_encode($mailbox_name) );
  993. $ret = $this->_genericCommand('GETQUOTA', $mailbox_name );
  994. if(isset( $ret["PARSED"] ) ){
  995. // remove the array index because the quota response returns only 1 line of output
  996. $ret['PARSED']=$ret["PARSED"][0];
  997. }
  998. return $ret;
  999. }
  1000. /**
  1001. * Send the GETQUOTAROOT command.
  1002. *
  1003. * @param string $mailbox_name the mailbox name to query for quota data
  1004. * @return mixed Returns a PEAR_Error with an error message on any
  1005. * kind of failure, or quota data on success
  1006. * @access public
  1007. * @since 1.0
  1008. */
  1009. function cmdGetQuotaRoot($mailbox_name)
  1010. {
  1011. //Check if the IMAP server has QUOTA support
  1012. if( ! $this->hasQuotaSupport() ){
  1013. return new PEAR_Error("This IMAP server does not support QUOTA's! ");
  1014. }
  1015. $mailbox_name=sprintf("%s",$this->utf_7_encode($mailbox_name) );
  1016. $ret = $this->_genericCommand('GETQUOTAROOT', $mailbox_name );
  1017. if(isset( $ret["PARSED"] ) ){
  1018. // remove the array index because the quota response returns only 1 line of output
  1019. $ret['PARSED']=$ret["PARSED"][0];
  1020. }
  1021. return $ret;
  1022. }
  1023. /**
  1024. * Send the SETQUOTA command.
  1025. *
  1026. * @param string $mailbox_name the mailbox name to query for quota data
  1027. * @param string $storageQuota sets the max number of bytes this mailbox can handle
  1028. * @param string $messagesQuota sets the max number of messages this mailbox can handle
  1029. * @return mixed Returns a PEAR_Error with an error message on any
  1030. * kind of failure, or quota data on success
  1031. * @access public
  1032. * @since 1.0
  1033. */
  1034. // TODO: implement the quota by number of emails!!
  1035. function cmdSetQuota($mailbox_name, $storageQuota = null ,$messagesQuota = null )
  1036. {
  1037. //Check if the IMAP server has QUOTA support
  1038. if( ! $this->hasQuotaSupport() ){
  1039. return new PEAR_Error("This IMAP server does not support QUOTA's! ");
  1040. }
  1041. if( ($messagesQuota == null) && ( $storageQuota == null) ){
  1042. return new PEAR_Error('$storageQuota and $messagesQuota parameters can\'t be both null if you want to use quota');
  1043. }
  1044. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1045. //Make the command request
  1046. $param=sprintf("%s (",$mailbox_name);
  1047. if($storageQuota != null ){
  1048. $param=sprintf("%sSTORAGE %s",$param,$storageQuota);
  1049. if( $messagesQuota != null ){
  1050. //if we have both types of quota on the same call we must append an space between
  1051. // those parameters
  1052. $param=sprintf("%s ",$param);
  1053. }
  1054. }
  1055. if($messagesQuota != null ){
  1056. $param=sprintf("%sMESSAGES %s",$param,$messagesQuota);
  1057. }
  1058. $param=sprintf("%s)",$param);
  1059. return $this->_genericCommand('SETQUOTA', $param );
  1060. }
  1061. /**
  1062. * Send the SETQUOTAROOT command.
  1063. *
  1064. * @param string $mailbox_name the mailbox name to query for quota data
  1065. * @param string $storageQuota sets the max number of bytes this mailbox can handle
  1066. * @param string $messagesQuota sets the max number of messages this mailbox can handle
  1067. * @return mixed Returns a PEAR_Error with an error message on any
  1068. * kind of failure, or quota data on success
  1069. * @access public
  1070. * @since 1.0
  1071. */
  1072. function cmdSetQuotaRoot($mailbox_name, $storageQuota = null ,$messagesQuota = null)
  1073. {
  1074. //Check if the IMAP server has QUOTA support
  1075. if( ! $this->hasQuotaSupport() ){
  1076. return new PEAR_Error("This IMAP server does not support QUOTA's! ");
  1077. }
  1078. if( ($messagesQuota == null) && ( $storageQuota == null) ){
  1079. return new PEAR_Error('$storageQuota and $messagesQuota parameters can\'t be both null if you want to use quota');
  1080. }
  1081. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1082. //Make the command request
  1083. $param=sprintf("%s (",$mailbox_name);
  1084. if($storageQuota != null ){
  1085. $param=sprintf("%sSTORAGE %s",$param,$storageQuota);
  1086. if( $messagesQuota != null ){
  1087. //if we have both types of quota on the same call we must append an space between
  1088. // those parameters
  1089. $param=sprintf("%s ",$param);
  1090. }
  1091. }
  1092. if($messagesQuota != null ){
  1093. $param=sprintf("%sMESSAGES %s",$param,$messagesQuota);
  1094. }
  1095. $param=sprintf("%s)",$param);
  1096. return $this->_genericCommand('SETQUOTAROOT', $param );
  1097. }
  1098. /********************************************************************
  1099. *** RFC2087 IMAP4 QUOTA extension ENDS HERE
  1100. ********************************************************************/
  1101. /********************************************************************
  1102. *** RFC2086 IMAP4 ACL extension BEGINS HERE
  1103. ********************************************************************/
  1104. function cmdSetACL($mailbox_name, $user, $acl)
  1105. {
  1106. //Check if the IMAP server has ACL support
  1107. if( ! $this->hasAclSupport() ){
  1108. return new PEAR_Error("This IMAP server does not support ACL's! ");
  1109. }
  1110. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1111. $user_name=sprintf("\"%s\"",$this->utf_7_encode($user) );
  1112. if(is_array($acl)){
  1113. $acl=implode('',$acl);
  1114. }
  1115. return $this->_genericCommand('SETACL', sprintf("%s %s \"%s\"",$mailbox_name,$user_name,$acl) );
  1116. }
  1117. function cmdDeleteACL($mailbox_name, $user)
  1118. {
  1119. //Check if the IMAP server has ACL support
  1120. if( ! $this->hasAclSupport() ){
  1121. return new PEAR_Error("This IMAP server does not support ACL's! ");
  1122. }
  1123. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1124. return $this->_genericCommand('DELETEACL', sprintf("%s \"%s\"",$mailbox_name,$user) );
  1125. }
  1126. function cmdGetACL($mailbox_name)
  1127. {
  1128. //Check if the IMAP server has ACL support
  1129. if( ! $this->hasAclSupport() ){
  1130. return new PEAR_Error("This IMAP server does not support ACL's! ");
  1131. }
  1132. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1133. $ret = $this->_genericCommand('GETACL', sprintf("%s",$mailbox_name) );
  1134. if(isset( $ret["PARSED"] ) ){
  1135. $ret['PARSED']=$ret["PARSED"][0]["EXT"];
  1136. }
  1137. return $ret;
  1138. }
  1139. function cmdListRights($mailbox_name, $user)
  1140. {
  1141. //Check if the IMAP server has ACL support
  1142. if( ! $this->hasAclSupport() ){
  1143. return new PEAR_Error("This IMAP server does not support ACL's! ");
  1144. }
  1145. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1146. $ret = $this->_genericCommand('LISTRIGHTS', sprintf("%s \"%s\"",$mailbox_name,$user) );
  1147. if(isset( $ret["PARSED"] ) ){
  1148. $ret["PARSED"]=$ret["PARSED"][0]["EXT"];
  1149. }
  1150. return $ret;
  1151. }
  1152. function cmdMyRights($mailbox_name)
  1153. {
  1154. //Check if the IMAP server has ACL support
  1155. if( ! $this->hasAclSupport() ){
  1156. return new PEAR_Error("This IMAP server does not support ACL's! ");
  1157. }
  1158. $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
  1159. $ret = $this->_genericCommand('MYRIGHTS', sprintf("%s",$mailbox_name) );
  1160. if(isset( $ret["PARSED"] ) ){
  1161. $ret["PARSED"]=$ret["PARSED"][0]["EXT"];
  1162. }
  1163. return $ret;
  1164. }
  1165. /********************************************************************
  1166. *** RFC2086 IMAP4 ACL extension ENDs HERE
  1167. ********************************************************************/
  1168. /*******************************************************************************
  1169. *** draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension BEGINS HERE
  1170. ********************************************************************************/
  1171. function cmdSetAnnotation($mailbox_name, $entry, $values)
  1172. {
  1173. // Check if the IMAP server has ANNOTATEMORE support
  1174. if(!$this->hasAnnotateMoreSupport()) {
  1175. return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!');
  1176. }
  1177. if (!is_array($values)) {
  1178. return new PEAR_Error('Invalid $values argument passed to cmdSetAnnotation');
  1179. }
  1180. $vallist = '';
  1181. foreach ($values as $name => $value) {
  1182. $vallist .= "\"$name\" \"$value\" ";
  1183. }
  1184. $vallist = rtrim($vallist);
  1185. return $this->_genericCommand('SETANNOTATION', sprintf('"%s" "%s" (%s)', $mailbox_name, $entry, $vallist));
  1186. }
  1187. function cmdDeleteAnnotation($mailbox_name, $entry, $values)
  1188. {
  1189. // Check if the IMAP server has ANNOTATEMORE support
  1190. if(!$this->hasAnnotateMoreSupport()) {
  1191. return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!');
  1192. }
  1193. if (!is_array($values)) {
  1194. return new PEAR_Error('Invalid $values argument passed to cmdDeleteAnnotation');
  1195. }
  1196. $vallist = '';
  1197. foreach ($values as $name) {
  1198. $vallist .= "\"$name\" NIL ";
  1199. }
  1200. $vallist = rtrim($vallist);
  1201. return $this->_genericCommand('SETANNOTATION', sprintf('"%s" "%s" (%s)', $mailbox_name, $entry, $vallist));
  1202. }
  1203. function cmdGetAnnotation($mailbox_name, $entries, $values)
  1204. {
  1205. // Check if the IMAP server has ANNOTATEMORE support
  1206. if(!$this->hasAnnotateMoreSupport()) {
  1207. return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!');
  1208. }
  1209. $entlist = '';
  1210. if (!is_array($entries)) {
  1211. $entries = array($entries);
  1212. }
  1213. foreach ($entries as $name) {
  1214. $entlist .= "\"$name\" ";
  1215. }
  1216. $entlist = rtrim($entlist);
  1217. if (count($entries) > 1) {
  1218. $entlist = "($entlist)";
  1219. }
  1220. $vallist = '';
  1221. if (!is_array($values)) {
  1222. $values = array($values);
  1223. }
  1224. foreach ($values as $name) {
  1225. $vallist .= "\"$name\" ";
  1226. }
  1227. $vallist = rtrim($vallist);
  1228. if (count($values) > 1) {
  1229. $vallist = "($vallist)";
  1230. }
  1231. return $this->_genericCommand('GETANNOTATION', sprintf('"%s" %s %s', $mailbox_name, $entlist, $vallist));
  1232. }
  1233. /*****************************************************************************
  1234. *** draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension ENDs HERE
  1235. ******************************************************************************/
  1236. /********************************************************************
  1237. ***
  1238. *** HERE ENDS THE EXTENSIONS FUNCTIONS
  1239. *** AND BEGIN THE AUXILIARY FUNCTIONS
  1240. ***
  1241. ********************************************************************/
  1242. /**
  1243. * tell if the server has capability $capability
  1244. *
  1245. * @return true or false
  1246. *
  1247. * @access public
  1248. * @since 1.0
  1249. */
  1250. function getServerAuthMethods()
  1251. {
  1252. if( $this->_serverAuthMethods == null ){
  1253. $this->cmdCapability();
  1254. return $this->_serverAuthMethods;
  1255. }
  1256. return false;
  1257. }
  1258. /**
  1259. * tell if the server has capability $capability
  1260. *
  1261. * @return true or false
  1262. *
  1263. * @access public
  1264. * @since 1.0
  1265. */
  1266. function hasCapability($capability)
  1267. {
  1268. if( $this->_serverSupportedCapabilities == null ){
  1269. $this->cmdCapability();
  1270. }
  1271. if($this->_serverSupportedCapabilities != null ){
  1272. if( in_array( $capability , $this->_serverSupportedCapabilities ) ){
  1273. return true;
  1274. }
  1275. }
  1276. return false;
  1277. }
  1278. /**
  1279. * tell if the server has Quota support
  1280. *
  1281. * @return true or false
  1282. *
  1283. * @access public
  1284. * @since 1.0
  1285. */
  1286. function hasQuotaSupport()
  1287. {
  1288. return $this->hasCapability('QUOTA');
  1289. }
  1290. /**
  1291. * tell if the server has Quota support
  1292. *
  1293. * @return true or false
  1294. *
  1295. * @access public
  1296. * @since 1.0
  1297. */
  1298. function hasAclSupport()
  1299. {
  1300. return $this->hasCapability('ACL');
  1301. }
  1302. /**
  1303. * tell if the server has support for the ANNOTATEMORE extension
  1304. *
  1305. * @return true or false
  1306. *
  1307. * @access public
  1308. * @since 1.0
  1309. */
  1310. function hasAnnotateMoreSupport()
  1311. {
  1312. return $this->hasCapability('ANNOTATEMORE');
  1313. }
  1314. /**
  1315. * Parses the responses like RFC822.SIZE and INTERNALDATE
  1316. *
  1317. * @param string the IMAP's server response
  1318. *
  1319. * @return string containing the parsed response
  1320. * @access private
  1321. * @since 1.0
  1322. */
  1323. function _parseOneStringResponse(&$str, $line,$file)
  1324. {
  1325. $this->_parseSpace($str , $line , $file );
  1326. $size = $this->_getNextToken($str,$uid);
  1327. return $uid;
  1328. }
  1329. /**
  1330. * Parses the FLAG response
  1331. *
  1332. * @param string the IMAP's server response
  1333. *
  1334. * @return Array containing the parsed response
  1335. * @access private
  1336. * @since 1.0
  1337. */
  1338. function _parseFLAGSresponse(&$str)
  1339. {
  1340. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1341. $params_arr[] = $this->_arrayfy_content($str);
  1342. $flags_arr=array();
  1343. for( $i = 0 ; $i < count($params_arr[0]) ; $i++ ){
  1344. $flags_arr[] = $params_arr[0][$i];
  1345. }
  1346. return $flags_arr;
  1347. }
  1348. /**
  1349. * Parses the BODY response
  1350. *
  1351. * @param string the IMAP's server response
  1352. *
  1353. * @return Array containing the parsed response
  1354. * @access private
  1355. * @since 1.0
  1356. */
  1357. function _parseBodyResponse(&$str, $command){
  1358. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1359. while($str[0] != ')' && $str!=''){
  1360. $params_arr[] = $this->_arrayfy_content($str);
  1361. }
  1362. return $params_arr;
  1363. }
  1364. /**
  1365. * Makes the content an Array
  1366. *
  1367. * @param string the IMAP's server response
  1368. *
  1369. * @return Array containing the parsed response
  1370. * @access private
  1371. * @since 1.0
  1372. */
  1373. function _arrayfy_content(&$str)
  1374. {
  1375. $params_arr=array();
  1376. $this->_getNextToken($str,$params);
  1377. if($params != '(' ){
  1378. return $params;
  1379. }
  1380. $this->_getNextToken($str,$params,false,false);
  1381. while ( $str != '' && $params != ')'){
  1382. if($params != '' ){
  1383. if($params[0] == '(' ){
  1384. $params=$this->_arrayfy_content( $params );
  1385. }
  1386. if($params != ' ' ){
  1387. //I don't remove the colons (") to handle the case of retriving " "
  1388. // If I remove the colons the parser will interpret this field as an imap separator (space)
  1389. // instead of a valid field so I remove the colons here
  1390. if($params=='""'){
  1391. $params='';
  1392. }else{
  1393. if($params[0]=='"'){
  1394. $params=substr($params,1,strlen($params)-2);
  1395. }
  1396. }
  1397. $params_arr[]=$params;
  1398. }
  1399. }else{
  1400. //if params if empty (for example i'm parsing 2 quotes ("")
  1401. // I'll append an array entry to mantain compatibility
  1402. $params_arr[]=$params;
  1403. }
  1404. $this->_getNextToken($str,$params,false,false);
  1405. }
  1406. return $params_arr;
  1407. }
  1408. /**
  1409. * Parses the BODY[],BODY[TEXT],.... responses
  1410. *
  1411. * @param string the IMAP's server response
  1412. *
  1413. * @return Array containing the parsed response
  1414. * @access private
  1415. * @since 1.0
  1416. */
  1417. function _parseContentresponse(&$str, $command)
  1418. {
  1419. $content = '';
  1420. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1421. $size =$this->_getNextToken($str,$content);
  1422. return array( "CONTENT"=> $content , "CONTENT_SIZE" =>$size );
  1423. }
  1424. /**
  1425. * Parses the ENVELOPE response
  1426. *
  1427. * @param string the IMAP's server response
  1428. *
  1429. * @return Array containing the parsed response
  1430. * @access private
  1431. * @since 1.0
  1432. */
  1433. function _parseENVELOPEresponse(&$str)
  1434. {
  1435. $content = '';
  1436. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1437. $this->_getNextToken($str,$parenthesis);
  1438. if( $parenthesis != '(' ){
  1439. $this->_prot_error("must be a '(' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ );
  1440. }
  1441. // Get the email's Date
  1442. $this->_getNextToken($str,$date);
  1443. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1444. // Get the email's Subject:
  1445. $this->_getNextToken($str,$subject);
  1446. //$subject=$this->decode($subject);
  1447. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1448. //FROM LIST;
  1449. $from_arr = $this->_getAddressList($str);
  1450. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1451. //"SENDER LIST\n";
  1452. $sender_arr = $this->_getAddressList($str);
  1453. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1454. //"REPLY-TO LIST\n";
  1455. $reply_to_arr=$this->_getAddressList($str);
  1456. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1457. //"TO LIST\n";
  1458. $to_arr = $this->_getAddressList($str);
  1459. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1460. //"CC LIST\n";
  1461. $cc_arr = $this->_getAddressList($str);
  1462. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1463. //"BCC LIST|$str|\n";
  1464. $bcc_arr = $this->_getAddressList($str);
  1465. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1466. $this->_getNextToken($str,$in_reply_to);
  1467. $this->_parseSpace($str , __LINE__ , __FILE__ );
  1468. $this->_getNextToken($str,$message_id);
  1469. $this->_getNextToken($str,$parenthesis);
  1470. if( $parenthesis != ')' ){
  1471. $this->_prot_error("must be a ')' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ );
  1472. }
  1473. return array( "DATE"=> $date , "SUBJECT" => $subject,"FROM" => $from_arr,
  1474. "SENDER" => $sender_arr , "REPLY_TO" => $reply_to_arr, "TO" => $to_arr,
  1475. "CC" =>$cc_arr, "BCC"=> $bcc_arr, "IN_REPLY_TO" =>$in_reply_to, "MESSAGE_ID"=>$message_id );
  1476. }
  1477. /**
  1478. * Parses the ARRDLIST as defined in RFC
  1479. *
  1480. * @param string the IMAP's server response
  1481. *
  1482. * @return Array containing the parsed response
  1483. * @access private
  1484. * @since 1.0
  1485. */
  1486. function _getAddressList(&$str)
  1487. {
  1488. $params_arr = $this->_arrayfy_content($str);
  1489. if( !isset( $params_arr ) ){
  1490. return $params_arr;
  1491. }
  1492. if( is_array($params_arr) ){
  1493. $personal_name = $params_arr[0][0];
  1494. $at_domain_list = $params_arr[0][1];
  1495. $mailbox_name = $params_arr[0][2];
  1496. $host_name = $params_arr[0][3];
  1497. if( $mailbox_name!='' && $host_name!='' ){
  1498. $email=$mailbox_name . "@" . $host_name;
  1499. }else{
  1500. $email=false;
  1501. }
  1502. if($email==false){
  1503. $rfc822_email=false;
  1504. }else{
  1505. if(!isset($personal_name)){
  1506. $rfc822_email= "<". $email . ">";
  1507. }else{
  1508. $rfc822_email= "\"". $personal_name ."\" <". $email . ">";
  1509. }
  1510. }
  1511. $email_arr[] = array ( "PERSONAL_NAME"=> $personal_name , "AT_DOMAIN_LIST"=>$at_domain_list ,
  1512. "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name), "HOST_NAME"=> $host_name,
  1513. "EMAIL"=>$email , "RFC822_EMAIL" => $rfc822_email );
  1514. return $email_arr;
  1515. }
  1516. return array();
  1517. }
  1518. /**
  1519. * Utility funcion to find the closing parenthesis ")" Position it takes care of quoted ones
  1520. *
  1521. * @param string the IMAP's server response
  1522. *
  1523. * @return int containing the pos of the closing parenthesis ")"
  1524. * @access private
  1525. * @since 1.0
  1526. */
  1527. function _getClosingBracesPos($str_line, $startDelim ='(', $stopDelim = ')' )
  1528. {
  1529. $len = strlen( $str_line );
  1530. $pos = 0;
  1531. // ignore all extra characters
  1532. // If inside of a string, skip string -- Boundary IDs and other
  1533. // things can have ) in them.
  1534. if ( $str_line[$pos] != $startDelim ) {
  1535. $this->_prot_error("_getClosingParenthesisPos: must start with a '(' but is a '". $str_line[$pos] ."'!!!!\n" .
  1536. "STR_LINE:$str_line|size:$len|POS: $pos\n" , __LINE__ , __FILE__ );
  1537. return( $len );
  1538. }
  1539. for( $pos = 1 ; $pos < $len ; $pos++ ){
  1540. if ($str_line[$pos] == $stopDelim ) {
  1541. break;
  1542. }
  1543. if ($str_line[$pos] == '"') {
  1544. $pos++;
  1545. while ( $str_line[$pos] != '"' && $pos < $len ) {
  1546. if ($str_line[$pos] == "\\" && $str_line[$pos + 1 ] == '"' )
  1547. $pos++;
  1548. if ($str_line[$pos] == "\\" && $str_line[$pos + 1 ] == "\\" )
  1549. $pos++;
  1550. $pos++;
  1551. }
  1552. }
  1553. if ( $str_line[$pos] == $startDelim ) {
  1554. $str_line_aux = substr( $str_line , $pos );
  1555. $pos_aux = $this->_getClosingBracesPos( $str_line_aux );
  1556. $pos+=$pos_aux;
  1557. }
  1558. }
  1559. if( $str_line[$pos] != $stopDelim ){
  1560. $this->_prot_error("_getClosingBracesPos: must be a $stopDelim but is a '". $str_line[$pos] ."'|POS:$pos|STR_LINE:$str_line!!!!" , __LINE__ , __FILE__ );
  1561. }
  1562. if( $pos >= $len )
  1563. return false;
  1564. return $pos;
  1565. }
  1566. /**
  1567. * Utility funcion to get from here to the end of the line
  1568. *
  1569. * @param string the IMAP's server response
  1570. *
  1571. * @return string containing the string to the end of the line
  1572. * @access private
  1573. * @since 1.0
  1574. */
  1575. function _getToEOL(&$str , $including = true)
  1576. {
  1577. $len = strlen( $str );
  1578. if( $including ){
  1579. for($i=0;$i<$len;$i++){
  1580. if( $str[$i] =="\n" )
  1581. break;
  1582. }
  1583. $content=substr($str,0,$i + 1);
  1584. $str=substr($str,$i + 1);
  1585. return $content;
  1586. }else{
  1587. for( $i = 0 ; $i < $len ; $i++ ){
  1588. if( $str[$i] =="\n" || $str[$i] == "\r")
  1589. break;
  1590. }
  1591. $content = substr( $str ,0 , $i );
  1592. $str = substr( $str , $i );
  1593. return $content;
  1594. }
  1595. }
  1596. /**
  1597. * Fetches the next IMAP token or parenthesis
  1598. *
  1599. * @param string the IMAP's server response
  1600. * @param string the next token
  1601. * @param boolean true: the parenthesis IS a token, false: I consider
  1602. * all the response in parenthesis as a token
  1603. *
  1604. * @return int containing the content size
  1605. * @access private
  1606. * @since 1.0
  1607. */
  1608. function _getNextToken(&$str, &$content, $parenthesisIsToken=true,$colonIsToken=true){
  1609. $len = strlen($str);
  1610. $pos = 0;
  1611. $content_size = false;
  1612. $content = false;
  1613. if($str == '' || $len < 2 ){
  1614. $content=$str;
  1615. return $len;
  1616. }
  1617. switch( $str[0] ){
  1618. case '{':
  1619. if( ($posClosingBraces = $this->_getClosingBracesPos($str, '{' , '}' )) == false ){
  1620. $this->_prot_error("_getClosingBracesPos() error!!!" , __LINE__ , __FILE__ );
  1621. }
  1622. if(! is_numeric( ( $strBytes = substr( $str , 1 , $posClosingBraces - 1) ) ) ){
  1623. $this->_prot_error("must be a number but is a '" . $strBytes ."'!!!!" , __LINE__ , __FILE__ );
  1624. }
  1625. if( $str[$posClosingBraces] != '}' ){
  1626. $this->_prot_error("must be a '}' but is a '" . $str[$posClosingBraces] ."'!!!!" , __LINE__ , __FILE__ );
  1627. }
  1628. if( $str[$posClosingBraces + 1] != "\r" ){
  1629. $this->_prot_error("must be a '\\r' but is a '" . $str[$posClosingBraces + 1] ."'!!!!" , __LINE__ , __FILE__ );
  1630. }
  1631. if( $str[$posClosingBraces + 2] != "\n" ){
  1632. $this->_prot_error("must be a '\\n' but is a '" . $str[$posClosingBraces + 2] ."'!!!!" , __LINE__ , __FILE__ );
  1633. }
  1634. $content = substr( $str , $posClosingBraces + 3 , $strBytes );
  1635. if( strlen( $content ) != $strBytes ){
  1636. $this->_prot_error("content size is ". strlen($content) . " but the string reports a size of $strBytes!!!\n" , __LINE__ , __FILE__ );
  1637. }
  1638. $content_size = $strBytes;
  1639. //Advance the string
  1640. $str = substr( $str , $posClosingBraces + $strBytes + 3 );
  1641. break;
  1642. case '"':
  1643. if($colonIsToken){
  1644. for($pos=1;$pos<$len;$pos++){
  1645. if ( $str[$pos] == "\"" ) {
  1646. break;
  1647. }
  1648. if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\"" )
  1649. $pos++;
  1650. if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\" )
  1651. $pos++;
  1652. }
  1653. if($str[$pos] != '"' ){
  1654. $this->_prot_error("must be a '\"' but is a '" . $str[$pos] ."'!!!!" , __LINE__ , __FILE__ );
  1655. }
  1656. $content_size = $pos;
  1657. $content = substr( $str , 1 , $pos - 1 );
  1658. //Advance the string
  1659. $str = substr( $str , $pos + 1 );
  1660. }else{
  1661. for($pos=1;$pos<$len;$pos++){
  1662. if ( $str[$pos] == "\"" ) {
  1663. break;
  1664. }
  1665. if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\"" )
  1666. $pos++;
  1667. if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\" )
  1668. $pos++;
  1669. }
  1670. if($str[$pos] != '"' ){
  1671. $this->_prot_error("must be a '\"' but is a '" . $str[$pos] ."'!!!!" , __LINE__ , __FILE__ );
  1672. }
  1673. $content_size = $pos;
  1674. $content = substr( $str , 0 , $pos + 1 );
  1675. //Advance the string
  1676. $str = substr( $str , $pos + 1 );
  1677. }
  1678. break;
  1679. case "\r":
  1680. $pos = 1;
  1681. if( $str[1] == "\n")
  1682. $pos++;
  1683. $content_size = $pos;
  1684. $content = substr( $str , 0 , $pos );
  1685. $str = substr( $str , $pos );
  1686. break;
  1687. case "\n":
  1688. $pos = 1;
  1689. $content_size = $pos;
  1690. $content = substr( $str , 0 , $pos );
  1691. $str = substr( $str , $pos );
  1692. break;
  1693. case '(':
  1694. if( $parenthesisIsToken == false ){
  1695. $pos = $this->_getClosingBracesPos( $str );
  1696. $content_size = $pos + 1;
  1697. $content = substr( $str , 0 , $pos + 1 );
  1698. $str = substr( $str , $pos + 1 );
  1699. }else{
  1700. $pos = 1;
  1701. $content_size = $pos;
  1702. $content = substr( $str , 0 , $pos );
  1703. $str = substr( $str , $pos );
  1704. }
  1705. break;
  1706. case ')':
  1707. $pos = 1;
  1708. $content_size = $pos;
  1709. $content = substr( $str , 0 , $pos );
  1710. $str = substr( $str , $pos );
  1711. break;
  1712. case ' ':
  1713. $pos = 1;
  1714. $content_size = $pos;
  1715. $content = substr( $str , 0 , $pos );
  1716. $str = substr( $str , $pos );
  1717. break;
  1718. default:
  1719. for( $pos = 0 ; $pos < $len ; $pos++ ){
  1720. if ( $str[$pos] == ' ' || $str[$pos] == "\r" || $str[$pos] == ')' || $str[$pos] == '(' || $str[$pos] == "\n" ) {
  1721. break;
  1722. }
  1723. if ( $str[$pos] == "\\" && $str[$pos + 1 ] == ' ' )
  1724. $pos++;
  1725. if ( $str[$pos] == "\\" && $str[$pos + 1 ] == "\\" )
  1726. $pos++;
  1727. }
  1728. //Advance the string
  1729. if( $pos == 0 ){
  1730. $content_size = 1;
  1731. $content = substr( $str , 0 , 1 );
  1732. $str = substr( $str , 1 );
  1733. }else{
  1734. $content_size = $pos;
  1735. $content = substr( $str , 0 , $pos );
  1736. if($pos < $len){
  1737. $str = substr( $str , $pos );
  1738. }else{
  1739. //if this is the end of the string... exit the switch
  1740. break;
  1741. }
  1742. }
  1743. break;
  1744. }
  1745. return $content_size;
  1746. }
  1747. /**
  1748. * Utility funcion to display to console the protocol errors
  1749. *
  1750. * @param string the error
  1751. * @param int the line producing the error
  1752. * @param string file where the error was produced
  1753. *
  1754. * @return string containing the error
  1755. * @access private
  1756. * @since 1.0
  1757. */
  1758. function _prot_error($str , $line , $file,$printError=true)
  1759. {
  1760. if($printError){
  1761. echo "$line,$file,PROTOCOL ERROR!:$str\n";
  1762. }
  1763. }
  1764. function _getEXTarray(&$str , $startDelim = '(' , $stopDelim = ')'){
  1765. /* I let choose the $startDelim and $stopDelim to allow parsing
  1766. the OK response so I also can parse a response like this
  1767. * OK [UIDNEXT 150] Predicted next UID
  1768. */
  1769. $this->_getNextToken( $str , $parenthesis );
  1770. if( $parenthesis != $startDelim ){
  1771. $this->_prot_error("must be a '$startDelim' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ );
  1772. }
  1773. $parenthesis = '';
  1774. $struct_arr = array();
  1775. while( $parenthesis != $stopDelim && $str != '' ){
  1776. // The command
  1777. $this->_getNextToken( $str , $token );
  1778. $token = strtoupper( $token );
  1779. if( ( $ret = $this->_retrParsedResponse( $str , $token ) ) != false ){
  1780. //$struct_arr[$token] = $ret;
  1781. $struct_arr=array_merge($struct_arr, $ret);
  1782. }
  1783. $parenthesis=$token;
  1784. }//While
  1785. if( $parenthesis != $stopDelim ){
  1786. $this->_prot_error("1_must be a '$stopDelim' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ );
  1787. }
  1788. return $struct_arr;
  1789. }
  1790. function _retrParsedResponse( &$str , $token, $previousToken = null)
  1791. {
  1792. //echo "\n\nTOKEN:$token\r\n";
  1793. switch( $token ){
  1794. case "RFC822.SIZE" :
  1795. return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ));
  1796. break;
  1797. // case "RFC822.TEXT" :
  1798. // case "RFC822.HEADER" :
  1799. case "RFC822" :
  1800. return array($token=>$this->_parseContentresponse( $str , $token ));
  1801. break;
  1802. case "FLAGS" :
  1803. case "PERMANENTFLAGS" :
  1804. return array($token=>$this->_parseFLAGSresponse( $str ));
  1805. break;
  1806. case "ENVELOPE" :
  1807. return array($token=>$this->_parseENVELOPEresponse( $str ));
  1808. break;
  1809. case "EXPUNGE" :
  1810. return false;
  1811. break;
  1812. case "UID" :
  1813. case "UIDNEXT" :
  1814. case "UIDVALIDITY" :
  1815. case "UNSEEN" :
  1816. case "MESSAGES" :
  1817. case "UIDNEXT" :
  1818. case "UIDVALIDITY" :
  1819. case "UNSEEN" :
  1820. case "INTERNALDATE" :
  1821. return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ));
  1822. break;
  1823. case "BODY" :
  1824. case "BODYSTRUCTURE" :
  1825. return array($token=>$this->_parseBodyResponse( $str , $token ));
  1826. break;
  1827. case "RECENT" :
  1828. if( $previousToken != null ){
  1829. $aux["RECENT"]=$previousToken;
  1830. return $aux;
  1831. }else{
  1832. return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ));
  1833. }
  1834. break;
  1835. case "EXISTS" :
  1836. return array($token=>$previousToken);
  1837. break;
  1838. case "READ-WRITE" :
  1839. case "READ-ONLY" :
  1840. return array($token=>$token);
  1841. break;
  1842. case "QUOTA" :
  1843. /*
  1844. A tipical GETQUOTA DIALOG IS AS FOLLOWS
  1845. C: A0004 GETQUOTA user.damian
  1846. S: * QUOTA user.damian (STORAGE 1781460 4000000)
  1847. S: A0004 OK Completed
  1848. */
  1849. $mailbox = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1850. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1851. $this->_parseString( $str , '(' , __LINE__ , __FILE__ );
  1852. $ret_aux = array("MAILBOX"=>$this->utf_7_decode($mailbox) );
  1853. $this->_getNextToken( $str , $quota_resp );
  1854. if( ( $ext = $this->_retrParsedResponse( $str , $quota_resp )) == false){
  1855. $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__ );
  1856. }
  1857. $ret_aux=array_merge($ret_aux,$ext);
  1858. $this->_getNextToken( $str , $separator );
  1859. if( $separator == ')' ){
  1860. return array($token=>$ret_aux);
  1861. }
  1862. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1863. $this->_getNextToken( $str , $quota_resp );
  1864. if( ( $ext = $this->_retrParsedResponse( $str , $quota_resp )) == false){
  1865. $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__ );
  1866. }
  1867. $ret_aux=array_merge($ret_aux,$ext);
  1868. $this->_parseString( $str , ')' , __LINE__ , __FILE__ );
  1869. return array($token=>$ret_aux);
  1870. break;
  1871. case "QUOTAROOT" :
  1872. /*
  1873. A tipical GETQUOTA DIALOG IS AS FOLLOWS
  1874. C: A0004 GETQUOTA user.damian
  1875. S: * QUOTA user.damian (STORAGE 1781460 4000000)
  1876. S: A0004 OK Completed
  1877. */
  1878. $mailbox = $this->utf_7_decode($this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ));
  1879. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) );
  1880. $quotaroot = $this->_parseOneStringResponse( $str_line,__LINE__ , __FILE__ );
  1881. $ret = @array( "MAILBOX"=>$this->utf_7_decode($mailbox) , $token=>$quotaroot );
  1882. return array($token=>$ret);
  1883. break;
  1884. case "STORAGE" :
  1885. $used = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1886. $qmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1887. return array($token=>array("USED"=> $used, "QMAX" => $qmax));
  1888. break;
  1889. case "MESSAGE" :
  1890. $mused = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1891. $mmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1892. return array($token=>array("MUSED"=> $mused, "MMAX" => $mmax));
  1893. break;
  1894. case "FETCH" :
  1895. $this->_parseSpace( $str ,__LINE__ ,__FILE__ );
  1896. // Get the parsed pathenthesis
  1897. $struct_arr = $this->_getEXTarray( $str );
  1898. return $struct_arr;
  1899. break;
  1900. case "CAPABILITY" :
  1901. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1902. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) );
  1903. $struct_arr["CAPABILITIES"] = explode( ' ' , $str_line );
  1904. return array($token=>$struct_arr);
  1905. break;
  1906. case "STATUS" :
  1907. $mailbox = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
  1908. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1909. $ext = $this->_getEXTarray( $str );
  1910. $struct_arr["MAILBOX"] = $this->utf_7_decode($mailbox);
  1911. $struct_arr["ATTRIBUTES"] = $ext;
  1912. return array($token=>$struct_arr);
  1913. break;
  1914. case "LIST" :
  1915. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1916. $params_arr = $this->_arrayfy_content( $str );
  1917. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1918. $this->_getNextToken( $str , $hierarchydelim );
  1919. $this->_parseSpace( $str,__LINE__ , __FILE__);
  1920. $this->_getNextToken( $str , $mailbox_name );
  1921. $result_array = array( "NAME_ATTRIBUTES"=>$params_arr , "HIERACHY_DELIMITER"=>$hierarchydelim , "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name) );
  1922. return array($token=>$result_array);
  1923. break;
  1924. case "LSUB" :
  1925. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1926. $params_arr = $this->_arrayfy_content( $str );
  1927. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1928. $this->_getNextToken( $str , $hierarchydelim );
  1929. $this->_parseSpace( $str,__LINE__ , __FILE__);
  1930. $this->_getNextToken( $str , $mailbox_name );
  1931. $result_array = array( "NAME_ATTRIBUTES"=>$params_arr , "HIERACHY_DELIMITER"=>$hierarchydelim , "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name) );
  1932. return array($token=>$result_array);
  1933. break;
  1934. case "SEARCH" :
  1935. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1) );
  1936. $struct_arr["SEARCH_LIST"] = explode( ' ' , $str_line );
  1937. if(count($struct_arr["SEARCH_LIST"]) == 1 && $struct_arr["SEARCH_LIST"][0]==''){
  1938. $struct_arr["SEARCH_LIST"]=null;
  1939. }
  1940. return array($token=>$struct_arr);
  1941. break;
  1942. case "OK" :
  1943. /* TODO:
  1944. parse the [ .... ] part of the response, use the method
  1945. _getEXTarray(&$str,'[',$stopDelim=']')
  1946. */
  1947. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) );
  1948. if($str_line[0] == '[' ){
  1949. $braceLen=$this->_getClosingBracesPos($str_line, '[', ']' );
  1950. $str_aux='('. substr($str_line,1,$braceLen -1). ')';
  1951. $ext_arr=$this->_getEXTarray($str_aux);
  1952. //$ext_arr=array($token=>$this->_getEXTarray($str_aux));
  1953. }else{
  1954. $ext_arr=$str_line;
  1955. //$ext_arr=array($token=>$str_line);
  1956. }
  1957. $result_array = $ext_arr;
  1958. return $result_array;
  1959. break;
  1960. case "NO" :
  1961. /* TODO:
  1962. parse the [ .... ] part of the response, use the method
  1963. _getEXTarray(&$str,'[',$stopDelim=']')
  1964. */
  1965. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) );
  1966. $result_array[] = @array( "COMMAND"=>$token , "EXT"=>$str_line );
  1967. return $result_array;
  1968. break;
  1969. case "BAD" :
  1970. /* TODO:
  1971. parse the [ .... ] part of the response, use the method
  1972. _getEXTarray(&$str,'[',$stopDelim=']')
  1973. */
  1974. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) );
  1975. $result_array[] = array( "COMMAND"=>$token , "EXT"=>$str_line );
  1976. return $result_array;
  1977. break;
  1978. case "BYE" :
  1979. /* TODO:
  1980. parse the [ .... ] part of the response, use the method
  1981. _getEXTarray(&$str,'[',$stopDelim=']')
  1982. */
  1983. $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) );
  1984. $result_array[] = array( "COMMAND"=>$command , "EXT"=> $str_line );
  1985. return $result_array;
  1986. break;
  1987. case "LISTRIGHTS" :
  1988. $this->_parseSpace( $str ,__LINE__ , __FILE__ );
  1989. $this->_getNextToken( $str , $mailbox );
  1990. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1991. $this->_getNextToken( $str , $user );
  1992. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  1993. $this->_getNextToken( $str , $granted );
  1994. $ungranted = explode( ' ' , rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ) );
  1995. $result_array = @array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "USER"=>$user , "GRANTED"=>$granted , "UNGRANTED"=>$ungranted );
  1996. return $result_array;
  1997. break;
  1998. case "MYRIGHTS" :
  1999. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  2000. $this->_getNextToken( $str ,$mailbox );
  2001. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  2002. $this->_getNextToken( $str , $granted );
  2003. $result_array = array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "GRANTED"=>$granted );
  2004. return $result_array;
  2005. break;
  2006. case "ACL" :
  2007. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  2008. $this->_getNextToken( $str , $mailbox );
  2009. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  2010. $acl_arr = explode( ' ' , rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) ) );
  2011. for( $i = 0 ; $i < count( $acl_arr ) ; $i += 2 ){
  2012. $arr[] = array( "USER"=>$acl_arr[$i] , "RIGHTS"=>$acl_arr[ $i + 1 ] );
  2013. }
  2014. $result_array = array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "USERS"=>$arr );
  2015. return $result_array;
  2016. break;
  2017. case "ANNOTATION" :
  2018. $this->_parseSpace($str, __LINE__, __FILE__);
  2019. $this->_getNextToken($str, $mailbox);
  2020. $this->_parseSpace($str, __LINE__, __FILE__);
  2021. $this->_getNextToken($str, $entry);
  2022. $this->_parseSpace($str, __LINE__, __FILE__);
  2023. $attrs = $this->_arrayfy_content($str);
  2024. $result_array = array('MAILBOX' => $mailbox, 'ENTRY' => $entry , 'ATTRIBUTES' => $attrs);
  2025. return $result_array;
  2026. break;
  2027. case "":
  2028. $this->_prot_error( "PROTOCOL ERROR!:str empty!!" , __LINE__ , __FILE__ );
  2029. break;
  2030. case "(":
  2031. $this->_prot_error("OPENING PARENTHESIS ERROR!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ );
  2032. break;
  2033. case ")":
  2034. //"CLOSING PARENTHESIS BREAK!!!!!!!"
  2035. break;
  2036. case "\r\n":
  2037. $this->_prot_error("BREAK!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ );
  2038. break;
  2039. case ' ':
  2040. // this can happen and we just ignore it
  2041. // This happens when - for example - fetch returns more than 1 parammeter
  2042. // for example you ask to get RFC822.SIZE and UID
  2043. //$this->_prot_error("SPACE BREAK!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ );
  2044. break;
  2045. default:
  2046. $body_token=strtoupper(substr($token,0,5));
  2047. //echo "BODYYYYYYY: $body_token\n";
  2048. $rfc822_token=strtoupper(substr($token,0,7));
  2049. //echo "BODYYYYYYY: $rfc822_token|$token\n";
  2050. if( $body_token == 'BODY[' || $body_token == 'BODY.' || $rfc822_token == 'RFC822.' ) {
  2051. //echo "TOKEN:$token\n";
  2052. //$this->_getNextToken( $str , $mailbox );
  2053. return array($token=>$this->_parseContentresponse( $str , $token ));
  2054. }else{
  2055. $this->_prot_error( "UNIMPLEMMENTED! I don't know the parameter '$token' !!!" , __LINE__ , __FILE__ );
  2056. }
  2057. break;
  2058. }
  2059. return false;
  2060. }
  2061. /*
  2062. * Verifies that the next character IS a space
  2063. */
  2064. function _parseSpace(&$str,$line,$file, $printError = true)
  2065. {
  2066. /*
  2067. This code repeats a lot in this class
  2068. so i make it a function to make all the code shorter
  2069. */
  2070. $this->_getNextToken( $str , $space );
  2071. if( $space != ' ' ){
  2072. $this->_prot_error("must be a ' ' but is a '$space' !!!!" , $line , $file,$printError );
  2073. }
  2074. return $space;
  2075. }
  2076. function _parseString( &$str , $char , $line , $file )
  2077. {
  2078. /*
  2079. This code repeats a lot in this class
  2080. so i make it a function to make all the code shorter
  2081. */
  2082. $this->_getNextToken( $str , $char_aux );
  2083. if( strtoupper($char_aux) != strtoupper( $char ) ){
  2084. $this->_prot_error("must be a $char but is a '$char_aux' !!!!", $line , $file );
  2085. }
  2086. return $char_aux;
  2087. }
  2088. function _genericImapResponseParser( &$str , $cmdid = null )
  2089. {
  2090. $result_array=array();
  2091. if( $this->_unParsedReturn ){
  2092. $unparsed_str = $str;
  2093. }
  2094. $this->_getNextToken( $str , $token );
  2095. while( $token != $cmdid && $str != '' ){
  2096. if($token == "+" ){
  2097. //if the token is + ignore the line
  2098. // TODO: verify that this is correct!!!
  2099. $this->_getToEOL( $str );
  2100. $this->_getNextToken( $str , $token );
  2101. }
  2102. $this->_parseString( $str , ' ' , __LINE__ , __FILE__ );
  2103. $this->_getNextToken( $str , $token );
  2104. if( $token == '+' ){
  2105. $this->_getToEOL( $str );
  2106. $this->_getNextToken( $str , $token );
  2107. }else
  2108. if( is_numeric( $token ) ){
  2109. // The token is a NUMBER so I store it
  2110. $msg_nro = $token;
  2111. $this->_parseSpace( $str , __LINE__ , __FILE__ );
  2112. // I get the command
  2113. $this->_getNextToken( $str , $command );
  2114. if( ( $ext_arr = $this->_retrParsedResponse( $str , $command, $msg_nro ) ) == false ){
  2115. // if this bogus response cis a FLAGS () or EXPUNGE response
  2116. // the ignore it
  2117. if( $command != 'FLAGS' && $command != 'EXPUNGE' ){
  2118. $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__, false);
  2119. }
  2120. }
  2121. $result_array[] = array( "COMMAND"=>$command , "NRO"=>$msg_nro , "EXT"=>$ext_arr );
  2122. }else{
  2123. // OK the token is not a NUMBER so it MUST be a COMMAND
  2124. $command = $token;
  2125. /* Call the parser return the array
  2126. take care of bogus responses!
  2127. */
  2128. if( ( $ext_arr = $this->_retrParsedResponse( $str , $command ) ) == false ){
  2129. $this->_prot_error( "bogus response!!!! (COMMAND:$command)" , __LINE__ , __FILE__ );
  2130. }
  2131. $result_array[] = array( "COMMAND"=>$command , "EXT"=>$ext_arr );
  2132. }
  2133. $this->_getNextToken( $str , $token );
  2134. $token = strtoupper( $token );
  2135. if( $token != "\r\n" && $token != '' ){
  2136. $this->_prot_error("PARSE ERROR!!! must be a '\\r\\n' here but is a '$token'!!!! (getting the next line)|STR:|$str|" , __LINE__ , __FILE__ );
  2137. }
  2138. $this->_getNextToken( $str , $token );
  2139. if($token == "+" ){
  2140. //if the token is + ignore the line
  2141. // TODO: verify that this is correct!!!
  2142. $this->_getToEOL( $str );
  2143. $this->_getNextToken( $str , $token );
  2144. }
  2145. }//While
  2146. // OK we finish the UNTAGGED Response now we must parse the FINAL TAGGED RESPONSE
  2147. //TODO: make this a litle more elegant!
  2148. $this->_parseSpace( $str , __LINE__ , __FILE__, false );
  2149. $this->_getNextToken( $str , $cmd_status );
  2150. $str_line = rtrim (substr( $this->_getToEOL( $str ) , 1 ) );
  2151. $response["RESPONSE"]=array( "CODE"=>$cmd_status , "STR_CODE"=>$str_line , "CMDID"=>$cmdid );
  2152. $ret=$response;
  2153. if( !empty($result_array)){
  2154. $ret=array_merge($ret,array("PARSED"=>$result_array) );
  2155. }
  2156. if( $this->_unParsedReturn ){
  2157. $unparsed["UNPARSED"]=$unparsed_str;
  2158. $ret=array_merge($ret,$unparsed);
  2159. }
  2160. if( isset($status_arr) ){
  2161. $status["STATUS"]=$status_arr;
  2162. $ret=array_merge($ret,$status);
  2163. }
  2164. return $ret;
  2165. }
  2166. function _genericCommand($command, $params = '')
  2167. {
  2168. if( !$this->_connected ){
  2169. return new PEAR_Error( "not connected! (CMD:$command)" );
  2170. }
  2171. $cmdid = $this->_getCmdId();
  2172. $this->_putCMD( $cmdid , $command , $params );
  2173. $args=$this->_getRawResponse( $cmdid );
  2174. return $this->_genericImapResponseParser( $args , $cmdid );
  2175. }
  2176. function utf_7_encode($str)
  2177. {
  2178. if($this->_useUTF_7 == false ){
  2179. return $str;
  2180. }
  2181. //return imap_utf7_encode($str);
  2182. $encoded_utf7 = '';
  2183. $base64_part = '';
  2184. if(is_array($str)){
  2185. return new PEAR_Error('error');
  2186. }
  2187. for ($i = 0; $i < strlen($str); $i++) {
  2188. //those chars should be base64 encoded
  2189. if ( ((ord($str[$i]) >= 39 ) and (ord($str[$i]) <= 126 )) or ((ord($str[$i]) >= 32 ) and (ord($str[$i]) <= 37 )) ) {
  2190. if ($base64_part) {
  2191. $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) );
  2192. $base64_part = '';
  2193. }
  2194. $encoded_utf7 = sprintf("%s%s",$encoded_utf7 , $str[$i]);
  2195. } else {
  2196. //handle &
  2197. if (ord($str[$i]) == 38 ) {
  2198. if ($base64_part) {
  2199. $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) );
  2200. $base64_part = '';
  2201. }
  2202. $encoded_utf7 = sprintf("%s&-", $encoded_utf7 );
  2203. } else {
  2204. $base64_part = sprintf("%s%s",$base64_part , $str[$i]);
  2205. //$base64_part = sprintf("%s%s%s",$base64_part , chr(0) , $str[$i]);
  2206. }
  2207. }
  2208. }
  2209. if ($base64_part) {
  2210. $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) );
  2211. $base64_part = '';
  2212. }
  2213. return $encoded_utf7;
  2214. }
  2215. function utf_7_decode($str)
  2216. {
  2217. if($this->_useUTF_7 == false ){
  2218. return $str;
  2219. }
  2220. //return imap_utf7_decode($str);
  2221. $base64_part = '';
  2222. $decoded_utf7 = '';
  2223. for ($i = 0; $i < strlen($str); $i++) {
  2224. if ( strlen($base64_part) > 0 ) {
  2225. if ($str[$i] == '-') {
  2226. if ($base64_part == '&') {
  2227. $decoded_utf7 = sprintf("%s&" , $decoded_utf7 );
  2228. } else {
  2229. $next_part_decoded= base64_decode( substr( $base64_part, 1 ) ) ;
  2230. $decoded_utf7 = sprintf("%s%s", $decoded_utf7 , $next_part_decoded );
  2231. }
  2232. $base64_part = '';
  2233. } else {
  2234. $base64_part = sprintf("%s%s", $base64_part , $str[$i] );
  2235. }
  2236. } else {
  2237. if ($str[$i] == '&') {
  2238. $base64_part = '&';
  2239. } else {
  2240. $decoded_utf7 = sprintf("%s%s", $decoded_utf7 , $str[$i] );
  2241. }
  2242. }
  2243. }
  2244. return $decoded_utf7;
  2245. }
  2246. }//Class
  2247. ?>