Index: include/imapproxy.h
===================================================================
--- include/imapproxy.h	(revision 14096)
+++ include/imapproxy.h	(working copy)
@@ -300,6 +300,7 @@
     unsigned char support_starttls;           /* starttls support flag */
     unsigned char login_disabled;             /* login disabled flag */
     char *chroot_directory;                   /* chroot(2) into this dir */
+    char *preauth_command;                    /* arbitrary pre-authentication command */
     char *auth_sasl_plain_username;           /* authentication username under SASL PLAIN */
     char *auth_sasl_plain_password;           /* authentication password under SASL PLAIN */
     char *auth_shared_secret;                 /* REQUIRED shared secret in leiu of a user password when using LOGIN command with SASL PLAIN authentication */
Index: src/imapcommon.c
===================================================================
--- src/imapcommon.c	(revision 14096)
+++ src/imapcommon.c	(working copy)
@@ -746,6 +746,116 @@
 
 
     /*
+     * Send and validate pre-authentication command if given
+     */
+    if ( PC_Struct.preauth_command )
+    {
+	snprintf( SendBuf, BufLen, "P0001 %s\r\n", PC_Struct.preauth_command );
+	
+	if ( IMAP_Write( Server.conn, SendBuf, strlen(SendBuf) ) == -1 )
+	{
+	    syslog( LOG_INFO,
+		    "PREAUTH failed: IMAP_Write() failed attempting to send pre-authentication command to IMAP server: %s",
+		    strerror( errno ) );
+	    goto fail;
+	}
+    
+	// Read the server response
+	//
+	for ( ;; )
+	{
+	    if ( ( rc = IMAP_Line_Read( &Server ) ) == -1 )
+	    {
+		syslog( LOG_INFO,
+			"PREAUTH failed: No response from IMAP server after sending pre-authentication command (%s)",
+			PC_Struct.preauth_command );
+		goto fail;
+	    }
+
+	    if ( Server.LiteralBytesRemaining )
+	    {
+		syslog(LOG_ERR, "%s: Unexpected string literal in server pre-authentication response.", fn );
+		goto fail;
+	    }
+	
+	    if ( Server.ReadBuf[0] != '*' )
+		break;
+	}
+    
+    
+	// Try to match up the tag in the server response to the client tag.
+	//
+	endptr = Server.ReadBuf + rc;
+    
+	tokenptr = memtok( Server.ReadBuf, endptr, &last );
+    
+	if ( !tokenptr )
+	{
+
+	    // no tokens found in server response?  Not likely, but we still
+	    // have to check.
+	    //
+	    syslog( LOG_INFO, "PREAUTH failed: server response to pre-authentication command contained no tokens." );
+	    goto fail;
+	}
+    
+	if ( memcmp( (const void *)tokenptr, (const void *)"P0001", strlen( tokenptr ) ) )
+	{
+
+	    // non-matching tag read back from the server... Lord knows what this
+	    // is, so we'll fail.
+	    //
+	    syslog( LOG_INFO, "PREAUTH failed: server response to pre-authentication command contained non-matching tag." );
+	    goto fail;
+	}
+    
+    
+	// Now that we've matched the tags up, see if the response was 'OK'
+	//
+	tokenptr = memtok( NULL, endptr, &last );
+    
+	if ( !tokenptr )
+	{
+	    // again, not likely but we still have to check... 
+	    //
+	    syslog( LOG_INFO, "PREAUTH failed: Malformed server response to pre-authentication command" );
+	    goto fail;
+	}
+    
+	if ( memcmp( (const void *)tokenptr, "OK", 2 ) )
+	{
+	    // In order to log the full server response (minus the tag),
+	    // we want to re-construct the ReadBuf starting at the location
+	    // currently pointed to by tokenptr.  Thus, we put back the
+	    // last space that memtok() had replaced with a null characater
+	    // (at location pointed to by last).
+	    //
+	    *last = ' ';
+
+	    // Then we re-adjust endptr to point to the CR at the end of
+	    // the line and set to NULL (a few lines below) so we can use
+	    // the rest of the response information as a normal string
+	    // 
+	    endptr = memchr( last + 1, '\r', endptr - (last + 1) );
+
+	    // No CR is unexpected; does this indicate malformed response?
+	    // Probably.  Anyway, we'll just give up on finding any other
+	    // info from the server.
+	    //
+	    if ( !endptr )
+	    endptr = last;
+
+	    *endptr = '\0';
+
+	    syslog( LOG_INFO,
+		"PREAUTH failed: non-OK server response to pre-authentication command (%s): %s",
+		PC_Struct.preauth_command, tokenptr );
+	    goto fail;
+	}
+    }
+    
+
+    /*
      * If configured to do so, execute SASL PLAIN authentication
      * using the static authentication username and password from
      * configuration (auth_sasl_plain_username/auth_sasl_plain_password).
Index: src/config.c
===================================================================
--- src/config.c	(revision 14095)
+++ src/config.c	(working copy)
@@ -393,6 +393,9 @@
     ADD_TO_TABLE( "cache_expiration_time", SetNumericValue, 
 		  &PC_Struct.cache_expiration_time, index );
 
+    ADD_TO_TABLE( "preauth_command", SetStringValue,
+		  &PC_Struct.preauth_command, index );
+
     ADD_TO_TABLE( "auth_sasl_plain_username", SetStringValue,
 		  &PC_Struct.auth_sasl_plain_username, index );
     
@@ -505,6 +508,24 @@
 	}
 	
 	Value = CP;
+
+	// we don't just want the next token, we want the rest of the line
+	// (put back the space that strtok() changed into a null character)
+	//
+	Value[ strlen( Value ) ] = ' ';
+
+	// however, we then have to be careful to remove trailing whitespace
+	//
+	i = strlen( Value ) - 1;
+	while ( ( Value[ i ] == ' ' )
+	     || ( Value[ i ] == '\t' )
+	     || ( Value[ i ] == '\r' )
+	     || ( Value[ i ] == '\n' ) )
+	{
+	    i--;
+	}
+	if ( i < ( strlen( Value ) - 1 ) )
+	    Value[ i + 1 ] = '\0';
 	
 	for (i = 0; ConfigTable[i].Keyword[0] != '\0'; i++ )
 	{
Index: scripts/imapproxy.conf
===================================================================
--- scripts/imapproxy.conf	(revision 14095)
+++ scripts/imapproxy.conf	(working copy)
@@ -185,6 +185,23 @@
 
 
 #
+## preauth_command
+##
+## Arbitrary command that can be sent to the server before
+## authenticating users.  This can be useful to access non-
+## standard IMAP servers such as Yahoo!, which requires the
+## following command to be sent before authentication is allowed:
+##    ID ("GUID" "1")
+## (See: http://en.wikipedia.org/wiki/Yahoo!_Mail#Free_IMAP_and_SMTPs_access )
+## To use such a command, this setting should look like this:
+##    preauth_command ID ("GUID" "1")
+## No matter what this command is, it is expected to return an
+## OK response
+#
+#preauth_command
+
+
+#
 ## enable_admin_commands
 ##
 ## Used to enable or disable the internal squirrelmail-imap_proxy
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 14101)
+++ ChangeLog	(working copy)
@@ -1,13 +1,20 @@
 2011-04-17  Paul Lesniewski <paul@squirrelmail.org>
-        * Add restart operation to (linux) init script
-        * Add BSD-style init script (thanks to Emmanuel Dreyfus)
+	* Added configurable, arbitrary pre-authentication command
+	  that the administrator can use to send non-standard
+	  commands to the server before each user authenticates
+	  (for an example usage, see:
+	  http://en.wikipedia.org/wiki/Yahoo!_Mail#Free_IMAP_and_SMTPs_access )
 
 2011-04-17  Paul Lesniewski <paul@squirrelmail.org>
-        * Fixed server connection synchronization issues in the SELECT
-          cache code (ensure server failures result in server connections
-          being fully shut down and removed from connection cache).
+	* Add restart operation to (linux) init script
+	* Add BSD-style init script (thanks to Emmanuel Dreyfus)
 
 2011-04-17  Paul Lesniewski <paul@squirrelmail.org>
+	* Fixed server connection synchronization issues in the SELECT
+	  cache code (ensure server failures result in server connections
+	  being fully shut down and removed from connection cache).
+
+2011-04-17  Paul Lesniewski <paul@squirrelmail.org>
 	* When NO or BAD response is returned from the server against
 	  a LOGIN or AUTHENTICATE request, we now log the full server
 	  response and pass it back to the client (useful if client
