Author: dr Date: Fri Nov 30 14:30:57 2007 New Revision: 6894 Log: - Fixed issue #12138: Mail's IMAP transport can hang when connection gets dropped.
Modified: trunk/Mail/ChangeLog trunk/Mail/src/transports/imap/imap_transport.php trunk/Mail/src/transports/transport_connection.php Modified: trunk/Mail/ChangeLog ============================================================================== --- trunk/Mail/ChangeLog [iso-8859-1] (original) +++ trunk/Mail/ChangeLog [iso-8859-1] Fri Nov 30 14:30:57 2007 @@ -1,3 +1,10 @@ +1.4rc1 - [RELEASEDATE] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Fixed issue #12138: Mail's IMAP transport can hang when connection gets + dropped. + + 1.4beta1 - Wednesday 28 November 2007 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modified: trunk/Mail/src/transports/imap/imap_transport.php ============================================================================== --- trunk/Mail/src/transports/imap/imap_transport.php [iso-8859-1] (original) +++ trunk/Mail/src/transports/imap/imap_transport.php [iso-8859-1] Fri Nov 30 14:30:57 2007 @@ -357,7 +357,7 @@ } $this->connection = new ezcMailTransportConnection( $server, $port, $this->options ); // get the server greeting - $response = $this->connection->getLine(); + $response = $this->getLine(); if ( strpos( $response, "* OK" ) === false ) { throw new ezcMailTransportException( "The connection to the IMAP server is ok, but a negative response from server was received. Try again later." ); @@ -497,7 +497,7 @@ $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} LOGIN {$user} {$password}" ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); if ( strpos( $response, '* OK' ) !== false ) { // the server is busy waiting for authentication process to @@ -566,7 +566,7 @@ $result = array(); $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} LIST \"{$reference}\" \"{$mailbox}\"" ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); while ( strpos( $response, '* LIST (' ) !== false ) { // only consider the selectable mailboxes @@ -578,7 +578,7 @@ $result[] = $response; } - $response = $this->connection->getLine(); + $response = $this->getLine(); } $response = $this->getResponse( $tag, $response ); @@ -970,7 +970,7 @@ $line = substr( $line, 0, strlen( $line ) - 1 ); $messages[$currentMessage] = intval( $line ); $currentMessage = next( $messageList ); - $response = $this->connection->getLine(); + $response = $this->getLine(); } // skip the OK response ("{$tag} OK Fetch completed.") $response = trim( $this->getResponse( $tag, $response ) ); @@ -1042,7 +1042,7 @@ $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} {$uid}FETCH {$ids} (RFC822.SIZE)" ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); while ( strpos( $response, $tag ) === false ) { if ( strpos( $response, ' FETCH (' ) !== false ) @@ -1059,7 +1059,7 @@ } } - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); } if ( $this->responseType( $response ) != self::RESPONSE_OK ) @@ -1237,7 +1237,7 @@ if ( $this->options->uidReferencing ) { // special case (BUG?) where "UID FETCH {$msgNum}" returns nothing - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); if ( $this->responseType( $response ) === self::RESPONSE_OK ) { throw new ezcMailTransportException( "The IMAP server could not fetch the message '{$msgNum}': {$response}." ); @@ -1254,14 +1254,14 @@ while ( strpos( $response, 'BODY[TEXT]' ) === false ) { $message .= $response; - $response = $this->connection->getLine(); - } - - $response = $this->connection->getLine(); + $response = $this->getLine(); + } + + $response = $this->getLine(); while ( strpos( $response, $tag ) === false ) { $message .= $response; - $response = $this->connection->getLine(); + $response = $this->getLine(); } } // skip the OK response ("{$tag} OK Fetch completed.") @@ -1942,7 +1942,7 @@ $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} {$uid}FETCH {$ids} (FLAGS)" ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); while ( strpos( $response, $tag ) === false ) { if ( strpos( $response, ' FETCH (' ) !== false ) @@ -1960,7 +1960,7 @@ $flags[intval( $matches[1] )] = $parts; } } - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); } if ( $this->responseType( $response ) != self::RESPONSE_OK ) @@ -2256,11 +2256,11 @@ $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} CAPABILITY" ); - $response = $this->connection->getLine(); + $response = $this->getLine(); while ( $this->responseType( $response ) != self::RESPONSE_UNTAGGED && strpos( $response, '* CAPABILITY ' ) === false ) { - $response = $this->connection->getLine(); + $response = $this->getLine(); } $result = trim( $response ); @@ -2358,7 +2358,7 @@ } $this->connection->sendData( $command ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); if ( strpos( $response, 'TRYCREATE' ) !== false ) { @@ -2439,7 +2439,7 @@ $tag = $this->getNextTag(); $this->connection->sendData( "{$tag} {$uid}FETCH {$messageNumbers} (BODY.PEEK[HEADER.FIELDS ({$query})])" ); - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); while ( strpos( $response, $tag ) === false ) { if ( strpos( $response, ' FETCH (' ) !== false ) @@ -2481,7 +2481,7 @@ $result[$messageNumber] = ''; } - $response = trim( $this->connection->getLine() ); + $response = trim( $this->getLine() ); } if ( $this->responseType( $response ) != self::RESPONSE_OK ) @@ -2499,6 +2499,32 @@ natcasesort( $result ); } return $result; + } + + /** + * Returns one line of data from the stream. + * + * The returned line will have linebreaks removed if the $trim option is + * set, internally it calls the transport's getLine(), but with a wrapper + * to account for broken connections. + * + * @throws ezcMailTransportConnection + * if there is no valid connection + * @param bool $trim + * @return string + */ + protected function getLine() + { + try + { + return $this->connection->getLine(); + } + catch ( ezcMailTransportException $e ) + { + /* Catch the exception, set the state and rethrow */ + $this->state = self::STATE_NOT_CONNECTED; + throw $e; + } } /** @@ -2568,18 +2594,26 @@ */ protected function getResponse( $tag, $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(); + try + { + if ( is_null( $response ) ) + { + $response = $this->getLine(); + } + while ( strpos( $response, $tag ) === false ) + { + if ( strpos( $response, ' BAD ' ) !== false || + strpos( $response, ' NO ' ) !== false ) + { + break; + } + $response = $this->getLine(); + } + } + catch ( ezcMailTransportException $e ) + { + $this->state = self::STATE_NOT_CONNECTED; + throw $e; } return $response; } Modified: trunk/Mail/src/transports/transport_connection.php ============================================================================== --- trunk/Mail/src/transports/transport_connection.php [iso-8859-1] (original) +++ trunk/Mail/src/transports/transport_connection.php [iso-8859-1] Fri Nov 30 14:30:57 2007 @@ -204,10 +204,18 @@ if ( is_resource( $this->connection ) ) { // in case there is a problem with the connection fgets() returns false - while ( $line !== false - && strpos( $line, self::CRLF ) === false ) + while ( strpos( $line, self::CRLF ) === false ) { $line = fgets( $this->connection, 512 ); + + /* If the mail server aborts the connection, fgets() will + * return false. We need to throw an exception here to prevent + * the calling code from looping indefinitely. */ + if ( $line === false ) + { + throw new ezcMailTransportException( 'Could not read from the stream. It was probably terminated by the host.' ); + } + $data .= $line; } -- svn-components mailing list svn-components@lists.ez.no http://lists.ez.no/mailman/listinfo/svn-components