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

Reply via email to