X-Git-Url: http://gitweb.hugovil.com/?a=blobdiff_plain;f=src%2Fimap.c;h=bb800827d5bdb044eefd348f00e33e1df66f009b;hb=a1a7d5dc60fb40d39c9f1cbc6b1879bc562299a7;hp=6c272d53d756bbe10dc7a0480732209606764bfa;hpb=eaca75aa6c133cc6ff3d707f7ae8dbfbe51a6d2a;p=dockapps%2Fwmnotify.git diff --git a/src/imap.c b/src/imap.c index 6c272d5..bb80082 100644 --- a/src/imap.c +++ b/src/imap.c @@ -25,84 +25,163 @@ #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" + +/* 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 char tx_buffer[IMAP4_IN_BUF_SIZE]; -static char rx_buffer[IMAP4_IN_BUF_SIZE]; +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 ) { + /* This shouldn't happen, the examine command doesn't requires that much data. */ + if( wmnotify_infos.debug ) { + ErrorLocation( __FILE__, __LINE__ ); + fprintf( stderr, "Response too big to fit in receive buffer.\n" ); + } + 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 ) { + /* 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 ) { 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 +205,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 +240,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,8 +249,8 @@ IMAP4_CheckForNewMail( void ) goto imap4_logout; } - unseen_string_found = FALSE; - argv[0] = IMAP_CMD_EXAMINE; + /* Selecting the mailbox first. */ + argv[0] = IMAP4_CMD_EXAMINE; argv[1] = "inbox"; status = IMAP4_SendCommand( 2, argv ); if( status != EXIT_SUCCESS ) { @@ -177,12 +258,26 @@ IMAP4_CheckForNewMail( void ) goto imap4_logout; } + /* 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;