mixed) $options */ public function __construct( ezcMailTransportConnection $connection, array $messages, $deleteFromServer = false, array $options = array() ) { $this->connection = $connection; $this->messages = $messages; $this->deleteFromServer = $deleteFromServer; $this->nextData = null; $this->options = new ezcMailImapSetOptions( $options ); $this->uid = ( $this->options->uidReferencing ) ? ezcMailImapTransport::UID : ezcMailImapTransport::NO_UID; } /** * Returns true if all the data has been fetched from this set. * * @return bool */ public function isFinished() { return $this->currentMessage === false ? true : false; } /** * Returns one line of data from the current mail in the set. * * Null is returned if there is no current mail in the set or * the end of the mail is reached, * * @return string */ public function getNextLine() { if ( $this->currentMessage === null ) { // instead of calling $this->nextMail() in the constructor, it is called // here, to avoid sending commands to the server when creating the set, and // instead send the server commands when parsing the set (see ezcMailParser). $this->nextMail(); } if ( $this->hasMoreMailData ) { $data = ( $this->nextData === null ) ? $this->connection->getLine() : $this->nextData; if ( strpos( $data, $this->currentTag ) === false ) { $this->nextData = $this->connection->getLine(); // the next code checks if the current line ends with ')' // and the next line has the command tag (e.g. 'A0034'). if ( substr( trim( $data ), strlen( trim( $data ) ) - 1 ) === ')' && strpos( $this->nextData, $this->currentTag ) === 0 ) { $this->hasMoreMailData = false; // remove the mail if required by the user. if ( $this->deleteFromServer === true ) { $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} {$this->uid}STORE {$this->currentMessage} +FLAGS (\\Deleted)" ); // skip OK response ("{$tag} OK Store completed.") $response = $this->getResponse( $tag ); } return null; } } return $data; } return null; } /** * Moves the set to the next mail and returns true upon success. * * False is returned if there are no more mail in the set. * * @throws ezcMailTransportException * if the server sent a negative response * @return bool */ public function nextMail() { if ( $this->currentMessage === null ) { $this->currentMessage = reset( $this->messages ); } else { $this->currentMessage = next( $this->messages ); } $this->nextData = null; if ( $this->currentMessage !== false ) { $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} {$this->uid}FETCH {$this->currentMessage} RFC822" ); $response = $this->connection->getLine(); if ( strpos( $response, ' NO ' ) !== false || strpos( $response, ' BAD ') !== false ) { throw new ezcMailTransportException( "The IMAP server sent a negative reply when requesting mail." ); } else { $response = $this->getResponse( 'FETCH (', $response ); if ( strpos( $response, 'FETCH (' ) !== false ) { $this->hasMoreMailData = true; return true; } else { $response = $this->getResponse( $tag ); if ( strpos( $response, 'OK ' ) === false ) { throw new ezcMailTransportException( "The IMAP server sent a negative reply when requesting mail." ); } } } } return false; } /** * Reads the responses from the server until encountering $tag. * * In IMAP, each command sent by the client is prepended with a * alphanumeric tag like 'A1234'. The server sends the response * to the client command as lines, and the last line in the response * is prepended with the same tag, and it contains the status of * the command completion ('OK', 'NO' or 'BAD'). * * Sometimes the server sends alerts and response lines from other * commands before sending the tagged line, so this method just * reads all the responses until it encounters $tag. * * It returns the tagged line to be processed by the calling method. * * If $response is specified, then it will not read the response * from the server before searching for $tag in $response. * * Before calling this method, a connection to the IMAP server must be * established. * * @param string $tag * @param string $response * @return string */ private function getResponse( $tag = null, $response = null ) { if ( is_null( $response ) ) { $response = $this->connection->getLine(); } while ( strpos( $response, $tag ) === false ) { if ( strpos( $response, ' BAD ' ) !== false || strpos( $response, ' NO ' ) !== false ) { break; } $response = $this->connection->getLine(); } return $response; } /** * Generates the next IMAP tag to prepend to client commands. * * The structure of the IMAP tag is Axxxx, where: * - A is a letter (uppercase for conformity) * - x is a digit from 0 to 9 * * example of generated tag: T5439 * * It uses the class variable $this->currentTag. * * Everytime it is called, the tag increases by 1. * * If it reaches the last tag, it wraps around to the first tag. * * By default, the first generated tag is A0001. * * @return string */ private function getNextTag() { $tagLetter = substr( $this->currentTag, 0, 1 ); $tagNumber = intval( substr( $this->currentTag, 1 ) ); $tagNumber++; if ( $tagLetter == 'Z' && $tagNumber == 10000 ) { $tagLetter = 'A'; $tagNumber = 1; } if ( $tagNumber == 10000 ) { $tagLetter++; $tagNumber = 0; } $this->currentTag = $tagLetter . sprintf( "%04s", $tagNumber ); return $this->currentTag; } /** * Returns whether the set has mails. * * @return bool */ public function hasData() { return count( $this->messages ); } /** * Returns message numbers from the current set. * * @return array(int) */ public function getMessageNumbers() { return $this->messages; } } ?>