/*++ * Function: SetBannerAndCapability * * Purpose: Connect to an IMAP server as a client and fetch the initial * banner string and the output from a CAPABILITY command. * * Parameters: none * * Returns: nuttin -- exits if there's a problem * * Authors: Dave McMurtrie <*****@*****.**> * * Notes: All AUTH mechanisms will be stripped from the capability * string. AUTH=LOGIN will be added. * The support_unselect flag in the global copy of the * ProxyConfig struct will be set in this function depending on * whether the server supports UNSELECT or not. *-- */ static void SetBannerAndCapability( void ) { int sd; ITD_Struct itd; ICD_Struct conn; int BytesRead; char *fn = "SetBannerAndCapability()"; int NumRef = 0; /* initialize some stuff */ memset( &itd, 0, sizeof itd ); for ( ;; ) { sd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( sd == -1 ) { syslog(LOG_ERR, "%s: socket() failed: %s -- exiting", fn, strerror(errno) ); exit( 1 ); } if ( connect( sd, (struct sockaddr *)&ISD.srv, sizeof(ISD.srv) ) == -1 ) { syslog(LOG_ERR, "%s: connect() to imap server on socket [%d] failed: %s", fn, sd, strerror(errno)); close( sd ); if ( errno == ECONNREFUSED && ++NumRef < 10 ) { sleep( 60 ); /* IMAP server may not be started yet. */ continue; } syslog( LOG_ERR, "%s: unable to connect() to imap server and retry limit exceeded -- exiting.", fn ); exit( 1 ); } break; /* Success */ } memset( &conn, 0, sizeof ( ICD_Struct ) ); itd.conn = &conn; itd.conn->sd = sd; /* * The first thing we get back from the server should be the * banner string. */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Error reading banner line from server on initial connection: %s -- Exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in banner response -- Exiting.", fn ); exit( 1 ); } BannerLen = ParseBannerAndCapability( Banner, sizeof Banner - 1, itd.ReadBuf, BytesRead ); /* * See if the string we got back starts with "* OK" by comparing the * first 4 characters of the buffer. */ if ( strncasecmp( Banner, IMAP_UNTAGGED_OK, strlen(IMAP_UNTAGGED_OK)) ) { syslog(LOG_ERR, "%s: Unexpected response from imap server on initial connection: %s -- Exiting.", fn, Banner); close( itd.conn->sd ); exit( 1 ); } /* Now we send a CAPABILITY command to the server. */ if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 ) { syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) ); close( itd.conn->sd ); exit( 1 ); } /* * From RFC2060: * The server MUST send a single untagged * CAPABILITY response with "IMAP4rev1" as one of the listed * capabilities before the (tagged) OK response. * * The means we should read exactly 2 lines of data back from the server. * The first will be the untagged capability line. * The second will be the OK response with the tag in it. */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn ); close( itd.conn->sd ); exit ( 1 ); } CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1, itd.ReadBuf, BytesRead ); /* Now read the tagged response and make sure it's OK */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn ); exit( 1 ); } if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) ) { syslog(LOG_ERR, "%s: Received non-OK tagged reponse from imap server on CAPABILITY command -- exiting.", fn ); close( itd.conn->sd ); exit( 1 ); } /* Be nice and logout */ if ( IMAP_Write( itd.conn, "2 LOGOUT\r\n", strlen("2 LOGOUT\r\n") ) == -1 ) { syslog(LOG_WARNING, "%s: IMAP_Write() failed on LOGOUT: %s -- Ignoring", fn, strerror(errno) ); close( itd.conn->sd ); return; } /* read the final OK logout */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog(LOG_WARNING, "%s: IMAP_Line_Read() failed on LOGOUT -- Ignoring", fn ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_WARNING, "%s: Server sent unexpected literal specifier in LOGOUT response -- Ignoring", fn ); } close( itd.conn->sd ); return; }
/*++ * Function: SetBannerAndCapability * * Purpose: Connect to an IMAP server as a client and fetch the initial * banner string and the output from a CAPABILITY command. * * Parameters: none * * Returns: nuttin -- exits if there's a problem * * Authors: Dave McMurtrie <*****@*****.**> * * Notes: All AUTH mechanisms will be stripped from the capability * string. AUTH=LOGIN will be added. * The support_unselect flag in the global copy of the * ProxyConfig struct will be set in this function depending on * whether the server supports UNSELECT or not. *-- */ static void SetBannerAndCapability( void ) { int sd; ITD_Struct itd; ICD_Struct conn; int BytesRead; char *fn = "SetBannerAndCapability()"; /* initialize some stuff */ memset( &itd, 0, sizeof itd ); for ( ;; ) { sd = socket( ISD.srv->ai_family, ISD.srv->ai_socktype, ISD.srv->ai_protocol ); if ( sd == -1 ) { syslog(LOG_ERR, "%s: socket() failed: %s -- exiting", fn, strerror(errno) ); exit( 1 ); } if ( connect( sd, (struct sockaddr *)ISD.srv->ai_addr, ISD.srv->ai_addrlen ) == -1 ) { syslog(LOG_ERR, "%s: connect() to IMAP server on socket [%d] failed: %s -- retrying", fn, sd, strerror(errno)); close( sd ); sleep( 15 ); /* IMAP server may not be started yet. */ } else { break; /* Success */ } } memset( &conn, 0, sizeof ( ICD_Struct ) ); itd.conn = &conn; itd.conn->sd = sd; /* * The first thing we get back from the server should be the * banner string. */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Error reading banner line from server on initial connection: %s -- Exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in banner response -- Exiting.", fn ); exit( 1 ); } BannerLen = ParseBannerAndCapability( Banner, sizeof Banner - 1, itd.ReadBuf, BytesRead, 0 ); /* * See if the string we got back starts with "* OK" by comparing the * first 4 characters of the buffer. */ if ( strncasecmp( Banner, IMAP_UNTAGGED_OK, strlen(IMAP_UNTAGGED_OK)) ) { syslog(LOG_ERR, "%s: Unexpected response from IMAP server on initial connection: %s -- Exiting.", fn, Banner); close( itd.conn->sd ); exit( 1 ); } /* Now we send a CAPABILITY command to the server. */ if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 ) { syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) ); close( itd.conn->sd ); exit( 1 ); } /* * From RFC2060: * The server MUST send a single untagged * CAPABILITY response with "IMAP4rev1" as one of the listed * capabilities before the (tagged) OK response. * * The means we should read exactly 2 lines of data back from the server. * The first will be the untagged capability line. * The second will be the OK response with the tag in it. */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn ); close( itd.conn->sd ); exit ( 1 ); } CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1, itd.ReadBuf, BytesRead, 1 ); /* Now read the tagged response and make sure it's OK */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn ); exit( 1 ); } if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) ) { syslog(LOG_ERR, "%s: Received non-OK tagged reponse from IMAP server on CAPABILITY command -- exiting.", fn ); close( itd.conn->sd ); exit( 1 ); } /* * If we're using it, attempt STARTTLS before doing one more * CAPABILITY so our capability string is accurate */ if ( PC_Struct.login_disabled || PC_Struct.force_tls ) { #if HAVE_LIBSSL if ( PC_Struct.support_starttls != STARTTLS_NOT_SUPPORTED ) { if ( Attempt_STARTTLS( &itd ) != 0 ) { syslog(LOG_ERR, "%s: STARTTLS failed for CAPABILITY check -- exiting.", fn ); close( itd.conn->sd ); exit( 1 ); } else { /* * STARTTLS was successful, so we can proceed * to get the new CAPABILITY list - first, * send a CAPABILITY command to the server. */ if ( IMAP_Write( itd.conn, "1 CAPABILITY\r\n", strlen("1 CAPABILITY\r\n") ) == -1 ) { syslog(LOG_ERR, "%s: Unable to send capability command to server: %s -- exiting.", fn, strerror(errno) ); close( itd.conn->sd ); exit( 1 ); } /* * From RFC2060: * The server MUST send a single untagged * CAPABILITY response with "IMAP4rev1" as one of the listed * capabilities before the (tagged) OK response. * * The means we should read exactly 2 lines of data back from the server. * The first will be the untagged capability line. * The second will be the OK response with the tag in it. */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in CAPABILITY response -- Exiting.", fn ); close( itd.conn->sd ); exit ( 1 ); } CapabilityLen = ParseBannerAndCapability( Capability, sizeof Capability - 1, itd.ReadBuf, BytesRead, 1 ); /* Now read the tagged response and make sure it's OK */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog( LOG_ERR, "%s: Failed to read capability response from server: %s -- exiting.", fn, strerror( errno ) ); close( itd.conn->sd ); exit( 1 ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_ERR, "%s: Server sent unexpected literal specifier in tagged CAPABILITY response -- exiting.", fn ); exit( 1 ); } if ( strncasecmp( itd.ReadBuf, IMAP_TAGGED_OK, strlen(IMAP_TAGGED_OK) ) ) { syslog(LOG_ERR, "%s: Received non-OK tagged reponse from imap server on CAPABILITY command -- exiting.", fn ); close( itd.conn->sd ); exit( 1 ); } } } else #endif /* HAVE_LIBSSL */ { /* We're screwed! We won't be able to login without SASL */ syslog(LOG_ERR, "%s: IMAP server has LOGINDISABLED and we can't do STARTTLS. Exiting.", fn); close( itd.conn->sd ); exit( 1 ); } } else { //syslog( LOG_ERR, "%s: Not trying STARTTLS and second CAPABILITY.", fn ); } /* Be nice and logout */ if ( IMAP_Write( itd.conn, "2 LOGOUT\r\n", strlen("2 LOGOUT\r\n") ) == -1 ) { syslog(LOG_WARNING, "%s: IMAP_Write() failed on LOGOUT: %s -- Ignoring", fn, strerror(errno) ); close( itd.conn->sd ); return; } /* read the final OK logout */ BytesRead = IMAP_Line_Read( &itd ); if ( BytesRead == -1 ) { syslog(LOG_WARNING, "%s: IMAP_Line_Read() failed on LOGOUT -- Ignoring", fn ); } if ( itd.LiteralBytesRemaining ) { syslog( LOG_WARNING, "%s: Server sent unexpected literal specifier in LOGOUT response -- Ignoring", fn ); } close( itd.conn->sd ); return; }