Added support for IMAP4 commands that may span multiple packets by calling recv(...
authorHugo Villeneuve <hugo@hugovil.com>
Sat, 25 May 2013 21:25:00 +0000 (17:25 -0400)
committerHugo Villeneuve <hugo@hugovil.com>
Sat, 25 May 2013 21:36:10 +0000 (17:36 -0400)
Now using the same Tx and Rx buffers for POP3 and IMAP4

ChangeLog
src/imap.c
src/imap.h
src/network.c
src/network.h
src/pop3.c
src/pop3.h

index f52ccd3..bbc4cf1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,7 @@
 2005-06-22  Hugo Villeneuve  <hugo@hugovil.com>
+       * Now using the same Tx and Rx buffers for POP3 and IMAP4.
+       * Added support for IMAP4 commands that may span multiple
+       packets by calling recv() until full IMAP4 response is received.
        * Removed autogenerated files from subversion repository.
        
 2004-02-24  Hugo Villeneuve  <hugo@hugovil.com>
index 6c272d5..798d5b6 100644 (file)
 #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;
 }
 
 
@@ -126,7 +201,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 +236,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 +245,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 +254,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;
index 75d7841..e759456 100644 (file)
 #endif
 
 
-#define IMAP4_IN_BUF_SIZE 1024
-
-#define IMAP4_SUCCESS      "OK"
-#define IMAP4_FAILURE      "NO"
-#define IMAP4_PROTOCOL_ERR "BAD"
-
-#define IMAP4_UNSEEN "* OK [UNSEEN"
-
-
-#define IMAP_CMD_CAPABILITY "CAPABILITY"
-#define IMAP_CMD_LOGIN      "LOGIN"
-#define IMAP_CMD_EXAMINE    "EXAMINE"
-#define IMAP_CMD_LOGOUT     "LOGOUT"
-
-#define IMAP4_ENDL "\r\n" /* CRLF */
-
-
 int
 IMAP4_CheckForNewMail( void );
 
index 2520000..caa1c28 100644 (file)
 #define RECV_FLAGS 0
 
 
+/* Common buffers for IMAP4 and POP3. */
+char tx_buffer[WMNOTIFY_BUFSIZE + 1];
+char rx_buffer[WMNOTIFY_BUFSIZE + 1];
+
+
 int
 SocketOpen( char *server_name, int port )
 {
index e291aa2..19a9f2c 100644 (file)
 #include <unistd.h>
 
 
+/* POP3 responses may be up to 512 characters long, including the terminating
+   CRLF. */
+#define WMNOTIFY_BUFSIZE 1024
+
+
 int
 SocketOpen( char *server_name, int port );
 
index d9da74f..268647c 100644 (file)
@@ -23,8 +23,9 @@
 #include "pop3.h"
 
 
-static char tx_buffer[POP3_IN_BUF_SIZE];
-static char rx_buffer[POP3_IN_BUF_SIZE];
+/* Defined in network.c */
+extern char tx_buffer[WMNOTIFY_BUFSIZE + 1];
+extern char rx_buffer[WMNOTIFY_BUFSIZE + 1];
 
 
 static int
@@ -32,7 +33,7 @@ POP3_ReceiveResponse( void )
 {
   int len;
   
-  len = WmnotifyGetResponse( rx_buffer, POP3_IN_BUF_SIZE );
+  len = WmnotifyGetResponse( rx_buffer, WMNOTIFY_BUFSIZE );
   if( len < 0 ) {
     perror( PACKAGE );
     ErrorLocation( __FILE__, __LINE__ );
index 6f018ce..a617f1a 100644 (file)
 #  define _SCOPE_ extern
 #endif
 
-/* POP3 responses may be up to 512 characters long, including the terminating
-   CRLF. */
-#define POP3_IN_BUF_SIZE 512
+
+#define POP3_ENDL "\r\n" /* CRLF */
 
 #define POP3_CMD_USERNAME "USER"
 #define POP3_CMD_PASSWORD "PASS"
 #define POP3_CMD_STAT     "STAT"
 #define POP3_CMD_QUIT     "QUIT"
-#define POP3_ENDL "\r\n" /* CRLF */
-
 
 #define POP3_RSP_SUCCESS "+OK"
 #define POP3_RSP_FAILURE "-ERR"