#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;
+ 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 );
- len = WmnotifyGetResponse( rx_buffer, IMAP4_IN_BUF_SIZE );
if( len < 0 ) {
+ /* An error occured. */
perror( PACKAGE );
ErrorLocation( __FILE__, __LINE__ );
- return len;
+ 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 ) {
+ /* 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;
}
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. */
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 );
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 ) {
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;