Path

ez components / documentation / api reference / 2007.2 / mail


Mail

[ Tutorial ] [ Class tree ] [ Element index ] [ ChangeLog ] [ Credits ]

Source for file imap_transport.php

Documentation is available at imap_transport.php

   1. <?php
   2. /**
   3.  * File containing the ezcMailImapTransport class.
   4.  *
   5.  * @package Mail
   6.  * @version 1.4
   7.  * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
   8.  * @license http://ez.no/licenses/new_bsd New BSD License
   9.  */
  10.  
  11. /**
  12.  * The class ezcMailImapTransport implements functionality for handling IMAP
  13.  * mail servers.
  14.  *
  15.  * The implementation supports most of the commands specified in:
  16.  *  - {@link http://www.faqs.org/rfcs/rfc1730.html} (IMAP4)
  17.  *  - {@link http://www.faqs.org/rfcs/rfc2060.html} (IMAP4rev1)
  18.  *
  19.  * Each user account on the IMAP server has it's own folders (mailboxes).
  20.  * Mailboxes can be created, renamed or deleted. All accounts have a special
  21.  * mailbox called Inbox which cannot be deleted or renamed.
  22.  *
  23.  * Messages are organized in mailboxes, and are identified by a message number
  24.  * (which can change over time) and a unique ID (which does not change under
  25.  * normal circumstances). The commands operating on messages can handle both
  26.  * modes (message numbers or unique IDs).
  27.  *
  28.  * Messages are marked by certain flags (SEEN, DRAFT, etc). Deleting a message
  29.  * actually sets it's DELETED flag, and a later call to {@link expunge()} will
  30.  * delete all the messages marked with the DELETED flag.
  31.  *
  32.  * The IMAP server can be in different states. Most IMAP commands require
  33.  * that a connection is established and a user is authenticated. Certain
  34.  * commands require in addition that a mailbox is selected.
  35.  *
  36.  * The IMAP transport class allows developers to interface with an IMAP server.
  37.  * The commands which support unique IDs to refer to messages are marked with
  38.  * [*] (see {@link ezcMailImapTransportOptions} to find out how to enable
  39.  * unique IDs referencing):
  40.  *
  41.  * Basic commands:
  42.  *  - connect to an IMAP server ({@link __construct()})
  43.  *  - authenticate a user with a username and password ({@link authenticate()})
  44.  *  - select a mailbox ({@link selectMailbox()})
  45.  *  - disconnect from the IMAP server ({@link disconnect()})
  46.  *
  47.  * Work with mailboxes:
  48.  *  - get the list of mailboxes of the user ({@link listMailboxes()})
  49.  *  - create a mailbox ({@link createMailbox()}
  50.  *  - rename a mailbox ({@link renameMailbox()}
  51.  *  - delete a mailbox ({@link deleteMailbox()}
  52.  *  - append a message to a mailbox ({@link append()}
  53.  *  - select a mailbox ({@link selectMailbox()})
  54.  *  - get the status of messages in the current mailbox ({@link status()})
  55.  *  - get the number of messages with a certain flag ({@link countByFlag()}
  56.  *
  57.  * Work with message numbers (on the currently selected mailbox):
  58.  *  - get the message numbers and sizes of all the messages ({@link listMessages()})
  59.  *  - get the message numbers and IDs of all the messages ({@link listUniqueIdentifiers()})
  60.  *  - [*] get the headers of a certain message ({@link top()})
  61.  *  - [*] delete a message ({@link delete()} and {@link expunge()})
  62.  *  - [*] copy messages to another mailbox ({@link copyMessages()})
  63.  *  - [*] get the sizes of the specified messages ({@link fetchSizes()})
  64.  *
  65.  * Work with flags (on the currently selected mailbox):
  66.  *  - [*] get the flags of the specified messages ({@link fetchFlags()})
  67.  *  - [*] set a flag on the specified messages ({@link setFlag()})
  68.  *  - [*] clear a flag from the specified messages ({@link clearFlag()})
  69.  *
  70.  * Work with {@link ezcMailImapSet} sets (parseable with {@link ezcMailParser})
  71.  * (on the currently selected mailbox):
  72.  *  - [*] create a set from all messages ({@link fetchAll()})
  73.  *  - [*] create a set from a certain message ({@link fetchByMessageNr()})
  74.  *  - [*] create a set from a range of messages ({@link fetchFromOffset()})
  75.  *  - [*] create a set from messages with a certain flag ({@link fetchByFlag()})
  76.  *  - [*] create a set from a sorted range of messages ({@link sortFromOffset()})
  77.  *  - [*] create a set from a sorted list of messages ({@link sortMessages()})
  78.  *  - [*] create a set from a free-form search ({@link searchMailbox()})
  79.  *
  80.  * Miscellaneous commands:
  81.  *  - get the capabilities of the IMAP server ({@link capability()})
  82.  *  - get the hierarchy delimiter (useful for nested mailboxes) ({@link getHierarchyDelimiter()})
  83.  *  - issue a NOOP command to keep the connection alive ({@link noop()})
  84.  *
  85.  * The usual operation with an IMAP server is illustrated by this example:
  86.  * <code>
  87.  * // create a new IMAP transport object by specifying the server name, optional port
  88.  * // and optional SSL mode
  89.  * $options = new ezcMailImapTransportOptions();
  90.  * $options->ssl = true;
  91.  * $imap = new ezcMailImapTransport( 'imap.example.com', null, $options );
  92.  *
  93.  * // Authenticate to the IMAP server
  94.  * $imap->authenticate( 'username', 'password' );
  95.  *
  96.  * // Select a mailbox (here 'Inbox')
  97.  * $imap->selectMailbox( 'Inbox' );
  98.  *
  99.  * // issue commands to the IMAP server
 100.  * // for example get the number of RECENT messages
 101.  * $recent = $imap->countByFlag( 'RECENT' );
 102.  *
 103.  * // see the above list of commands or consult the online documentation for
 104.  * // the full list of commands you can issue to an IMAP server and examples
 105.  *
 106.  * // disconnect from the IMAP server
 107.  * $imap->disconnect();
 108.  * </code>
 109.  *
 110.  * See {@link ezcMailImapTransportOptions} for other options you can specify
 111.  * for IMAP.
 112.  *
 113.  * @todo ignore messages of a certain size?
 114.  * @todo // support for signing?
 115.  * @todo listUniqueIdentifiers(): add UIVALIDITY value to UID (like in POP3).
 116.  *        (if necessary).
 117.  *
 118.  * @property ezcMailImapTransportOptions $options 
 119.  *            Holds the options you can set to the IMAP transport.
 120.  *
 121.  * @package Mail
 122.  * @version 1.4
 123.  * @mainclass
 124.  */
 125. class ezcMailImapTransport
 126. {
 127.     /**
 128.      * Internal state when the IMAP transport is not connected to a server.
 129.      *
 130.      * @access private
 131.      */
 132.     const STATE_NOT_CONNECTED 1;
 133.  
 134.     /**
 135.      * Internal state when the IMAP transport is connected to a server,
 136.      * but no successful authentication has been performed.
 137.      *
 138.      * @access private
 139.      */
 140.     const STATE_NOT_AUTHENTICATED 2;
 141.  
 142.     /**
 143.      * Internal state when the IMAP transport is connected to a server
 144.      * and authenticated, but no mailbox is selected yet.
 145.      *
 146.      * @access private
 147.      */
 148.     const STATE_AUTHENTICATED 3;
 149.  
 150.     /**
 151.      * Internal state when the IMAP transport is connected to a server,
 152.      * authenticated, and a mailbox is selected.
 153.      *
 154.      * @access private
 155.      */
 156.     const STATE_SELECTED 4;
 157.  
 158.     /**
 159.      * Internal state when the IMAP transport is connected to a server,
 160.      * authenticated, and a mailbox is selected read only.
 161.      *
 162.      * @access private
 163.      */
 164.     const STATE_SELECTED_READONLY 5;
 165.  
 166.     /**
 167.      * Internal state when the LOGOUT command has been issued to the IMAP
 168.      * server, but before the disconnect has taken place.
 169.      *
 170.      * @access private
 171.      */
 172.     const STATE_LOGOUT 6;
 173.  
 174.     /**
 175.      * The response sent from the IMAP server is "OK".
 176.      *
 177.      * @access private
 178.      */
 179.     const RESPONSE_OK 1;
 180.  
 181.     /**
 182.      * The response sent from the IMAP server is "NO".
 183.      *
 184.      * @access private
 185.      */
 186.     const RESPONSE_NO 2;
 187.  
 188.     /**
 189.      * The response sent from the IMAP server is "BAD".
 190.      *
 191.      * @access private
 192.      */
 193.     const RESPONSE_BAD 3;
 194.  
 195.     /**
 196.      * The response sent from the IMAP server is untagged (starts with "*").
 197.      *
 198.      * @access private
 199.      */
 200.     const RESPONSE_UNTAGGED 4;
 201.  
 202.     /**
 203.      * The response sent from the IMAP server requires the client to send
 204.      * information (starts with "+").
 205.      *
 206.      * @access private
 207.      */
 208.     const RESPONSE_FEEDBACK 5;
 209.  
 210.     /**
 211.      * Use UID commands (access messages by their unique ID).
 212.      *
 213.      * @access private
 214.      */
 215.     const UID 'UID ';
 216.  
 217.     /**
 218.      * Use message number commands (access messages by their message numbers).
 219.      *
 220.      * @access private
 221.      */
 222.     const NO_UID '';
 223.  
 224.     /**
 225.      * Basic flags are used by {@link setFlag()} and {@link clearFlag()}
 226.      *
 227.      * Basic flags:
 228.      *  - ANSWERED   - message has been answered
 229.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
 230.      *  - DRAFT      - message is marked as a draft
 231.      *  - FLAGGED    - message is "flagged" for urgent/special attention
 232.      *  - SEEN       - message has been read
 233.      *
 234.      * @var array(string) 
 235.      */
 236.     protected static $basicFlags array'ANSWERED''DELETED''DRAFT''FLAGGED''SEEN' );
 237.  
 238.     /**
 239.      * Extended flags are used by {@link searchByFlag()}
 240.      *
 241.      * Basic flags:
 242.      *  - ANSWERED   - message has been answered
 243.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
 244.      *  - DRAFT      - message is marked as a draft
 245.      *  - FLAGGED    - message is "flagged" for urgent/special attention
 246.      *  - RECENT     - message is recent
 247.      *  - SEEN       - message has been read
 248.      *
 249.      * Opposites of the above flags:
 250.      *  - UNANSWERED
 251.      *  - UNDELETED
 252.      *  - UNDRAFT
 253.      *  - UNFLAGGED
 254.      *  - OLD
 255.      *  - UNSEEN
 256.      *
 257.      * Composite flags:
 258.      *  - NEW        - equivalent to RECENT + UNSEEN
 259.      *  - ALL        - all the messages
 260.      *
 261.      * @var array(string) 
 262.      */
 263.     protected static $extendedFlags array'ALL''ANSWERED''DELETED''DRAFT''FLAGGED''NEW''OLD''RECENT''SEEN''UNANSWERED''UNDELETED''UNDRAFT''UNFLAGGED''UNRECENT''UNSEEN' );
 264.  
 265.     /**
 266.      * Used to generate a tag for sending commands to the IMAP server.
 267.      *
 268.      * @var string 
 269.      */
 270.     protected $currentTag = 'A0000';
 271.  
 272.     /**
 273.      * Holds the connection state.
 274.      *
 275.      * @var int {@link STATE_NOT_CONNECTED},
 276.      *           {@link STATE_NOT_AUTHENTICATED},
 277.      *           {@link STATE_AUTHENTICATED},
 278.      *           {@link STATE_SELECTED},
 279.      *           {@link STATE_SELECTED_READONLY} or
 280.      *           {@link STATE_LOGOUT}.
 281.      */
 282.     protected $state = self::STATE_NOT_CONNECTED;
 283.  
 284.     /**
 285.      * Holds the currently selected mailbox.
 286.      *
 287.      * @var string 
 288.      */
 289.     protected $selectedMailbox = null;
 290.  
 291.     /**
 292.      * Holds the connection to the IMAP server.
 293.      *
 294.      * @var ezcMailTransportConnection 
 295.      */
 296.     protected $connection = null;
 297.  
 298.     /**
 299.      * Holds the options for an IMAP transport connection.
 300.      *
 301.      * @var ezcMailImapTransportOptions 
 302.      */
 303.     private $options;
 304.  
 305.     /**
 306.      * Creates a new IMAP transport and connects to the $server at $port.
 307.      *
 308.      * You can specify the $port if the IMAP server is not on the default port
 309.      * 993 (for SSL connections) or 143 (for plain connections). Use the $options
 310.      * parameter to specify an SSL connection.
 311.      *
 312.      * See {@link ezcMailImapTransportOptions} for options you can specify for
 313.      * IMAP.
 314.      *
 315.      * Example of creating an IMAP transport:
 316.      * <code>
 317.      * // replace with your IMAP server address
 318.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
 319.      *
 320.      * // if you want to use SSL:
 321.      * $options = new ezcMailImapTransportOptions();
 322.      * $options->ssl = true;
 323.      *
 324.      * $imap = new ezcMailImapTransport( 'imap.example.com', null, $options );
 325.      * </code>
 326.      *
 327.      * @throws ezcMailTransportException
 328.      *          if it was not possible to connect to the server
 329.      * @throws ezcBaseExtensionNotFoundException
 330.      *          if trying to use SSL and the extension openssl is not installed
 331.      * @throws ezcBasePropertyNotFoundException
 332.      *          if $options contains a property not defined
 333.      * @throws ezcBaseValueException
 334.      *          if $options contains a property with a value not allowed
 335.      * @param string $server 
 336.      * @param int $port 
 337.      * @param ezcMailImapTransportOptions|array(string=>mixed)$options 
 338.      */
 339.     public function __construct$server$port null$options array() )
 340.     {
 341.         if $options instanceof ezcMailImapTransportOptions )
 342.         {
 343.             $this->options $options;
 344.         }
 345.         else if is_array$options ) )
 346.         {
 347.             $this->options new ezcMailImapTransportOptions$options );
 348.         }
 349.         else
 350.         {
 351.             throw new ezcBaseValueException"options"$options"ezcMailImapTransportOptions|array" );
 352.         }
 353.  
 354.         if $port === null )
 355.         {
 356.             $port $this->options->ssl === true 993 143;
 357.         }
 358.         $this->connection = new ezcMailTransportConnection$server$port$this->options );
 359.         // get the server greeting
 360.         $response $this->connection->getLine();
 361.         if strpos$response"* OK" === false )
 362.         {
 363.             throw new ezcMailTransportException"The connection to the IMAP server is ok, but a negative response from server was received. Try again later." );
 364.         }
 365.         $this->state = self::STATE_NOT_AUTHENTICATED;
 366.     }
 367.  
 368.     /**
 369.      * Destructs the IMAP transport.
 370.      *
 371.      * If there is an open connection to the IMAP server it is closed.
 372.      */
 373.     public function __destruct()
 374.     {
 375.         $this->disconnect();
 376.     }
 377.  
 378.     /**
 379.      * Sets the value of the property $name to $value.
 380.      *
 381.      * @throws ezcBasePropertyNotFoundException
 382.      *          if the property $name does not exist
 383.      * @throws ezcBaseValueException
 384.      *          if $value is not accepted for the property $name
 385.      * @param string $name 
 386.      * @param mixed $value 
 387.      * @ignore
 388.      */
 389.     public function __set$name$value )
 390.     {
 391.         switch $name )
 392.         {
 393.             case 'options':
 394.                 if !$value instanceof ezcMailImapTransportOptions ) )
 395.                 {
 396.                     throw new ezcBaseValueException'options'$value'instanceof ezcMailImapTransportOptions' );
 397.                 }
 398.                 $this->options $value;
 399.                 break;
 400.  
 401.             default:
 402.                 throw new ezcBasePropertyNotFoundException$name );
 403.         }
 404.     }
 405.  
 406.     /**
 407.      * Returns the value of the property $name.
 408.      *
 409.      * @throws ezcBasePropertyNotFoundException
 410.      *          if the property $name does not exist
 411.      * @param string $name 
 412.      * @ignore
 413.      */
 414.     public function __get$name )
 415.     {
 416.         switch $name )
 417.         {
 418.             case 'options':
 419.                 return $this->options;
 420.             
 421.             default:
 422.                 throw new ezcBasePropertyNotFoundException$name );
 423.         }
 424.     }
 425.  
 426.     /**
 427.      * Returns true if the property $name is set, otherwise false.
 428.      *
 429.      * @param string $name 
 430.      * @return bool 
 431.      * @ignore
 432.      */
 433.     public function __isset$name )
 434.     {
 435.         switch $name )
 436.         {
 437.             case 'options':
 438.                 return true;
 439.  
 440.             default:
 441.                 return false;
 442.         }
 443.     }
 444.  
 445.     /**
 446.      * Disconnects the transport from the IMAP server.
 447.      */
 448.     public function disconnect()
 449.     {
 450.         if $this->state !== self::STATE_NOT_CONNECTED
 451.              && $this->connection->isConnected(=== true )
 452.         {
 453.             $tag $this->getNextTag();
 454.             $this->connection->sendData"{$tag} LOGOUT);
 455.             // discard the "bye bye" message ("{$tag} OK Logout completed.")
 456.             $this->getResponse$tag );
 457.             $this->state = self::STATE_LOGOUT;
 458.             $this->selectedMailbox = null;
 459.  
 460.             $this->connection->close();
 461.             $this->connection = null;
 462.             $this->state = self::STATE_NOT_CONNECTED;
 463.         }
 464.     }
 465.  
 466.     /**
 467.      * Authenticates the user to the IMAP server with $user and $password.
 468.      *
 469.      * This method should be called directly after the construction of this
 470.      * object.
 471.      *
 472.      * If the server is waiting for the authentication process to respond, the
 473.      * connection with the IMAP server will be closed, and false is returned,
 474.      * and it is the application's task to reconnect and reauthenticate.
 475.      *
 476.      * Example of creating an IMAP transport and authenticating:
 477.      * <code>
 478.      * // replace with your IMAP server address
 479.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
 480.      *
 481.      * // replace the values with your username and password for the IMAP server
 482.      * $imap->authenticate( 'username', 'password' );
 483.      * </code>
 484.      *
 485.      * @throws ezcMailTransportException
 486.      *          if already authenticated
 487.      *          or if the provided username/password combination did not work
 488.      * @param string $user 
 489.      * @param string $password 
 490.      * @return bool 
 491.      */
 492.     public function authenticate$user$password )
 493.     {
 494.         if $this->state != self::STATE_NOT_AUTHENTICATED )
 495.         {
 496.             throw new ezcMailTransportException"Tried to authenticate when there was no connection or when already authenticated." );
 497.         }
 498.  
 499.         $tag $this->getNextTag();
 500.         $this->connection->sendData"{$tag} LOGIN {$user} {$password});
 501.         $response trim$this->connection->getLine() );
 502.         if strpos$response'* OK' !== false )
 503.         {
 504.             // the server is busy waiting for authentication process to
 505.             // respond, so it is a good idea to just close the connection,
 506.             // otherwise the application will be halted until the server
 507.             // recovers
 508.             $this->connection->close();
 509.             $this->connection = null;
 510.             $this->state = self::STATE_NOT_CONNECTED;
 511.             return false;
 512.         }
 513.         if $this->responseType$response != self::RESPONSE_OK )
 514.         {
 515.             throw new ezcMailTransportException"The IMAP server did not accept the username and/or password: {$response}.);
 516.         }
 517.         else
 518.         {
 519.             $this->state = self::STATE_AUTHENTICATED;
 520.             $this->selectedMailbox = null;
 521.         }
 522.         return true;
 523.     }
 524.  
 525.     /**
 526.      * Returns an array with the names of the available mailboxes for the user
 527.      * currently authenticated on the IMAP server.
 528.      *
 529.      * Before calling this method, a connection to the IMAP server must be
 530.      * established and a user must be authenticated successfully.
 531.      *
 532.      * For more information about $reference and $mailbox, consult
 533.      * the IMAP RFCs documents ({@link http://www.faqs.org/rfcs/rfc1730.html}
 534.      * or {@link http://www.faqs.org/rfcs/rfc2060.html}, section 7.2.2.).
 535.      *
 536.      * By default, $reference is "" and $mailbox is "*".
 537.      *
 538.      * The array returned contains the mailboxes available for the connected
 539.      * user on this IMAP server. Inbox is a special mailbox, and it can be
 540.      * specified upper-case or lower-case or mixed-case. The other mailboxes
 541.      * should be specified as they are (to the {@link selectMailbox()} method).
 542.      *
 543.      * Example of listing mailboxes:
 544.      * <code>
 545.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
 546.      * $imap->authenticate( 'username', 'password' );
 547.      *
 548.      * $mailboxes = $imap->listMailboxes();
 549.      * </code>
 550.      *
 551.      * @throws ezcMailMailTransportException
 552.      *          if the current server state is not accepted
 553.      *          or if the server sent a negative response
 554.      * @param string $reference 
 555.      * @param string $mailbox 
 556.      * @return array(string) 
 557.      */
 558.     public function listMailboxes$reference ''$mailbox '*' )
 559.     {
 560.         if $this->state != self::STATE_AUTHENTICATED &&
 561.              $this->state != self::STATE_SELECTED &&
 562.              $this->state != self::STATE_SELECTED_READONLY )
 563.         {
 564.             throw new ezcMailTransportException"Can't call listMailboxes() when not successfully logged in." );
 565.         }
 566.  
 567.         $result array();
 568.         $tag $this->getNextTag();
 569.         $this->connection->sendData"{$tag} LIST \"{$reference}\" \"{$mailbox}\");
 570.         $response trim$this->connection->getLine() );
 571.         while strpos$response'* LIST (' !== false )
 572.         {
 573.             // only consider the selectable mailboxes
 574.             if strpos$response"\\Noselect" === false )
 575.             {
 576.                 $response substr$responsestrpos$response"\" " );
 577.                 $response trim$response );
 578.                 $response trim$response"\"" );
 579.                 $result[$response;
 580.  
 581.             }
 582.             $response $this->connection->getLine();
 583.         }
 584.  
 585.         $response $this->getResponse$tag$response );
 586.         if $this->responseType$response != self::RESPONSE_OK )
 587.         {
 588.             throw new ezcMailTransportException"Could not list mailboxes with the parameters '\"{$reference}\"' and '\"{$mailbox}\"': {$response}.);
 589.         }
 590.         return $result;
 591.     }
 592.  
 593.     /**
 594.      * Returns the hierarchy delimiter of the IMAP server, useful for handling
 595.      * nested IMAP folders.
 596.      *
 597.      * For more information about the hierarchy delimiter, consult the IMAP RFCs
 598.      * {@link http://www.faqs.org/rfcs/rfc1730.html} or
 599.      * {@link http://www.faqs.org/rfcs/rfc2060.html}, section 6.3.8.
 600.      *
 601.      * Before calling this method, a connection to the IMAP server must be
 602.      * established and a user must be authenticated successfully.
 603.      *
 604.      * Example of returning the hierarchy delimiter:
 605.      * <code>
 606.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
 607.      * $imap->authenticate( 'username', 'password' );
 608.      *
 609.      * $delimiter = $imap->getDelimiter();
 610.      * </code>
 611.      *
 612.      * After running the above code, $delimiter should be something like "/".
 613.      *
 614.      * @throws ezcMailMailTransportException
 615.      *          if the current server state is not accepted
 616.      *          or if the server sent a negative response
 617.      * @return string 
 618.      */
 619.     public function getHierarchyDelimiter()
 620.     {
 621.         if $this->state != self::STATE_AUTHENTICATED &&
 622.              $this->state != self::STATE_SELECTED &&
 623.              $this->state != self::STATE_SELECTED_READONLY )
 624.         {
 625.             throw new ezcMailTransportException"Can't call getDelimiter() when not successfully logged in." );
 626.         }
 627.  
 628.         $tag $this->getNextTag();
 629.         $this->connection->sendData"{$tag} LIST \"\" \"\");
 630.  
 631.         // there should be only one * LIST response line from IMAP
 632.         $response trim$this->getResponse'* LIST' ) );
 633.         $parts explode'"'$response );
 634.  
 635.         if count$parts >= )
 636.         {
 637.             $result $parts[1];
 638.         }
 639.         else
 640.         {
 641.             throw new ezcMailTransportException"Could not retrieve the hierarchy delimiter: {$response}.);
 642.         }
 643.  
 644.         $response $this->getResponse$tag$response );
 645.         if $this->responseType$response != self::RESPONSE_OK )
 646.         {
 647.             throw new ezcMailTransportException"Could not retrieve the hierarchy delimiter: {$response}.);
 648.         }
 649.         return $result;
 650.     }
 651.  
 652.     /**
 653.      * Selects the mailbox $mailbox, which will be the active mailbox for the
 654.      * subsequent commands until it is changed.
 655.      *
 656.      * Before calling this method, a connection to the IMAP server must be
 657.      * established and a user must be authenticated successfully.
 658.      *
 659.      * Inbox is a special mailbox and can be specified with any case.
 660.      *
 661.      * This method should be called after authentication, and before fetching
 662.      * any messages.
 663.      *
 664.      * Example of selecting a mailbox:
 665.      * <code>
 666.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
 667.      * $imap->authenticate( 'username', 'password' );
 668.      *
 669.      * $imap->selectMailbox( 'Reports 2006' );
 670.      * </code>
 671.      *
 672.      * @throws ezcMailMailTransportException
 673.      *          if the current server state is not accepted
 674.      *          or if the server sent a negative response
 675.      * @param string $mailbox 
 676.      * @param bool $readOnly 
 677.      */
 678.     public function selectMailbox$mailbox$readOnly false )
 679.     {
 680.         if $this->state != self::STATE_AUTHENTICATED &&
 681.              $this->state != self::STATE_SELECTED &&
 682.              $this->state != self::STATE_SELECTED_READONLY )
 683.         {
 684.             throw new ezcMailTransportException"Can't call selectMailbox() when not successfully logged in." );
 685.         }
 686.  
 687.         $tag $this->getNextTag();
 688.  
 689.         // if the mailbox selection will be successful, $state will be STATE_SELECTED
 690.         // or STATE_SELECTED_READONLY, depending on the $readOnly parameter
 691.         if $readOnly !== true 
 692.         {
 693.             $this->connection->sendData"{$tag} SELECT \"{$mailbox}\");
 694.             $state self::STATE_SELECTED;
 695.         }
 696.         else
 697.         {
 698.             $this->connection->sendData"{$tag} EXAMINE \"{$mailbox}\");
 699.             $state self::STATE_SELECTED_READONLY;
 700.         }
 701.  
 702.         // if the selecting of the mailbox fails (with "NO" or "BAD" response
 703.         // from the server), $state reverts to STATE_AUTHENTICATED
 704.         $response trim$this->getResponse$tag ) );
 705.         if $this->responseType$response == self::RESPONSE_OK )
 706.         {
 707.             $this->state = $state;
 708.             $this->selectedMailbox = $mailbox;
 709.         }
 710.         else
 711.         {
 712.             $this->state = self::STATE_AUTHENTICATED;
 713.             $this->selectedMailbox = null;
 714.             throw new ezcMailTransportException"Could not select mailbox '{$mailbox}': {$response}.);
 715.         }
 716.     }
 717.  
 718.     /**
 719.      * Creates the mailbox $mailbox.
 720.      *
 721.      * Inbox cannot be created.
 722.      *
 723.      * Before calling this method, a connection to the IMAP server must be
 724.      * established and a user must be authenticated successfully.
 725.      *
 726.      * @throws ezcMailTransportException
 727.      *          if the current server state is not accepted
 728.      *          or if the server sent a negative response
 729.      * @param string $mailbox 
 730.      * @return bool 
 731.      */
 732.     public function createMailbox$mailbox )
 733.     {
 734.         if $this->state != self::STATE_AUTHENTICATED &&
 735.              $this->state != self::STATE_SELECTED &&
 736.              $this->state != self::STATE_SELECTED_READONLY )
 737.         {
 738.             throw new ezcMailTransportException"Can't call createMailbox() when not successfully logged in." );
 739.         }
 740.  
 741.         $tag $this->getNextTag();
 742.         $this->connection->sendData"{$tag} CREATE \"{$mailbox}\");
 743.         $response trim$this->getResponse$tag ) );
 744.         if $this->responseType$response != self::RESPONSE_OK )
 745.         {
 746.             throw new ezcMailTransportException"The IMAP server could not create mailbox '{$mailbox}': {$response}.);
 747.         }
 748.         return true;
 749.     }
 750.  
 751.     /**
 752.      * Renames the mailbox $mailbox to $newName.
 753.      *
 754.      * Inbox cannot be renamed.
 755.      *
 756.      * Before calling this method, a connection to the IMAP server must be
 757.      * established and a user must be authenticated successfully.
 758.      *
 759.      * @throws ezcMailTransportException
 760.      *          if the current server state is not accepted
 761.      *          or if trying to rename the currently selected mailbox
 762.      *          or if the server sent a negative response
 763.      * @param string $mailbox 
 764.      * @param string $newName 
 765.      * @return bool 
 766.      */
 767.     public function renameMailbox$mailbox$newName )
 768.     {
 769.         if $this->state != self::STATE_AUTHENTICATED &&
 770.              $this->state != self::STATE_SELECTED &&
 771.              $this->state != self::STATE_SELECTED_READONLY )
 772.         {
 773.             throw new ezcMailTransportException"Can't call renameMailbox() when not successfully logged in." );
 774.         }
 775.  
 776.         if strtolower$this->selectedMailbox == strtolower$mailbox ) )
 777.         {
 778.             throw new ezcMailTransportException"Can't rename the currently selected mailbox." );
 779.         }
 780.  
 781.         $tag $this->getNextTag();
 782.         $this->connection->sendData"{$tag} RENAME \"{$mailbox}\" \"{$newName}\");
 783.         $response trim$this->getResponse$tag ) );
 784.         if $this->responseType$response != self::RESPONSE_OK )
 785.         {
 786.             throw new ezcMailTransportException"The IMAP server could not rename the mailbox '{$mailbox}to '{$newName}': {$response}.);
 787.         }
 788.         return true;
 789.     }
 790.  
 791.     /**
 792.      * Deletes the mailbox $mailbox.
 793.      *
 794.      * Inbox and the the currently selected mailbox cannot be deleted.
 795.      *
 796.      * Before calling this method, a connection to the IMAP server must be
 797.      * established and a user must be authenticated successfully.
 798.      *
 799.      * @throws ezcMailTransportException
 800.      *          if the current server state is not accepted
 801.      *          or if trying to delete the currently selected mailbox
 802.      *          or if the server sent a negative response
 803.      * @param string $mailbox 
 804.      * @return bool 
 805.      */
 806.     public function deleteMailbox$mailbox )
 807.     {
 808.         if $this->state != self::STATE_AUTHENTICATED &&
 809.              $this->state != self::STATE_SELECTED &&
 810.              $this->state != self::STATE_SELECTED_READONLY )
 811.         {
 812.             throw new ezcMailTransportException"Can't call deleteMailbox() when not successfully logged in." );
 813.         }
 814.  
 815.         if strtolower$this->selectedMailbox == strtolower$mailbox ) )
 816.         {
 817.             throw new ezcMailTransportException"Can't delete the currently selected mailbox." );
 818.         }
 819.  
 820.         $tag $this->getNextTag();
 821.         $this->connection->sendData"{$tag} DELETE \"{$mailbox}\");
 822.         $response trim$this->getResponse$tag ) );
 823.         if $this->responseType$response != self::RESPONSE_OK )
 824.         {
 825.             throw new ezcMailTransportException"The IMAP server could not delete the mailbox '{$mailbox}': {$response}.);
 826.         }
 827.         return true;
 828.     }
 829.  
 830.     /** 
 831.      * Copies message(s) from the currently selected mailbox to mailbox
 832.      * $destination.
 833.      *
 834.      * This method supports unique IDs instead of message numbers. See
 835.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
 836.      * referencing.
 837.      *
 838.      * Warning! When using unique IDs referencing and trying to copy a message
 839.      * with an ID that does not exist, this method will not throw an exception.
 840.      *
 841.      * @todo Find out if it is possible to catch this IMAP bug.
 842.      *
 843.      *  $messages can be:
 844.      *   - a single message number (eg: '1')
 845.      *   - a message range (eg. '1:4')
 846.      *   - a message list (eg. '1,2,4')
 847.      *
 848.      *  Before calling this method, a connection to the IMAP server must be
 849.      *  established and a user must be authenticated successfully, and a mailbox
 850.      *  must be selected (the mailbox from which messages will be copied).
 851.      *
 852.      *  Example of copying 3 messages to a mailbox:
 853.      *  <code>
 854.      *  $imap = new ezcMailImapTransport( 'imap.example.com' );
 855.      *  $imap->authenticate( 'username', 'password' );
 856.      *  $imap->selectMailbox( 'Inbox' );
 857.      *
 858.      *  $imap->copyMessages( '1,2,4', 'Reports 2006' );
 859.      *  </code>
 860.      *
 861.      *  The above code will copy the messages with numbers 1, 2 and 4 from Inbox
 862.      *  to Reports 2006.
 863.      *
 864.      * @throws ezcMailTransportException
 865.      *          if the current server state is not accepted
 866.      *          or if the server sent a negative response
 867.      * @param string $messages 
 868.      * @param string $destination 
 869.      * @return bool 
 870.      */
 871.     public function copyMessages$messages$destination )
 872.     {
 873.         $uid $this->options->uidReferencing self::UID self::NO_UID;
 874.  
 875.         if $this->state != self::STATE_SELECTED &&
 876.              $this->state != self::STATE_SELECTED_READONLY )
 877.         {
 878.             throw new ezcMailTransportException"Can't call copyMessages() on the IMAP transport when a mailbox is not selected." );
 879.         }
 880.     
 881.         $tag $this->getNextTag();
 882.         $this->connection->sendData"{$tag} {$uid}COPY {$messages} \"{$destination}\");
 883.         
 884.         $response trim$this->getResponse$tag ) );
 885.         if $this->responseType$response != self::RESPONSE_OK )
 886.         {
 887.             throw new ezcMailTransportException"The IMAP server could not copy '{$messages}to '{$destination}': {$response}.);
 888.         }
 889.         return true;
 890.     }
 891.  
 892.     /**
 893.      * Returns a list of the not deleted messages in the current mailbox.
 894.      *
 895.      * It returns only the messages with the flag DELETED not set.
 896.      *
 897.      * Before calling this method, a connection to the IMAP server must be
 898.      * established and a user must be authenticated successfully, and a mailbox
 899.      * must be selected.
 900.      *
 901.      * The format of the returned array is
 902.      * <code>
 903.      *   array( message_id => size );
 904.      * </code>
 905.      *
 906.      * Example:
 907.      * <code>
 908.      *   array( 2 => 1700, 5 => 1450, 6 => 21043 );
 909.      * </code>
 910.      *
 911.      * If $contentType is set, it returns only the messages with
 912.      * $contentType in the Content-Type header.
 913.      *
 914.      * For example $contentType can be "multipart/mixed" to return only the
 915.      * messages with attachments.
 916.      *
 917.      * @throws ezcMailTransportException
 918.      *          if a mailbox is not selected
 919.      *          or if the server sent a negative response
 920.      * @param string $contentType 
 921.      * @return array(int) 
 922.      */
 923.     public function listMessages$contentType null )
 924.     {
 925.         if $this->state != self::STATE_SELECTED &&
 926.              $this->state != self::STATE_SELECTED_READONLY )
 927.         {
 928.             throw new ezcMailTransportException"Can't call listMessages() on the IMAP transport when a mailbox is not selected." );
 929.         }
 930.  
 931.         $messageList array();
 932.         $messages array();
 933.  
 934.         // get the numbers of the existing messages
 935.         $tag $this->getNextTag();
 936.         $command "{$tag} SEARCH UNDELETED";
 937.         if !is_null$contentType ) )
 938.         {
 939.             $command .= " HEADER \"Content-Type\" \"{$contentType}\"";
 940.         }
 941.         $this->connection->sendData$command );
 942.         $response trim$this->getResponse'* SEARCH' ) );
 943.         if strpos$response'* SEARCH' !== false )
 944.         {
 945.             $ids trimsubstr$response) );
 946.             if $ids !== "" )
 947.             {
 948.                 $messageList explode' '$ids );
 949.             }
 950.         }
 951.         // skip the OK response ("{$tag} OK Search completed.")
 952.         $response trim$this->getResponse$tag$response ) );
 953.         if $this->responseType$response != self::RESPONSE_OK )
 954.         {
 955.             throw new ezcMailTransportException"The IMAP server could not list messages: {$response}.);
 956.         }
 957.  
 958.         if !empty$messageList ) )
 959.         {
 960.             // get the sizes of the messages
 961.             $tag $this->getNextTag();
 962.             $query trimimplode','$messageList ) );
 963.             $this->connection->sendData"{$tag} FETCH {$query} RFC822.SIZE);
 964.             $response $this->getResponse'FETCH (' );
 965.             $currentMessage trimreset$messageList ) );
 966.             while strpos$response'FETCH (' !== false )
 967.             {
 968.                 $line $response;
 969.                 $line explode' '$line );
 970.                 $line trim$line[count$line 1);
 971.                 $line substr$line0strlen$line );
 972.                 $messages[$currentMessageintval$line );
 973.                 $currentMessage next$messageList );
 974.                 $response $this->connection->getLine();
 975.             }
 976.             // skip the OK response ("{$tag} OK Fetch completed.")
 977.             $response trim$this->getResponse$tag$response ) );
 978.             if $this->responseType$response != self::RESPONSE_OK )
 979.             {
 980.                 throw new ezcMailTransportException"The IMAP server could not list messages: {$response}.);
 981.             }
 982.         }
 983.         return $messages;
 984.     }
 985.  
 986.     /**
 987.      * Fetches the sizes in bytes for messages $messages.
 988.      *
 989.      * This method supports unique IDs instead of message numbers. See
 990.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
 991.      * referencing.
 992.      *
 993.      * $messages is an array of message numbers, for example:
 994.      * <code>
 995.      *   array( 1, 2, 4 );
 996.      * </code>
 997.      *
 998.      * The format of the returned array is:
 999.      * <code>
1000.      *   array( message_number => size )
1001.      * </code>
1002.      *
1003.      * Before calling this method, a connection to the IMAP server must be
1004.      * established and a user must be authenticated successfully, and a mailbox
1005.      * must be selected.
1006.      *
1007.      * Example:
1008.      * <code>
1009.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1010.      * $imap->authenticate( 'username', 'password' );
1011.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1012.      *
1013.      * $sizes = $imap->fetchSizes( array( 1, 2, 4 ) );
1014.      * </code>
1015.      *
1016.      * The returned array $sizes will be something like:
1017.      * <code>
1018.      *   array( 1 => 1043,
1019.      *          2 => 203901,
1020.      *          4 => 14277
1021.      *        );
1022.      * </code>
1023.      *
1024.      * @throws ezcMailTransportException
1025.      *          if a mailbox is not selected
1026.      *          or if the server sent a negative response
1027.      * @param array $messages 
1028.      * @return array(int) 
1029.      */
1030.     public function fetchSizes$messages )
1031.     {
1032.         $uid $this->options->uidReferencing self::UID self::NO_UID;
1033.  
1034.         if $this->state != self::STATE_SELECTED &&
1035.              $this->state != self::STATE_SELECTED_READONLY )
1036.         {
1037.             throw new ezcMailTransportException"Can't call fetchSizes() on the IMAP transport when a mailbox is not selected." );
1038.         }
1039.  
1040.         $sizes array();
1041.         $ids implode$messages',' );
1042.  
1043.         $tag $this->getNextTag();
1044.         $this->connection->sendData"{$tag} {$uid}FETCH {$ids} (RFC822.SIZE));
1045.  
1046.         $response trim$this->connection->getLine() );
1047.         while strpos$response$tag === false )
1048.         {
1049.             if strpos$response' FETCH (' !== false )
1050.             {
1051.                 if $this->options->uidReferencing )
1052.                 {
1053.                     preg_match'/\*\s.*\sFETCH\s\(RFC822\.SIZE\s(.*)\sUID\s(.*)\)/U'$response$matches );
1054.                     $sizes[intval$matches[2)= (int) $matches[1];
1055.                 }
1056.                 else
1057.                 {
1058.                     preg_match'/\*\s(.*)\sFETCH\s\(RFC822\.SIZE\s(.*)\)/U'$response$matches );
1059.                     $sizes[intval$matches[1)= (int) $matches[2];
1060.                 }
1061.  
1062.             }
1063.             $response trim$this->connection->getLine() );
1064.         }
1065.  
1066.         if $this->responseType$response != self::RESPONSE_OK )
1067.         {
1068.             throw new ezcMailTransportException"The IMAP server could not fetch flags for the messages '{$messages}': {$response}.);
1069.         }
1070.         return $sizes;
1071.     }
1072.  
1073.     /**
1074.      * Returns information about the messages in the current mailbox.
1075.      *
1076.      * The information returned through the parameters is:
1077.      *  - $numMessages = number of not deleted messages in the selected mailbox
1078.      *  - $sizeMessages = sum of the not deleted messages sizes
1079.      *  - $recent = number of recent and not deleted messages
1080.      *  - $unseen = number of unseen and not deleted messages
1081.      *
1082.      * Before calling this method, a connection to the IMAP server must be
1083.      * established and a user must be authenticated successfully, and a mailbox
1084.      * must be selected.
1085.      *
1086.      * Example of returning the status of the currently selected mailbox:
1087.      * <code>
1088.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1089.      * $imap->authenticate( 'username', 'password' );
1090.      * $imap->selectMailbox( 'Inbox' );
1091.      *
1092.      * $imap->status( $numMessages, $sizeMessages, $recent, $unseen );
1093.      * </code>
1094.      *
1095.      * After running the above code, $numMessages, $sizeMessages, $recent
1096.      * and $unseen will be populated with values.
1097.      *
1098.      * @throws ezcMailTransportException
1099.      *          if a mailbox is not selected
1100.      *          or if the server sent a negative response
1101.      * @param int &$numMessages 
1102.      * @param int &$sizeMessages 
1103.      * @param int &$recent 
1104.      * @param int &$unseen 
1105.      * @return bool 
1106.      */
1107.     public function status&$numMessages&$sizeMessages&$recent 0&$unseen )
1108.     {
1109.         if $this->state != self::STATE_SELECTED &&
1110.              $this->state != self::STATE_SELECTED_READONLY )
1111.         {
1112.             throw new ezcMailTransportException"Can't call status() on the IMAP transport when a mailbox is not selected." );
1113.         }
1114.         $messages $this->listMessages();
1115.         $numMessages count$messages );
1116.         $sizeMessages array_sum$messages );
1117.         $messages array_keys$messages );
1118.         $recentMessages array_intersect$this->searchByFlag"RECENT" )$messages );
1119.         $unseenMessages array_intersect$this->searchByFlag"UNSEEN" )$messages );
1120.         $recent count$recentMessages );
1121.         $unseen count$unseenMessages );
1122.         return true;
1123.     }
1124.  
1125.     /**
1126.      * Deletes the message with the message number $msgNum from the current mailbox.
1127.      *
1128.      * This method supports unique IDs instead of message numbers. See
1129.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1130.      * referencing.
1131.      *
1132.      * The message number $msgNum must be a valid identifier fetched with e.g.
1133.      * {@link listMessages()}.
1134.      *
1135.      * The message is not physically deleted, but has its DELETED flag set,
1136.      * and can be later undeleted by clearing its DELETED flag with
1137.      * {@link clearFlag()}.
1138.      *
1139.      * Before calling this method, a connection to the IMAP server must be
1140.      * established and a user must be authenticated successfully, and a mailbox
1141.      * must be selected.
1142.      *
1143.      * @throws ezcMailTransportException
1144.      *          if a mailbox is not selected
1145.      *          or if the server sent a negative response
1146.      * @param int $msgNum 
1147.      * @return bool 
1148.      */
1149.     public function delete$msgNum )
1150.     {
1151.         $uid $this->options->uidReferencing self::UID self::NO_UID;
1152.  
1153.         if $this->state != self::STATE_SELECTED )
1154.         {
1155.             throw new ezcMailTransportException"Can't call delete() when a mailbox is not selected." );
1156.         }
1157.         $tag $this->getNextTag();
1158.         $this->connection->sendData"{$tag} {$uid}STORE {$msgNum} +FLAGS (\\Deleted));
1159.  
1160.         // get the response (should be "{$tag} OK Store completed.")
1161.         $response trim$this->getResponse$tag ) );
1162.         if $this->responseType$response != self::RESPONSE_OK )
1163.         {
1164.             throw new ezcMailTransportException"The IMAP server could not delete the message '{$msgNum}': {$response}.);
1165.         }
1166.         return true;
1167.     }
1168.  
1169.     /**
1170.      * Returns the headers and the first characters from message $msgNum,
1171.      * without setting the SEEN flag.
1172.      *
1173.      * This method supports unique IDs instead of message numbers. See
1174.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1175.      * referencing.
1176.      *
1177.      * If the command failed or if it was not supported by the server an empty
1178.      * string is returned.
1179.      *
1180.      * This method is useful for retrieving the headers of messages from the
1181.      * mailbox as strings, which can be later parsed with {@link ezcMailParser}
1182.      * and {@link ezcMailVariableSet}. In this way the retrieval of the full
1183.      * messages from the server is avoided when building a list of messages.
1184.      *
1185.      * Before calling this method, a connection to the IMAP server must be
1186.      * established and a user must be authenticated successfully, and a mailbox
1187.      * must be selected.
1188.      *
1189.      * Example of listing the mail headers of all the messages in the current
1190.      * mailbox:
1191.      * <code>
1192.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1193.      * $imap->authenticate( 'username', 'password' );
1194.      * $imap->selectMailbox( 'Inbox' );
1195.      *
1196.      * $parser = new ezcMailParser();
1197.      * $messages = $imap->listMessages();
1198.      * foreach ( $messages as $messageNr => $size )
1199.      * {
1200.      *     $set = new ezcMailVariableSet( $imap->top( $messageNr ) );
1201.      *     $mail = $parser->parseMail( $set );
1202.      *     $mail = $mail[0];
1203.      *     echo "From: {$mail->from}, Subject: {$mail->subject}, Size: {$size}\n";
1204.      * }
1205.      * </code>
1206.      *
1207.      * For a more advanced example see the "Mail listing example" in the online
1208.      * documentation.
1209.      *
1210.      * @throws ezcMailTransportException
1211.      *          if a mailbox is not selected
1212.      *          or if the server sent a negative response
1213.      * @param int $msgNum 
1214.      * @param int $chars 
1215.      * @return string 
1216.      */
1217.     public function top$msgNum$chars )
1218.     {
1219.         $uid $this->options->uidReferencing self::UID self::NO_UID;
1220.  
1221.         if $this->state != self::STATE_SELECTED &&
1222.              $this->state != self::STATE_SELECTED_READONLY )
1223.         {
1224.             throw new ezcMailTransportException"Can't call top() on the IMAP transport when a mailbox is not selected." );
1225.         }
1226.  
1227.         $tag $this->getNextTag();
1228.  
1229.         if $chars === )
1230.         {
1231.             $command "{$tag} {$uid}FETCH {$msgNum} (BODY.PEEK[HEADER] BODY.PEEK[TEXT])";
1232.         }
1233.         else
1234.         {
1235.             $command "{$tag} {$uid}FETCH {$msgNum} (BODY.PEEK[HEADER] BODY.PEEK[TEXT]<0.{$chars}>)";
1236.         }
1237.         $this->connection->sendData$command );
1238.         if $this->options->uidReferencing )
1239.         {
1240.             // special case (BUG?) where "UID FETCH {$msgNum}" returns nothing
1241.             $response trim$this->connection->getLine() );
1242.             if $this->responseType$response === self::RESPONSE_OK )
1243.             {
1244.                 throw new ezcMailTransportException"The IMAP server could not fetch the message '{$msgNum}': {$response}.);
1245.             }
1246.         }
1247.         else
1248.         {
1249.             $response $this->getResponse'FETCH (' );
1250.         }
1251.         $message "";
1252.         if strpos$response'FETCH (' !== false )
1253.         {
1254.             $response "";
1255.             while strpos$response'BODY[TEXT]' === false )
1256.             {
1257.                 $message .= $response;
1258.                 $response $this->connection->getLine();
1259.             }
1260.  
1261.             $response $this->connection->getLine();
1262.             while strpos$response$tag === false )
1263.             {
1264.                 $message .= $response;
1265.                 $response $this->connection->getLine();
1266.             }
1267.         }
1268.         // skip the OK response ("{$tag} OK Fetch completed.")
1269.         $response trim$this->getResponse$tag$response ) );
1270.         if $this->responseType$response != self::RESPONSE_OK )
1271.         {
1272.             throw new ezcMailTransportException"The IMAP server could not fetch the message '{$msgNum}': {$response}.);
1273.         }
1274.         return $message;
1275.     }
1276.  
1277.     /**
1278.      * Returns the unique identifiers for the messages from the current mailbox.
1279.      *
1280.      * You can fetch the unique identifier for a specific message by
1281.      * providing the $msgNum parameter.
1282.      *
1283.      * The unique identifier can be used to recognize mail from servers
1284.      * between requests. In contrast to the message numbers the unique
1285.      * numbers assigned to an email usually never changes.
1286.      *
1287.      * The format of the returned array is:
1288.      * <code>
1289.      *   array( message_num => unique_id );
1290.      * </code>
1291.      *
1292.      * Example:
1293.      * <code>
1294.      *   array( 1 => 216, 2 => 217, 3 => 218, 4 => 219 );
1295.      * </code>
1296.      *
1297.      * Before calling this method, a connection to the IMAP server must be
1298.      * established and a user must be authenticated successfully, and a mailbox
1299.      * must be selected.
1300.      *
1301.      * @todo add UIVALIDITY value to UID (like in POP3) (if necessary).
1302.      *
1303.      * @throws ezcMailTransportException
1304.      *          if a mailbox is not selected
1305.      *          or if the server sent a negative response
1306.      * @param int $msgNum 
1307.      * @return array(string) 
1308.      */
1309.     public function listUniqueIdentifiers$msgNum null )
1310.     {
1311.         if $this->state != self::STATE_SELECTED &&
1312.              $this->state != self::STATE_SELECTED_READONLY )
1313.         {
1314.             throw new ezcMailTransportException"Can't call listUniqueIdentifiers() on the IMAP transport when a mailbox is not selected." );
1315.         }
1316.  
1317.         $result array();
1318.         if $msgNum !== null )
1319.         {
1320.             $tag $this->getNextTag();
1321.             $this->connection->sendData"{$tag} UID SEARCH {$msgNum});
1322.             $response $this->getResponse'* SEARCH' );
1323.             if strpos$response'* SEARCH' !== false )
1324.             {
1325.                 $result[(int)$msgNumtrimsubstr$response) );
1326.             }
1327.             $response trim$this->getResponse$tag$response ) );
1328.         }
1329.         else
1330.         {
1331.             $uids array();
1332.             $messages array_keys$this->listMessages() );
1333.             $tag $this->getNextTag();
1334.             $this->connection->sendData"{$tag} UID SEARCH UNDELETED);
1335.             $response $this->getResponse'* SEARCH' );
1336.             if strpos$response'* SEARCH' !== false )
1337.             {
1338.                 $response trimsubstr$response) );
1339.                 if $response !== "" )
1340.                 {
1341.                     $uids explode' '$response );
1342.                 }
1343.                 for $i 0$i count$messages )$i++ )
1344.                 {
1345.                     $result[trim$messages[$i)$uids[$i];
1346.                 }
1347.             }
1348.             $response trim$this->getResponse$tag ) );
1349.         }
1350.         if $this->responseType$response != self::RESPONSE_OK )
1351.         {
1352.             throw new ezcMailTransportException"The IMAP server could not fetch the unique identifiers: {$response}.);
1353.         }
1354.         return $result;
1355.     }
1356.  
1357.     /**
1358.      * Returns an {@link ezcMailImapSet} with all the messages from the current mailbox.
1359.      *
1360.      * This method supports unique IDs instead of message numbers. See
1361.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1362.      * referencing.
1363.      *
1364.      * If $deleteFromServer is set to true the mail will be marked for deletion
1365.      * after retrieval. If not it will be left intact.
1366.      *
1367.      * The set returned can be parsed with {@link ezcMailParser}.
1368.      *
1369.      * Before calling this method, a connection to the IMAP server must be
1370.      * established and a user must be authenticated successfully, and a mailbox
1371.      * must be selected.
1372.      *
1373.      * Example:
1374.      * <code>
1375.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1376.      * $imap->authenticate( 'username', 'password' );
1377.      * $imap->selectMailbox( 'Inbox' );
1378.      *
1379.      * $set = $imap->fetchAll();
1380.      *
1381.      * // parse $set with ezcMailParser
1382.      * $parser = new ezcMailParser();
1383.      * $mails = $parser->parseMail( $set );
1384.      * foreach ( $mails as $mail )
1385.      * {
1386.      *     // process $mail which is an ezcMail object
1387.      * }
1388.      * </code>
1389.      *
1390.      * @throws ezcMailTransportException
1391.      *          if a mailbox is not selected
1392.      *          or if the server sent a negative response
1393.      * @param bool $deleteFromServer 
1394.      * @return ezcMailParserSet 
1395.      */
1396.     public function fetchAll$deleteFromServer false )
1397.     {
1398.         if $this->options->uidReferencing )
1399.         {
1400.             $messages array_values$this->listUniqueIdentifiers() );
1401.         }
1402.         else
1403.         {
1404.             $messages array_keys$this->listMessages() );
1405.         }
1406.  
1407.         return new ezcMailImapSet$this->connection$messages$deleteFromServerarray'uidReferencing' => $this->options->uidReferencing ) );
1408.     }
1409.  
1410.     /**
1411.      * Returns an {@link ezcMailImapSet} containing only the $number -th message in
1412.      * the current mailbox.
1413.      *
1414.      * This method supports unique IDs instead of message numbers. See
1415.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1416.      * referencing.
1417.      *
1418.      * If $deleteFromServer is set to true the mail will be marked for deletion
1419.      * after retrieval. If not it will be left intact.
1420.      *
1421.      * Note: for IMAP the first message is 1 (so for $number = 0 an exception
1422.      * will be thrown).
1423.      *
1424.      * Before calling this method, a connection to the IMAP server must be
1425.      * established and a user must be authenticated successfully, and a mailbox
1426.      * must be selected.
1427.      *
1428.      * Example:
1429.      * <code>
1430.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1431.      * $imap->authenticate( 'username', 'password' );
1432.      * $imap->selectMailbox( 'Inbox' );
1433.      *
1434.      * $set = $imap->fetchByMessageNr( 1 );
1435.      *
1436.      * // $set can be parsed with ezcMailParser
1437.      * </code>
1438.      *
1439.      * @throws ezcMailTransportException
1440.      *          if a mailbox is not selected
1441.      *          or if the server sent a negative response
1442.      * @throws ezcMailNoSuchMessageException
1443.      *          if the message $number is out of range
1444.      * @param int $number 
1445.      * @param bool $deleteFromServer 
1446.      * @return ezcMailImapSet 
1447.      */
1448.     public function fetchByMessageNr$number$deleteFromServer false )
1449.     {
1450.         if $this->options->uidReferencing )
1451.         {
1452.             $messages array_flip$this->listUniqueIdentifiers() );
1453.         }
1454.         else
1455.         {
1456.             $messages $this->listMessages();
1457.         }
1458.  
1459.         if !isset$messages[$number) )
1460.         {
1461.             throw new ezcMailNoSuchMessageException$number );
1462.         }
1463.  
1464.         return new ezcMailImapSet$this->connectionarray=> $number )$deleteFromServerarray'uidReferencing' => $this->options->uidReferencing ) );
1465.     }
1466.  
1467.     /**
1468.      * Returns an {@link ezcMailImapSet} with $count messages starting from $offset from
1469.      * the current mailbox.
1470.      *
1471.      * This method supports unique IDs instead of message numbers. See
1472.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1473.      * referencing.
1474.      *
1475.      * Fetches $count messages starting from the $offset and returns them as a
1476.      * {@link ezcMailImapSet}. If $count is not specified or if it is 0, it fetches
1477.      * all messages starting from the $offset.
1478.      *
1479.      * Before calling this method, a connection to the IMAP server must be
1480.      * established and a user must be authenticated successfully, and a mailbox
1481.      * must be selected.
1482.      *
1483.      * Example:
1484.      * <code>
1485.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1486.      * $imap->authenticate( 'username', 'password' );
1487.      * $imap->selectMailbox( 'Inbox' );
1488.      *
1489.      * $set = $imap->fetchFromOffset( 1, 10 );
1490.      *
1491.      * // $set can be parsed with ezcMailParser
1492.      * </code>
1493.      *
1494.      * @throws ezcMailTransportException
1495.      *          if a mailbox is not selected
1496.      *          or if the server sent a negative response
1497.      * @throws ezcMailInvalidLimitException
1498.      *          if $count is negative
1499.      * @throws ezcMailOffsetOutOfRangeException
1500.      *          if $offset is outside of the existing range of messages
1501.      * @param int $offset 
1502.      * @param int $count 
1503.      * @param bool $deleteFromServer 
1504.      * @return ezcMailImapSet 
1505.      */
1506.     public function fetchFromOffset$offset$count 0$deleteFromServer false )
1507.     {
1508.         if $count )
1509.         {
1510.             throw new ezcMailInvalidLimitException$offset$count );
1511.         }
1512.  
1513.         if $this->options->uidReferencing )
1514.         {
1515.             $messages array_values$this->listUniqueIdentifiers() );
1516.             $ids array_flip$messages );
1517.  
1518.             if $count === )
1519.             {
1520.                 $count count$messages );
1521.             }
1522.  
1523.             if !isset$ids[$offset) )
1524.             {
1525.                 throw new ezcMailOffsetOutOfRangeException$offset$count );
1526.             }
1527.  
1528.             $range array();
1529.             for $i $ids[$offset]$i min$countcount$messages ) )$i++ )
1530.             {
1531.                 $range[$messages[$i];
1532.             }
1533.         }
1534.         else
1535.         {
1536.             $messages array_keys$this->listMessages() );
1537.  
1538.             if $count === )
1539.             {
1540.                 $count count$messages );
1541.             }
1542.  
1543.             $range array_slice$messages$offset 1$counttrue );
1544.  
1545.             if !isset$range[$offset 1) )
1546.             {
1547.                 throw new ezcMailOffsetOutOfRangeException$offset$count );
1548.             }
1549.         }
1550.  
1551.         return new ezcMailImapSet$this->connection$range$deleteFromServerarray'uidReferencing' => $this->options->uidReferencing ) );
1552.     }
1553.  
1554.     /**
1555.      * Returns an {@link ezcMailImapSet} containing the messages which match the
1556.      * provided $criteria from the current mailbox.
1557.      *
1558.      * This method supports unique IDs instead of message numbers. See
1559.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1560.      * referencing.
1561.      *
1562.      * See {@link http://www.faqs.org/rfcs/rfc1730.html} - 6.4.4. (or
1563.      * {@link http://www.faqs.org/rfcs/rfc1730.html} - 6.4.4.) for criterias
1564.      * which can be used for searching. The criterias can be combined in the
1565.      * same search string (separate the criterias with spaces).
1566.      *
1567.      * If $criteria is null or empty then it will default to 'ALL' (returns all
1568.      * messages in the mailbox).
1569.      *
1570.      * Before calling this method, a connection to the IMAP server must be
1571.      * established and a user must be authenticated successfully, and a mailbox
1572.      * must be selected.
1573.      *
1574.      * Examples:
1575.      * <code>
1576.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1577.      * $imap->authenticate( 'username', 'password' );
1578.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1579.      *
1580.      * // return an ezcMailImapSet containing all messages flagged as 'SEEN'
1581.      * $set = $imap->searchMailbox( 'SEEN' );
1582.      *
1583.      * // return an ezcMailImapSet containing messages with 'release' in their Subject
1584.      * $set = $imap->searchMailbox( 'SUBJECT "release"' );
1585.      *
1586.      * // criterias can be combined:
1587.      * // return an ezcMailImapSet containing messages flagged as 'SEEN' and
1588.      * // with 'release' in their Subject
1589.      * $set = $imap->searchMailbox( 'SEEN SUBJECT "release"' );
1590.      *
1591.      * // $set can be parsed with ezcMailParser
1592.      * </code>
1593.      *
1594.      * @throws ezcMailTransportException
1595.      *          if a mailbox is not selected
1596.      *          or if the server sent a negative response
1597.      * @param string $criteria 
1598.      * @return ezcMailImapSet 
1599.      */
1600.     public function searchMailbox$criteria null )
1601.     {
1602.         $uid $this->options->uidReferencing self::UID self::NO_UID;
1603.  
1604.         if $this->state != self::STATE_SELECTED &&
1605.              $this->state != self::STATE_SELECTED_READONLY )
1606.         {
1607.             throw new ezcMailTransportException"Can't call searchMailbox() on the IMAP transport when a mailbox is not selected." );
1608.         }
1609.  
1610.         $criteria trim$criteria );
1611.         if empty$criteria ) )
1612.         {
1613.             $criteria 'ALL';
1614.         }
1615.  
1616.         $matchingMessages array();
1617.         $tag $this->getNextTag();
1618.         $this->connection->sendData"{$tag} {$uid}SEARCH {$criteria});
1619.  
1620.         $response $this->getResponse'* SEARCH' );
1621.         if strpos$response'* SEARCH' !== false )
1622.         {
1623.             $ids substrtrim$response ));
1624.             if trim$ids !== "" )
1625.             {
1626.                 $matchingMessages explode' '$ids );
1627.             }
1628.         }
1629.  
1630.         $response trim$this->getResponse$tag$response ) );
1631.         if $this->responseType$response != self::RESPONSE_OK )
1632.         {
1633.             throw new ezcMailTransportException"The IMAP server could not search the messages by the specified criteria: {$response}.);
1634.         }
1635.  
1636.         return new ezcMailImapSet$this->connectionarray_values$matchingMessages )falsearray'uidReferencing' => $this->options->uidReferencing ) );
1637.     }
1638.  
1639.     /**
1640.      * Returns an {@link ezcMailImapSet} containing $count messages starting
1641.      * from $offset sorted by $sortCriteria from the current mailbox.
1642.      *
1643.      * This method supports unique IDs instead of message numbers. See
1644.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1645.      * referencing.
1646.      *
1647.      * It is useful for paging through a mailbox.
1648.      *
1649.      * Fetches $count messages starting from the $offset and returns them as a
1650.      * {@link ezcMailImapSet}. If $count is is 0, it fetches all messages
1651.      * starting from the $offset.
1652.      *
1653.      * $sortCriteria is an email header like: Subject, To, From, Date, Sender, etc.
1654.      *
1655.      * Before calling this method, a connection to the IMAP server must be
1656.      * established and a user must be authenticated successfully, and a mailbox
1657.      * must be selected.
1658.      *
1659.      * Example:
1660.      * <code>
1661.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1662.      * $imap->authenticate( 'username', 'password' );
1663.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1664.      *
1665.      * // Fetch a range of messages sorted by Date
1666.      * $set = $imap->sortFromOffset( 1, 10, "Date" );
1667.      *
1668.      * // $set can be parsed with ezcMailParser
1669.      * </code>
1670.      *
1671.      * @throws ezcMailTransportException
1672.      *          if a mailbox is not selected
1673.      *          or if the server sent a negative response
1674.      * @throws ezcMailInvalidLimitException
1675.      *          if $count is negative
1676.      * @throws ezcMailOffsetOutOfRangeException
1677.      *          if $offset is outside of the existing range of messages
1678.      * @param int $offset 
1679.      * @param int $count 
1680.      * @param string $sortCriteria 
1681.      * @param bool $reverse 
1682.      * @return ezcMailImapSet 
1683.      */
1684.     public function sortFromOffset$offset$count 0$sortCriteria$reverse false )
1685.     {
1686.         if $count )
1687.         {
1688.             throw new ezcMailInvalidLimitException$offset$count );
1689.         }
1690.  
1691.         if $this->options->uidReferencing )
1692.         {
1693.             $uids array_values$this->listUniqueIdentifiers() );
1694.  
1695.             $flip array_flip$uids );
1696.             if !isset$flip[$offset) )
1697.             {
1698.                 throw new ezcMailOffsetOutOfRangeException$offset$count );
1699.             }
1700.  
1701.             $start $flip[$offset];
1702.  
1703.             $messages $this->sort$uids$sortCriteria$reverse );
1704.  
1705.             if $count === )
1706.             {
1707.                 $count count$messages );
1708.             }
1709.  
1710.             $ids array_keys$messages );
1711.  
1712.             for $i $start$i $count$i++ )
1713.             {
1714.                 $range[$ids[$i];
1715.             }
1716.         }
1717.         else
1718.         {
1719.             $messageCount $this->countByFlag'ALL' );
1720.             $messages array_keys$this->sortrange1$messageCount )$sortCriteria$reverse ) );
1721.  
1722.             if $count === )
1723.             {
1724.                 $count count$messages );
1725.             }
1726.  
1727.             $range array_slice$messages$offset 1$counttrue );
1728.  
1729.             if !isset$range[$offset 1) )
1730.             {
1731.                 throw new ezcMailOffsetOutOfRangeException$offset$count );
1732.             }
1733.         }
1734.  
1735.         return new ezcMailImapSet$this->connection$rangefalsearray'uidReferencing' => $this->options->uidReferencing ) );
1736.     }
1737.  
1738.     /**
1739.      * Returns an {@link ezcMailImapSet} containing messages $messages sorted by
1740.      * $sortCriteria from the current mailbox.
1741.      *
1742.      * This method supports unique IDs instead of message numbers. See
1743.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1744.      * referencing.
1745.      *
1746.      * $messages is an array of message numbers, for example:
1747.      * <code>
1748.      *   array( 1, 2, 4 );
1749.      * </code>
1750.      *
1751.      * $sortCriteria is an email header like: Subject, To, From, Date, Sender, etc.
1752.      *
1753.      * Before calling this method, a connection to the IMAP server must be
1754.      * established and a user must be authenticated successfully, and a mailbox
1755.      * must be selected.
1756.      *
1757.      * Example:
1758.      * <code>
1759.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1760.      * $imap->authenticate( 'username', 'password' );
1761.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1762.      *
1763.      * // Fetch the list of messages sorted by Date
1764.      * $set = $imap->sortMessages( 1, 10, "Date" );
1765.      *
1766.      * // $set can be parsed with ezcMailParser
1767.      * </code>
1768.      *
1769.      * @throws ezcMailTransportException
1770.      *          if a mailbox is not selected
1771.      *          or if the server sent a negative response
1772.      *          or if array $messages is empty
1773.      * @param array(int) $messages 
1774.      * @param string $sortCriteria 
1775.      * @param bool $reverse 
1776.      * @return ezcMailImapSet 
1777.      */
1778.     public function sortMessages$messages$sortCriteria$reverse false )
1779.     {
1780.         $messages $this->sort$messages$sortCriteria$reverse );
1781.         return new ezcMailImapSet$this->connectionarray_keys $messages )falsearray'uidReferencing' => $this->options->uidReferencing ) );
1782.     }
1783.  
1784.     /**
1785.      * Returns an {@link ezcMailImapSet} containing messages with a certain flag from
1786.      * the current mailbox.
1787.      *
1788.      * This method supports unique IDs instead of message numbers. See
1789.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1790.      * referencing.
1791.      *
1792.      * $flag can be one of:
1793.      *
1794.      * Basic flags:
1795.      *  - ANSWERED   - message has been answered
1796.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
1797.      *  - DRAFT      - message is marked as a draft
1798.      *  - FLAGGED    - message is "flagged" for urgent/special attention
1799.      *  - RECENT     - message is recent
1800.      *  - SEEN       - message has been read
1801.      *
1802.      * Opposites of the above flags:
1803.      *  - UNANSWERED
1804.      *  - UNDELETED
1805.      *  - UNDRAFT
1806.      *  - UNFLAGGED
1807.      *  - OLD
1808.      *  - UNSEEN
1809.      *
1810.      * Composite flags:
1811.      *  - NEW        - equivalent to RECENT + UNSEEN
1812.      *  - ALL        - all the messages
1813.      *
1814.      * Before calling this method, a connection to the IMAP server must be
1815.      * established and a user must be authenticated successfully, and a mailbox
1816.      * must be selected.
1817.      *
1818.      * Example:
1819.      * <code>
1820.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1821.      * $imap->authenticate( 'username', 'password' );
1822.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1823.      *
1824.      * // Fetch the messages marked with the RECENT flag
1825.      * $set = $imap->fetchByFlag( 'RECENT' );
1826.      *
1827.      * // $set can be parsed with ezcMailParser
1828.      * </code>
1829.      *
1830.      * @throws ezcMailTransportException
1831.      *          if a mailbox is not selected
1832.      *          or if the server sent a negative response
1833.      *          or if $flag is not valid
1834.      * @param string $flag 
1835.      * @return ezcMailImapSet 
1836.      */
1837.     public function fetchByFlag$flag )
1838.     {
1839.         $messages $this->searchByFlag$flag );
1840.         return new ezcMailImapSet$this->connection$messagesfalsearray'uidReferencing' => $this->options->uidReferencing ) );
1841.     }
1842.  
1843.     /**
1844.      * Wrapper function to fetch count of messages by a certain flag.
1845.      *
1846.      * $flag can be one of:
1847.      *
1848.      * Basic flags:
1849.      *  - ANSWERED   - message has been answered
1850.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
1851.      *  - DRAFT      - message is marked as a draft
1852.      *  - FLAGGED    - message is "flagged" for urgent/special attention
1853.      *  - RECENT     - message is recent
1854.      *  - SEEN       - message has been read
1855.      *
1856.      * Opposites of the above flags:
1857.      *  - UNANSWERED
1858.      *  - UNDELETED
1859.      *  - UNDRAFT
1860.      *  - UNFLAGGED
1861.      *  - OLD
1862.      *  - UNSEEN
1863.      *
1864.      * Composite flags:
1865.      *  - NEW        - equivalent to RECENT + UNSEEN
1866.      *  - ALL        - all the messages
1867.      *
1868.      * Before calling this method, a connection to the IMAP server must be
1869.      * established and a user must be authenticated successfully, and a mailbox
1870.      * must be selected.
1871.      *
1872.      * @throws ezcMailTransportException
1873.      *          if a mailbox is not selected
1874.      *          or if the server sent a negative response
1875.      *          or if $flag is not valid
1876.      * @param string $flag 
1877.      * @return int 
1878.      */
1879.     public function countByFlag$flag )
1880.     {
1881.         $flag $this->normalizeFlag$flag );
1882.         $messages $this->searchByFlag$flag );
1883.         return count$messages );
1884.     }
1885.  
1886.     /**
1887.      * Fetches IMAP flags for messages $messages.
1888.      *
1889.      * This method supports unique IDs instead of message numbers. See
1890.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1891.      * referencing.
1892.      *
1893.      * $messages is an array of message numbers, for example:
1894.      * <code>
1895.      *   array( 1, 2, 4 );
1896.      * </code>
1897.      *
1898.      * The format of the returned array is:
1899.      * <code>
1900.      *   array( message_number => array( flags ) )
1901.      * </code>
1902.      *
1903.      * Before calling this method, a connection to the IMAP server must be
1904.      * established and a user must be authenticated successfully, and a mailbox
1905.      * must be selected.
1906.      *
1907.      * Example:
1908.      * <code>
1909.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
1910.      * $imap->authenticate( 'username', 'password' );
1911.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
1912.      *
1913.      * $flags = $imap->fetchFlags( array( 1, 2, 4 ) );
1914.      * </code>
1915.      *
1916.      * The returned array $flags will be something like:
1917.      * <code>
1918.      *   array( 1 => array( '\Seen' ),
1919.      *          2 => array( '\Seen' ),
1920.      *          4 => array( '\Seen', 'NonJunk' )
1921.      *        );
1922.      * </code>
1923.      *
1924.      * @throws ezcMailTransportException
1925.      *          if a mailbox is not selected
1926.      *          or if the server sent a negative response
1927.      * @param array $messages 
1928.      * @return array(mixed) 
1929.      */
1930.     public function fetchFlags$messages )
1931.     {
1932.         $uid $this->options->uidReferencing self::UID self::NO_UID;
1933.  
1934.         if $this->state != self::STATE_SELECTED &&
1935.              $this->state != self::STATE_SELECTED_READONLY )
1936.         {
1937.             throw new ezcMailTransportException"Can't call fetchFlags() on the IMAP transport when a mailbox is not selected." );
1938.         }
1939.  
1940.         $flags array();
1941.         $ids implode$messages',' );
1942.  
1943.         $tag $this->getNextTag();
1944.         $this->connection->sendData"{$tag} {$uid}FETCH {$ids} (FLAGS));
1945.  
1946.         $response trim$this->connection->getLine() );
1947.         while strpos$response$tag === false )
1948.         {
1949.             if strpos$response' FETCH (' !== false )
1950.             {
1951.                 if $this->options->uidReferencing )
1952.                 {
1953.                     preg_match'/\*\s.*\sFETCH\s\(FLAGS \((.*)\)\sUID\s(.*)\)/U'$response$matches );
1954.                     $parts explode' '$matches[1);
1955.                     $flags[intval$matches[2)$parts;
1956.                 }
1957.                 else
1958.                 {
1959.                     preg_match'/\*\s(.*)\sFETCH\s\(FLAGS \((.*)\)/U'$response$matches );
1960.                     $parts explode' '$matches[2);
1961.                     $flags[intval$matches[1)$parts;
1962.                 }
1963.             }
1964.             $response trim$this->connection->getLine() );
1965.         }
1966.  
1967.         if $this->responseType$response != self::RESPONSE_OK )
1968.         {
1969.             throw new ezcMailTransportException"The IMAP server could not fetch flags for the messages '{$messages}': {$response}.);
1970.         }
1971.         return $flags;
1972.     }
1973.  
1974.     /**
1975.      * Sets $flag on $messages.
1976.      *
1977.      * This method supports unique IDs instead of message numbers. See
1978.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
1979.      * referencing.
1980.      *
1981.      * $messages can be:
1982.      *  - a single message number (eg. 1)
1983.      *  - a message range (eg. 1:4)
1984.      *  - a message list (eg. 1,2,4)
1985.      *
1986.      * $flag can be one of:
1987.      *  - ANSWERED   - message has been answered
1988.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
1989.      *  - DRAFT      - message is marked as a draft
1990.      *  - FLAGGED    - message is "flagged" for urgent/special attention
1991.      *  - SEEN       - message has been read
1992.      *
1993.      * This function automatically adds the '\' in front of the flag when
1994.      * calling the server command.
1995.      *
1996.      * Before calling this method, a connection to the IMAP server must be
1997.      * established and a user must be authenticated successfully, and a mailbox
1998.      * must be selected.
1999.      *
2000.      * Example:
2001.      * <code>
2002.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
2003.      * $imap->authenticate( 'username', 'password' );
2004.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
2005.      *
2006.      * $imap->setFlag( '1:4', 'DRAFT' );
2007.      * </code>
2008.      *
2009.      * @throws ezcMailTransportException
2010.      *          if a mailbox is not selected
2011.      *          or if the server sent a negative response
2012.      *          or if $flag is not valid
2013.      * @param string $messages 
2014.      * @param string $flag 
2015.      * @return bool 
2016.      */
2017.     public function setFlag$messages$flag )
2018.     {
2019.         $uid $this->options->uidReferencing self::UID self::NO_UID;
2020.  
2021.         if $this->state != self::STATE_SELECTED )
2022.         {
2023.             throw new ezcMailTransportException"Can't call setFlag() when a mailbox is not selected." );
2024.         }
2025.  
2026.         $flag $this->normalizeFlag$flag );
2027.         if in_array$flagself::$basicFlags ) )
2028.         {
2029.             $tag $this->getNextTag();
2030.             $this->connection->sendData"{$tag} {$uid}STORE {$messages} +FLAGS (\\{$flag}));
2031.             $response trim$this->getResponse$tag ) );
2032.             if $this->responseType$response != self::RESPONSE_OK )
2033.             {
2034.                 throw new ezcMailTransportException"The IMAP server could not set flag '{$flag}on the messages '{$messages}': {$response}.);
2035.             }
2036.         }
2037.         else
2038.         {
2039.             throw new ezcMailTransportException"Flag '{$flag}is not allowed for setting.);
2040.         }
2041.         return true;
2042.     }
2043.  
2044.     /**
2045.      * Clears $flag from $messages.
2046.      *
2047.      * This method supports unique IDs instead of message numbers. See
2048.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
2049.      * referencing.
2050.      *
2051.      * $messages can be:
2052.      *  - a single message number (eg. '1')
2053.      *  - a message range (eg. '1:4')
2054.      *  - a message list (eg. '1,2,4')
2055.      *
2056.      * $flag can be one of:
2057.      *  - ANSWERED   - message has been answered
2058.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
2059.      *  - DRAFT      - message is marked as a draft
2060.      *  - FLAGGED    - message is "flagged" for urgent/special attention
2061.      *  - SEEN       - message has been read
2062.      *
2063.      * This function automatically adds the '\' in front of the flag when
2064.      * calling the server command.
2065.      *
2066.      * Before calling this method, a connection to the IMAP server must be
2067.      * established and a user must be authenticated successfully, and a mailbox
2068.      * must be selected.
2069.      *
2070.      * Example:
2071.      * <code>
2072.      * $imap = new ezcMailImapTransport( 'imap.example.com' );
2073.      * $imap->authenticate( 'username', 'password' );
2074.      * $imap->selectMailbox( 'mailbox' ); // Inbox or another mailbox
2075.      *
2076.      * $imap->clearFlag( '1:4', 'DRAFT' );
2077.      * </code>
2078.      *
2079.      * @throws ezcMailTransportException
2080.      *          if a mailbox is not selected
2081.      *          or if the server sent a negative response
2082.      *          or if $flag is not valid
2083.      * @param string $messages 
2084.      * @param string $flag 
2085.      * @return bool 
2086.      */
2087.     public function clearFlag$messages$flag )
2088.     {
2089.         $uid $this->options->uidReferencing self::UID self::NO_UID;
2090.  
2091.         if $this->state != self::STATE_SELECTED )
2092.         {
2093.             throw new ezcMailTransportException"Can't call clearFlag() when a mailbox is not selected." );
2094.         }
2095.  
2096.         $flag $this->normalizeFlag$flag );
2097.         if in_array$flagself::$basicFlags ) )
2098.         {
2099.             $tag $this->getNextTag();
2100.             $this->connection->sendData"{$tag} {$uid}STORE {$messages} -FLAGS (\\{$flag}));
2101.             $response trim$this->getResponse$tag ) );
2102.             if $this->responseType$response != self::RESPONSE_OK )
2103.             {
2104.                 throw new ezcMailTransportException"The IMAP server could not clear flag '{$flag}on the messages '{$messages}': {$response}.);
2105.             }
2106.         }
2107.         else
2108.         {
2109.             throw new ezcMailTransportException"Flag '{$flag}is not allowed for clearing.);
2110.         }
2111.         return true;
2112.     }
2113.  
2114.     /**
2115.      * Returns an array of message numbers from the selected mailbox which have a
2116.      * certain flag set.
2117.      *
2118.      * This method supports unique IDs instead of message numbers. See
2119.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
2120.      * referencing.
2121.      *
2122.      * $flag can be one of:
2123.      *
2124.      * Basic flags:
2125.      *  - ANSWERED   - message has been answered
2126.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
2127.      *  - DRAFT      - message is marked as a draft
2128.      *  - FLAGGED    - message is "flagged" for urgent/special attention
2129.      *  - RECENT     - message is recent
2130.      *  - SEEN       - message has been read
2131.      *
2132.      * Opposites of the above flags:
2133.      *  - UNANSWERED
2134.      *  - UNDELETED
2135.      *  - UNDRAFT
2136.      *  - UNFLAGGED
2137.      *  - OLD
2138.      *  - UNSEEN
2139.      *
2140.      * Composite flags:
2141.      *  - NEW        - equivalent to RECENT + UNSEEN
2142.      *  - ALL        - all the messages
2143.      *
2144.      * The returned array is something like this:
2145.      * <code>
2146.      *   array( 0 => 1, 1 => 5 );
2147.      * </code>
2148.      *
2149.      * Before calling this method, a connection to the IMAP server must be
2150.      * established and a user must be authenticated successfully, and a mailbox
2151.      * must be selected.
2152.      *
2153.      * @throws ezcMailTransportException
2154.      *          if a mailbox is not selected
2155.      *          or if the server sent a negative response
2156.      *          or if $flag is not valid
2157.      * @param string $flag 
2158.      * @return array(int) 
2159.      */
2160.     protected function searchByFlag$flag )
2161.     {
2162.         $uid $this->options->uidReferencing self::UID self::NO_UID;
2163.  
2164.         if $this->state != self::STATE_SELECTED &&
2165.              $this->state != self::STATE_SELECTED_READONLY )
2166.         {
2167.             throw new ezcMailTransportException"Can't call searchByFlag() on the IMAP transport when a mailbox is not selected." );
2168.         }
2169.  
2170.         $matchingMessages array();
2171.         $flag $this->normalizeFlag$flag );
2172.         if in_array$flagself::$extendedFlags ) )
2173.         {
2174.             $tag $this->getNextTag();
2175.             $this->connection->sendData"{$tag} {$uid}SEARCH ({$flag}));
2176.             $response $this->getResponse'* SEARCH' );
2177.  
2178.             if strpos$response'* SEARCH' !== false )
2179.             {
2180.                 $ids substrtrim$response ));
2181.                 if trim$ids !== "" )
2182.                 {
2183.                     $matchingMessages explode' '$ids );
2184.                 }
2185.             }
2186.             $response trim$this->getResponse$tag$response ) );
2187.             if $this->responseType$response != self::RESPONSE_OK )
2188.             {
2189.                 throw new ezcMailTransportException"The IMAP server could not search the messages by flags: {$response}.);
2190.             }
2191.         }
2192.         else
2193.         {
2194.             throw new ezcMailTransportException"Flag '{$flag}is not allowed for searching.);
2195.         }
2196.         return $matchingMessages;
2197.     }
2198.  
2199.     /**
2200.      * Sends a NOOP command to the server, use it to keep the connection alive.
2201.      *
2202.      * Before calling this method, a connection to the IMAP server must be
2203.      * established.
2204.      *
2205.      * @throws ezcMailTransportException
2206.      *          if there was no connection to the server
2207.      *          or if the server sent a negative response
2208.      */
2209.     public function noop()
2210.     {
2211.         if $this->state != self::STATE_NOT_AUTHENTICATED &&
2212.              $this->state != self::STATE_AUTHENTICATED &&
2213.              $this->state != self::STATE_SELECTED &&
2214.              $this->state != self::STATE_SELECTED_READONLY )
2215.         {
2216.             throw new ezcMailTransportException"Can not issue NOOP command if not connected." );
2217.         }
2218.  
2219.         $tag $this->getNextTag();
2220.         $this->connection->sendData"{$tag} NOOP);
2221.         $response trim$this->getResponse$tag ) );
2222.         if $this->responseType$response != self::RESPONSE_OK )
2223.         {
2224.             throw new ezcMailTransportException"NOOP failed: {$response}.);
2225.         }
2226.     }
2227.  
2228.     /**
2229.      * Returns an array with the capabilities of the IMAP server.
2230.      *
2231.      * The returned array will be something like this:
2232.      * <code>
2233.      *   array( 'IMAP4rev1', 'SASL-IR SORT', 'THREAD=REFERENCES', 'MULTIAPPEND',
2234.      *          'UNSELECT', 'LITERAL+', 'IDLE', 'CHILDREN', 'NAMESPACE',
2235.      *          'LOGIN-REFERRALS'
2236.      *        );
2237.      * </code>
2238.      *
2239.      * Before calling this method, a connection to the IMAP server must be
2240.      * established.
2241.      *
2242.      * @throws ezcMailTransportException
2243.      *          if there was no connection to the server
2244.      *          or if the server sent a negative response
2245.      * @return array(string) 
2246.      */
2247.     public function capability()
2248.     {
2249.         if $this->state != self::STATE_NOT_AUTHENTICATED &&
2250.              $this->state != self::STATE_AUTHENTICATED &&
2251.              $this->state != self::STATE_SELECTED &&
2252.              $this->state != self::STATE_SELECTED_READONLY )
2253.         {
2254.             throw new ezcMailTransportException"Trying to request capability when not connected to server." );
2255.         }
2256.  
2257.         $tag $this->getNextTag();
2258.         $this->connection->sendData"{$tag} CAPABILITY);
2259.  
2260.         $response $this->connection->getLine();
2261.         while $this->responseType$response != self::RESPONSE_UNTAGGED &&
2262.                 strpos$response'* CAPABILITY ' === false )
2263.         {
2264.             $response $this->connection->getLine();
2265.         }
2266.         $result trim$response );
2267.  
2268.         $response trim$this->getResponse$tag ) );
2269.         if $this->responseType$response != self::RESPONSE_OK )
2270.         {
2271.             throw new ezcMailTransportException"The IMAP server responded negative to the CAPABILITY command: {$response}.);
2272.         }
2273.  
2274.         return explode' 'str_replace'* CAPABILITY '''$result ) );
2275.     }
2276.  
2277.     /**
2278.      * Sends an EXPUNGE command to the server.
2279.      *
2280.      * This method permanently deletes the messages marked for deletion by
2281.      * the method {@link delete()}.
2282.      *
2283.      * Before calling this method, a connection to the IMAP server must be
2284.      * established and a user must be authenticated successfully, and a mailbox
2285.      * must be selected.
2286.      *
2287.      * @throws ezcMailTransportException
2288.      *          if a mailbox was not selected
2289.      *          or if the server sent a negative response
2290.      */
2291.     public function expunge()
2292.     {
2293.         if $this->state != self::STATE_SELECTED )
2294.         {
2295.             throw new ezcMailTransportException"Can not issue EXPUNGE command if a mailbox is not selected." );
2296.         }
2297.  
2298.         $tag $this->getNextTag();
2299.         $this->connection->sendData"{$tag} EXPUNGE);
2300.         $response trim$this->getResponse$tag ) );
2301.         if $this->responseType$response != self::RESPONSE_OK )
2302.         {
2303.             throw new ezcMailTransportException"EXPUNGE failed: {$response}.);
2304.         }
2305.     }
2306.  
2307.     /**
2308.      * Appends $mail to the $mailbox mailbox.
2309.      *
2310.      * Use this method to create email messages in a mailbox such as Sent or
2311.      * Draft.
2312.      *
2313.      * $flags is an array of flags to be set to the $mail (if provided):
2314.      *
2315.      * $flag can be one of:
2316.      *  - ANSWERED   - message has been answered
2317.      *  - DELETED    - message is marked to be deleted by later EXPUNGE
2318.      *  - DRAFT      - message is marked as a draft
2319.      *  - FLAGGED    - message is "flagged" for urgent/special attention
2320.      *  - SEEN       - message has been read
2321.      *
2322.      * This function automatically adds the '\' in front of each flag when
2323.      * calling the server command.
2324.      *
2325.      * Before calling this method, a connection to the IMAP server must be
2326.      * established and a user must be authenticated successfully.
2327.      *
2328.      * @throws ezcMailTransportException
2329.      *          if user is not authenticated
2330.      *          or if the server sent a negative response
2331.      *          or if $mailbox does not exists
2332.      * @param string $mailbox 
2333.      * @param string $mail 
2334.      * @param array(string) $flags 
2335.      */
2336.     public function append$mailbox$mail$flags null )
2337.     {
2338.         if $this->state != self::STATE_AUTHENTICATED &&
2339.              $this->state != self::STATE_SELECTED &&
2340.              $this->state != self::STATE_SELECTED_READONLY )
2341.         {
2342.             throw new ezcMailTransportException"Can't call append() if not authenticated." );
2343.         }
2344.  
2345.         $tag $this->getNextTag();
2346.         $mailSize strlen$mail );
2347.         if !is_null$flags ) )
2348.         {
2349.             for $i 0$i count$flags )$i++ )
2350.             {
2351.                 $flags[$i'\\' $this->normalizeFlag$flags[$i);
2352.             }
2353.             $flagList implode' '$flags );
2354.             $command "{$tag} APPEND {$mailbox} ({$flagList}{{$mailSize}}";
2355.         }
2356.         else
2357.         {
2358.             $command "{$tag} APPEND {$mailbox} {{$mailSize}}";
2359.         }
2360.  
2361.         $this->connection->sendData$command );
2362.         $response trim$this->connection->getLine() );
2363.  
2364.         if strpos$response'TRYCREATE' !== false )
2365.         {
2366.             throw new ezcMailTransportException"Mailbox does not exist: {$response}.);
2367.         }
2368.  
2369.         if $this->responseType$response == self::RESPONSE_FEEDBACK )
2370.         {
2371.             $this->connection->sendData$mail );
2372.             $response trim$this->getResponse$tag ) );
2373.             if $this->responseType$response != self::RESPONSE_OK )
2374.             {
2375.                 throw new ezcMailTransportException"The IMAP server could not append message to mailbox '{$mailbox}': {$response}.);
2376.             }
2377.         }
2378.         elseif $this->responseType$response != self::RESPONSE_OK )
2379.         {
2380.             throw new ezcMailTransportException"The IMAP server could not append message to mailbox '{$mailbox}': {$response}.);
2381.         }
2382.     }
2383.  
2384.     /**
2385.      * Clears $flag of unwanted characters and makes it uppercase.
2386.      *
2387.      * @param string $flag 
2388.      * @return string 
2389.      */ 
2390.     protected function normalizeFlag$flag )
2391.     {
2392.         $flag strtoupper$flag );
2393.         $flag str_replace'\\'''$flag );
2394.         return trim$flag );
2395.     }
2396.  
2397.     /**
2398.      * Sorts message numbers array $messages by the specified $sortCriteria.
2399.      *
2400.      * This method supports unique IDs instead of message numbers. See
2401.      * {@link ezcMailImapTransportOptions} for how to enable unique IDs
2402.      * referencing.
2403.      *
2404.      * $messages is an array of message numbers, for example:
2405.      * <code>
2406.      *   array( 1, 2, 4 );
2407.      * </code>
2408.      *
2409.      * $sortCriteria is an email header like: Subject, To, From, Date, Sender.
2410.      *
2411.      * The sorting is done with the php function natcasesort().
2412.      *
2413.      * Before calling this method, a connection to the IMAP server must be
2414.      * established and a user must be authenticated successfully, and a mailbox
2415.      * must be selected.
2416.      *
2417.      * @throws ezcMailTransportException
2418.      *          if a mailbox is not selected
2419.      *          or if the server sent a negative response
2420.      *          or if the array $messages is empty
2421.      * @param array(int) $messages 
2422.      * @param string $sortCriteria 
2423.      * @param bool $reverse 
2424.      * @return array(string) 
2425.      */
2426.     protected function sort$messages$sortCriteria$reverse false )
2427.     {
2428.         $uid $this->options->uidReferencing self::UID self::NO_UID;
2429.  
2430.         if $this->state != self::STATE_SELECTED &&
2431.              $this->state != self::STATE_SELECTED_READONLY )
2432.         {
2433.             throw new ezcMailTransportException"Can't call sort() on the IMAP transport when a mailbox is not selected." );
2434.         }
2435.  
2436.         $result array();
2437.         $query ucfirststrtolower$sortCriteria ) );
2438.         $messageNumbers implode','$messages );
2439.  
2440.         $tag $this->getNextTag();
2441.         $this->connection->sendData"{$tag} {$uid}FETCH {$messageNumbers} (BODY.PEEK[HEADER.FIELDS ({$query})]));
2442.  
2443.         $response trim$this->connection->getLine() );
2444.         while strpos$response$tag === false )
2445.         {
2446.             if strpos$response' FETCH (' !== false )
2447.             {
2448.                 if $this->options->uidReferencing )
2449.                 {
2450.                     preg_match('/^\* [0-9]+ FETCH \(UID ([0-9]+)/'$response$matches );
2451.                 }
2452.                 else
2453.                 {
2454.                     preg_match('/^\* ([0-9]+) FETCH/'$response$matches );
2455.                 }
2456.                 $messageNumber $matches[1];
2457.             }
2458.  
2459.             if strpos$response$query !== false )
2460.             {
2461.                 $strippedResponse trimtrimstr_replace"{$query}"''$response ) )'"' );
2462.                 switch $query )
2463.                 {
2464.                     case 'Date':
2465.                         $strippedResponse strtotime$strippedResponse );
2466.                         break;
2467.                     case 'Subject':
2468.                     case 'From':
2469.                     case 'Sender':
2470.                     case 'To':
2471.                         $strippedResponse ezcMailTools::mimeDecode$strippedResponse );
2472.                         break;
2473.                     default:
2474.                         break;
2475.                 }
2476.                 $result[$messageNumber$strippedResponse;
2477.             }
2478.  
2479.             // in case the mail doesn't have the $sortCriteria header (like junk mail missing Subject header)
2480.             if strpos$response')' !== false && !isset$result[$messageNumber) )
2481.             {
2482.                 $result[$messageNumber'';
2483.             }
2484.  
2485.             $response trim$this->connection->getLine() );
2486.         }
2487.  
2488.         if $this->responseType$response != self::RESPONSE_OK )
2489.         {
2490.             throw new ezcMailTransportException"The IMAP server could not sort the messages: {$response}.);
2491.         }
2492.  
2493.         if $reverse === true )
2494.         {
2495.             natcasesort$result );
2496.             $result array_reverse$resulttrue );
2497.         }
2498.         else
2499.         {
2500.             natcasesort$result );
2501.         }
2502.         return $result;
2503.     }
2504.  
2505.     /**
2506.      * Parses $line to return the response code.
2507.      *
2508.      * Returns one of the following:
2509.      *  - {@link RESPONSE_OK}
2510.      *  - {@link RESPONSE_NO}
2511.      *  - {@link RESPONSE_BAD}
2512.      *  - {@link RESPONSE_UNTAGGED}
2513.      *  - {@link RESPONSE_FEEDBACK}
2514.      *
2515.      * @throws ezcMailTransportException
2516.      *          if the IMAP response ($line) is not recognized
2517.      * @param string $line 
2518.      * @return int 
2519.      */
2520.     protected function responseType$line )
2521.     {
2522.         if strpos$line'OK ' !== false && strpos$line'OK ' == )
2523.         {
2524.             return self::RESPONSE_OK;
2525.         }
2526.         if strpos$line'NO ' !== false && strpos$line'NO ' == )
2527.         {
2528.             return self::RESPONSE_NO;
2529.         }
2530.         if strpos$line'BAD ' !== false && strpos$line'BAD ' == )
2531.         {
2532.             return self::RESPONSE_BAD;
2533.         }
2534.         if strpos$line'* ' !== false && strpos$line'* ' == )
2535.         {
2536.             return self::RESPONSE_UNTAGGED;
2537.         }
2538.         if strpos$line'+ ' !== false && strpos$line'+ ' == )
2539.         {
2540.             return self::RESPONSE_FEEDBACK;
2541.         }
2542.         throw new ezcMailTransportException"Unrecognized IMAP response in line: {$line});
2543.     }
2544.  
2545.     /**
2546.      * Reads the responses from the server until encountering $tag.
2547.      *
2548.      * In IMAP, each command sent by the client is prepended with a
2549.      * alphanumeric tag like 'A1234'. The server sends the response
2550.      * to the client command as lines, and the last line in the response
2551.      * is prepended with the same tag, and it contains the status of
2552.      * the command completion ('OK', 'NO' or 'BAD').
2553.      *
2554.      * Sometimes the server sends alerts and response lines from other
2555.      * commands before sending the tagged line, so this method just
2556.      * reads all the responses until it encounters $tag.
2557.      *
2558.      * It returns the tagged line to be processed by the calling method.
2559.      *
2560.      * If $response is specified, then it will not read the response
2561.      * from the server before searching for $tag in $response.
2562.      *
2563.      * Before calling this method, a connection to the IMAP server must be
2564.      * established.
2565.      *
2566.      * @param string $tag 
2567.      * @param string $response 
2568.      * @return string 
2569.      */
2570.     protected function getResponse$tag$response null )
2571.     {
2572.         if is_null$response ) )
2573.         {
2574.             $response $this->connection->getLine();
2575.         }
2576.         while strpos$response$tag === false )
2577.         {
2578.             if strpos$response' BAD ' !== false ||
2579.                  strpos$response' NO ' !== false )
2580.             {
2581.                 break;
2582.             }
2583.             $response $this->connection->getLine();
2584.         }
2585.         return $response;
2586.     }
2587.  
2588.     /**
2589.      * Generates the next IMAP tag to prepend to client commands.
2590.      *
2591.      * The structure of the IMAP tag is Axxxx, where:
2592.      *  - A is a letter (uppercase for conformity)
2593.      *  - x is a digit from 0 to 9
2594.      *
2595.      * example of generated tag: T5439
2596.      *
2597.      * It uses the class variable $this->currentTag.
2598.      *
2599.      * Everytime it is called, the tag increases by 1.
2600.      *
2601.      * If it reaches the last tag, it wraps around to the first tag.
2602.      *
2603.      * By default, the first generated tag is A0001.
2604.      *
2605.      * @return string 
2606.      */
2607.     protected function getNextTag()
2608.     {
2609.         $tagLetter substr$this->currentTag0);
2610.         $tagNumber intvalsubstr$this->currentTag) );
2611.         $tagNumber++;
2612.         if $tagLetter == 'Z' && $tagNumber == 10000 )
2613.         {
2614.             $tagLetter 'A';
2615.             $tagNumber 1;
2616.         }
2617.         if $tagNumber == 10000 )
2618.         {
2619.             $tagLetter++;
2620.             $tagNumber 0;
2621.         }
2622.         $this->currentTag = $tagLetter sprintf"%04s"$tagNumber );
2623.         return $this->currentTag;
2624.     }
2625. }
2626. ?>
Last updated: Mon, 17 Dec 2007