X-Git-Url: http://gitweb.hugovil.com/?a=blobdiff_plain;f=src%2Fimap.c;h=da11e473e11052801434cf41bd56a783ebbdc4e7;hb=01555cb56d0c00242817f0da62f2cab6c99c466e;hp=6c272d53d756bbe10dc7a0480732209606764bfa;hpb=eaca75aa6c133cc6ff3d707f7ae8dbfbe51a6d2a;p=dockapps%2Fwmnotify.git diff --git a/src/imap.c b/src/imap.c index 6c272d5..da11e47 100644 --- a/src/imap.c +++ b/src/imap.c @@ -1,5 +1,22 @@ -/* imap.c -- Routines for communication with an IMAP server */ - +/* + * imap.c -- Routines for communication with an IMAP server + * + * Copyright (C) 2003 Hugo Villeneuve + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ /* Define filename_M */ #define IMAP_M 1 @@ -25,84 +42,162 @@ #include "imap.h" -const static char line_delimiter[] = "\n"; +#define IMAP4_ENDL "\r\n" /* CRLF */ -static int tlabel = 0; -static int tlabel_len; +#define IMAP4_CMD_CAPABILITY "CAPABILITY" +#define IMAP4_CMD_LOGIN "LOGIN" +#define IMAP4_CMD_SELECT "SELECT" +#define IMAP4_CMD_EXAMINE "EXAMINE" +#define IMAP4_CMD_LOGOUT "LOGOUT" +#define IMAP4_CMD_SEARCH_UNSEEN "SEARCH UNSEEN" -static char tx_buffer[IMAP4_IN_BUF_SIZE]; -static char rx_buffer[IMAP4_IN_BUF_SIZE]; +/* Responses from IMAP4 server. */ +#define IMAP4_RSP_SUCCESS "OK" +#define IMAP4_RSP_FAILURE "NO" +#define IMAP4_RSP_PROTOCOL_ERR "BAD" +#define IMAP4_RSP_SEARCH_UNSEEN "* SEARCH " /* This is the line that will be returned by + * the IMAP4 server after receiving the + * "SEARCH UNSEEN" command, followed by the + * messages ID of the unseen messages. */ + +static int tlabel = 0; +static int tlabel_len; static int unseen_string_found; +/* Defined in network.c */ +extern char tx_buffer[WMNOTIFY_BUFSIZE + 1]; +extern char rx_buffer[WMNOTIFY_BUFSIZE + 1]; + static int IMAP4_ReceiveResponse( void ) { int len; char *token; - - len = WmnotifyGetResponse( rx_buffer, IMAP4_IN_BUF_SIZE ); + char *stringp; + + /* All interactions transmitted by client and server are in the form of + lines, that is, strings that end with a CRLF. The protocol receiver + of an IMAP4rev1 client or server is either reading a line, or is + reading a sequence of octets with a known count followed by a line. */ + + get_packet: + len = WmnotifyGetResponse( rx_buffer, WMNOTIFY_BUFSIZE ); if( len < 0 ) { - perror( PACKAGE ); - ErrorLocation( __FILE__, __LINE__ ); - return len; + /* An error occured. WmnotifyGetResponse() should have printed an error message. */ + goto error; + } + else if( len == 0 ) { + /* The return value will be 0 when the peer has performed an orderly shutdown. */ + if( wmnotify_infos.debug ) { + fprintf( stderr, "IMAP server has closed connection.\n" ); + } + goto error; + } + else if( len == WMNOTIFY_BUFSIZE ) { + if( wmnotify_infos.debug ) { + ErrorLocation( __FILE__, __LINE__ ); + fprintf( stderr, "Response too big (%d bytes) to fit in receive buffer.\n", len ); + } + goto error; } - rx_buffer[ len - 2 ] = '\0'; + /* We suppose that, if a partial response packet was sent, it is not broken in the middle + of a line (to confirm). Normally, each string is terminated by CRLF. */ + if( STREQ_LEN( &rx_buffer[ len - 2 ], IMAP4_ENDL, 2 ) == false ) { + /* No CRLF found at the end of the buffer --> not handled by wmnotify. */ + ErrorLocation( __FILE__, __LINE__ ); + fprintf( stderr, "Response buffer doesn't contain CRLF at the end.\n" ); + goto error; + } if( wmnotify_infos.debug ) { - printf( "Response: \"%s\"\n", rx_buffer ); + printf( "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" ); + printf( "IMAP4 Server Response (size %d bytes):\n", len ); + printf( "%s", rx_buffer ); + printf( "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" ); } - + + /* Converting the last CRLF into a LF followed by a NULL termination character. */ + rx_buffer[ len - 2 ] = '\n'; + rx_buffer[ len - 1 ] = '\0'; + /* Check the Server Completion Response returned by the IMAP4 server. There are currently * three Server Completion Responses codes: success ("OK"), failure ("NO") and protocol error * ("BAD"). */ - token = strtok( rx_buffer, line_delimiter ); + stringp = rx_buffer; + + while( ( token = strsep( &stringp, "\n" ) ) != NULL ) { + + /* In case no delimiter was found, the token is taken to + be the entire string *stringp, and *stringp is made NULL. */ + if( stringp == NULL ) { + if( token[0] == '\0' ) { + /* This means we finished parsing the last line of the buffer, but we need to + get more data to continue process the next part of the IMAP4 response. */ + goto get_packet; + } + else { + /* This should never happen. */ + ErrorLocation( __FILE__, __LINE__ ); + fprintf( stderr, " Delimiter not found in strsep() call.\n" ); + goto error; + } + } + + if( token == NULL ) { + /* This should never happen. */ + ErrorLocation( __FILE__, __LINE__ ); + fprintf( stderr, " NULL token returned by strsep().\n" ); + goto error; + } - while( len >= 0 ) { if( token[0] == '*' ) { - /* untagged response. */ - if( STREQ_LEN( token, IMAP4_UNSEEN, strlen(IMAP4_UNSEEN) ) == TRUE ) { - unseen_string_found = TRUE; + /* Untagged response. If there is a space after the SEARCH response, it means + * at least 1 message is unseen. */ + if( STREQ_LEN( token, IMAP4_RSP_SEARCH_UNSEEN, strlen(IMAP4_RSP_SEARCH_UNSEEN) ) == true ) { + unseen_string_found = true; } - goto loop_next; /* Next iteration of the while() loop (next line). */ } else { + /* Must be the status... */ + /* We check for the correct transaction label plus a space. */ - if( STREQ_LEN( token, tx_buffer, tlabel_len + 1 ) == TRUE ) { + if( STREQ_LEN( token, tx_buffer, tlabel_len + 1 ) == true ) { token += tlabel_len + 1; - if( STREQ_LEN( token, IMAP4_SUCCESS, strlen(IMAP4_SUCCESS) ) == TRUE ) { - break; /* OK, no errors. */ + if( STREQ_LEN( token, IMAP4_RSP_SUCCESS, strlen(IMAP4_RSP_SUCCESS) ) == true ) { + goto end; /* OK, no errors. */ } - else if( STREQ_LEN( token, IMAP4_PROTOCOL_ERR, strlen(IMAP4_PROTOCOL_ERR) ) == TRUE ) { + else if( STREQ_LEN( token, IMAP4_RSP_PROTOCOL_ERR, strlen(IMAP4_RSP_PROTOCOL_ERR) ) == true ) { fprintf( stderr, "%s: Protocol error (%s).\n", PACKAGE, token ); - len = -1; - break; + goto error; } - else if( STREQ_LEN( token, IMAP4_FAILURE, strlen(IMAP4_FAILURE) ) == TRUE ) { + else if( STREQ_LEN( token, IMAP4_RSP_FAILURE, strlen(IMAP4_RSP_FAILURE) ) == true ) { fprintf( stderr, "%s: Failure (%s).\n", PACKAGE, token ); - len = -1; - break; + goto error; } else { fprintf( stderr, "%s: Unknown error code (%s).\n", PACKAGE, token ); - len = -1; - break; + goto error; } } else { fprintf( stderr, "%s: Error, transaction label mismatch.\n", PACKAGE ); - len = -1; - break; + goto error; } } - - loop_next: - token = strtok( NULL, line_delimiter ); - } - + } /* while( token ) */ + + /* Get next part of IMAP4 response. */ + goto get_packet; + + end: + /* No error. */ return len; + + error: + return -1; } @@ -126,7 +221,9 @@ IMAP4_SendCommand( int argc, char *argv[] ) if( wmnotify_infos.debug ) { tx_buffer[len] = '\0'; - printf( "Command: \"%s\"\n", tx_buffer ); + printf( ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" ); + printf( "IMAP4 Client Command (size %d bytes):\n%s\n", len, tx_buffer ); + printf( ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" ); } /* Adding termination characters. */ @@ -159,7 +256,7 @@ IMAP4_CheckForNewMail( void ) goto end; } - argv[0] = IMAP_CMD_LOGIN; + argv[0] = IMAP4_CMD_LOGIN; argv[1] = wmnotify_infos.username; argv[2] = wmnotify_infos.password; status = IMAP4_SendCommand( 3, argv ); @@ -168,21 +265,35 @@ IMAP4_CheckForNewMail( void ) goto imap4_logout; } - unseen_string_found = FALSE; - argv[0] = IMAP_CMD_EXAMINE; - argv[1] = "inbox"; + /* Selecting the mailbox first. */ + argv[0] = IMAP4_CMD_EXAMINE; + argv[1] = wmnotify_infos.imap_folder; status = IMAP4_SendCommand( 2, argv ); if( status != EXIT_SUCCESS ) { new_messages = -1; goto imap4_logout; } - if( unseen_string_found == TRUE ) { + /* Searching in selected mailbox for new messages. We must use the UNSEEN search criteria + * instead of NEW (combination of RECENT and UNSEEN). If there is a new message, RECENT + * and UNSEEN will have entries. But if we recheck again later, RECENT will report zero. + * RECENT, when set, simply means that there are new messages since our last visit. + But, on the other hand, when using EXAMINE, no messages should lose their RECENT flag. */ + unseen_string_found = false; + argv[0] = IMAP4_CMD_SEARCH_UNSEEN; + argv[1] = ""; + status = IMAP4_SendCommand( 1, argv ); + if( status != EXIT_SUCCESS ) { + new_messages = -1; + goto imap4_logout; + } + + if( unseen_string_found == true ) { new_messages = 1; } imap4_logout: - argv[0] = IMAP_CMD_LOGOUT; + argv[0] = IMAP4_CMD_LOGOUT; status = IMAP4_SendCommand( 1, argv ); if( status != EXIT_SUCCESS ) { new_messages = -1;