On 4/10/14, 12:09 AM, Steve Holme wrote:
Currently all custom requests for IMAP overload the behaviour of a
LIST command :(

If i wanted to fix this how would i go about it? I am not yet familiar
enough with the code.

If I was you and if I was totally new to the project (and I'm assuming you
are - apologies if you are not) I would recommend:


The attached patch fixes this issue, where the library would return only the first line of a response to a custom request that included a string literal (http://tools.ietf.org/html/rfc3501#section-4.3).

The attached patch introduces the imap_perform_custom(...) action, the imap_state_custom_resp(...) handler, the IMAP_CUSTOM state and the octet_count variable in the imap_conn structure.

Given a response to a custom request, the response handler checks whether it ends in {<count>}\r\n. When it does it sets the octet_count in imap_conn. This count in conjunction with the IMAP_CUSTOM state, are used by the imap_endofresp(...) to determine whether the fetched data are expected or not.

Could i have your feedback before i continue with unit tests etc?

Thank you, kind regards,

--
Dionysios Kalofonos

From 820746961b7606517cafd3d0de32bbf51e5677b1 Mon Sep 17 00:00:00 2001
From: Dionysios Kalofonos <[email protected]>
Date: Thu, 17 Apr 2014 18:12:00 +0300
Subject: [PATCH] imap: Fixed the response handling of custom requests

Fixes an issue where the library would return only the first line of a
response to a custom request that included a string literal.

Quoting the rfc3501, s. 4.3:
A literal is a sequence of zero or more octets (including CR and
LF), prefix-quoted with an octet count in the form of an open
brace ("{"), the number of octets, close brace ("}"), and CRLF.
---
 lib/imap.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++---------------
 lib/imap.h |   2 ++
 2 files changed, 92 insertions(+), 28 deletions(-)

diff --git a/lib/imap.c b/lib/imap.c
index a3dd499..33fe732 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -279,6 +279,13 @@ static bool imap_endofresp(struct connectdata *conn, char 
*line, size_t len,
   const char *id = imapc->resptag;
   size_t id_len = strlen(id);
 
+  /* Do we expect more data? */
+  if(IMAP_CUSTOM == imapc->state && 0 < imapc->octet_count)
+  {
+    *resp = '?';
+    return TRUE;
+  }
+
   /* Do we have a tagged command response? */
   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
     line += id_len + 1;
@@ -307,21 +314,15 @@ static bool imap_endofresp(struct connectdata *conn, char 
*line, size_t len,
           return FALSE;
         break;
 
-      case IMAP_LIST:
-        if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
-          (imap->custom && !imap_matchresp(line, len, imap->custom) &&
-           (strcmp(imap->custom, "STORE") ||
-            !imap_matchresp(line, len, "FETCH")) &&
-           strcmp(imap->custom, "SELECT") &&
-           strcmp(imap->custom, "EXAMINE") &&
-           strcmp(imap->custom, "SEARCH") &&
-           strcmp(imap->custom, "EXPUNGE") &&
-           strcmp(imap->custom, "LSUB") &&
-           strcmp(imap->custom, "UID") &&
-           strcmp(imap->custom, "NOOP")))
-          return FALSE;
+      case IMAP_CUSTOM:
+        /* Accept anything! */
         break;
 
+      case IMAP_LIST:
+       if(!imap_matchresp(line, len, "LIST"))
+         return FALSE;
+       break;
+
       case IMAP_SELECT:
         /* SELECT is special in that its untagged responses do not have a
            common prefix so accept anything! */
@@ -439,6 +440,7 @@ static void state(struct connectdata *conn, imapstate 
newstate)
     "FETCH_FINAL",
     "APPEND",
     "APPEND_FINAL",
+    "CUSTOM",
     "LOGOUT",
     /* LAST */
   };
@@ -644,32 +646,49 @@ static CURLcode imap_perform_authentication(struct 
connectdata *conn)
 
 /***********************************************************************
  *
- * imap_perform_list()
+ * imap_perform_custom()
  *
- * Sends a LIST command or an alternative custom request.
+ * Sends a custom request.
  */
-static CURLcode imap_perform_list(struct connectdata *conn)
+static CURLcode imap_perform_custom(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   struct IMAP *imap = data->req.protop;
-  char *mailbox;
 
   if(imap->custom)
     /* Send the custom request */
     result = imap_sendf(conn, "%s%s", imap->custom,
                         imap->custom_params ? imap->custom_params : "");
-  else {
-    /* Make sure the mailbox is in the correct atom format */
-    mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
-    if(!mailbox)
-      return CURLE_OUT_OF_MEMORY;
 
-    /* Send the LIST command */
-    result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
+  if(!result)
+    state(conn, IMAP_CUSTOM);
+
+  return result;
+}
 
-    Curl_safefree(mailbox);
-  }
+/***********************************************************************
+ *
+ * imap_perform_list()
+ *
+ * Sends a LIST command.
+ */
+static CURLcode imap_perform_list(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct IMAP *imap = data->req.protop;
+  char *mailbox;
+
+  /* Make sure the mailbox is in the correct atom format */
+  mailbox = imap_atom(imap->mailbox ? imap->mailbox : "");
+  if(!mailbox)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* Send the LIST command */
+  result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
+
+  Curl_safefree(mailbox);
 
   if(!result)
     state(conn, IMAP_LIST);
@@ -1382,6 +1401,45 @@ static CURLcode imap_state_login_resp(struct connectdata 
*conn,
   return result;
 }
 
+/* For CUSTOM responses */
+static CURLcode imap_state_custom_resp(struct connectdata *conn, int imapcode,
+                                      imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  char *line = conn->data->state.buffer;
+  size_t len = strlen(line);
+  char *ptr = NULL, *endptr = NULL;
+  curl_off_t count = 0;
+
+  (void)instate; /* No use for this yet */
+
+  if(imapcode == '*' || imapcode == '?') {
+    /* Temporarily add the LF character back and send as body to the client */
+    line[len] = '\n';
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
+    imapc->octet_count -= (0 < imapc->octet_count ? len : 0);
+    /* Does the response ends in {<count>}\r\n? */
+    for(ptr = line + len; ptr >= line; ptr--) {
+      if(*ptr == '{') {
+       count = curlx_strtoofft(ptr + 1, &endptr, 10);
+       if(endptr - ptr > 1 && endptr[0] == '}' &&
+          endptr[1] == '\r' && endptr[2] == '\n')
+         imapc->octet_count = count;
+       break;
+      }
+    }
+    line[len] = '\0';
+  }
+  else if(imapcode != 'O')
+    result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
+  else
+    /* End of DO phase */
+    state(conn, IMAP_STOP);
+
+  return result;
+}
+
 /* For LIST responses */
 static CURLcode imap_state_list_resp(struct connectdata *conn, int imapcode,
                                      imapstate instate)
@@ -1439,7 +1497,7 @@ static CURLcode imap_state_select_resp(struct connectdata 
*conn, int imapcode,
       imapc->mailbox = strdup(imap->mailbox);
 
       if(imap->custom)
-        result = imap_perform_list(conn);
+        result = imap_perform_custom(conn);
       else
         result = imap_perform_fetch(conn);
     }
@@ -1731,6 +1789,10 @@ static CURLcode imap_statemach_act(struct connectdata 
*conn)
       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
       break;
 
+    case IMAP_CUSTOM:
+      result = imap_state_custom_resp(conn, imapcode, imapc->state);
+      break;
+
     case IMAP_LOGOUT:
       /* fallthrough, just stop! */
     default:
@@ -1950,7 +2012,7 @@ static CURLcode imap_perform(struct connectdata *conn, 
bool *connected,
     result = imap_perform_append(conn);
   else if(imap->custom && (selected || !imap->mailbox))
     /* Custom command using the same mailbox or no mailbox */
-    result = imap_perform_list(conn);
+    result = imap_perform_custom(conn);
   else if(!imap->custom && selected && imap->uid)
     /* FETCH from the same mailbox */
     result = imap_perform_fetch(conn);
diff --git a/lib/imap.h b/lib/imap.h
index 95e55be..6892443 100644
--- a/lib/imap.h
+++ b/lib/imap.h
@@ -53,6 +53,7 @@ typedef enum {
   IMAP_FETCH_FINAL,
   IMAP_APPEND,
   IMAP_APPEND_FINAL,
+  IMAP_CUSTOM,
   IMAP_LOGOUT,
   IMAP_LAST          /* never used */
 } imapstate;
@@ -88,6 +89,7 @@ struct imap_conn {
   bool ir_supported;          /* Initial response supported by server */
   char *mailbox;              /* The last selected mailbox */
   char *mailbox_uidvalidity;  /* UIDVALIDITY parsed from select response */
+  curl_off_t octet_count;     /* Set when the response ends in {<count>}\r\n */
 };
 
 extern const struct Curl_handler Curl_handler_imap;
-- 
1.8.5.2 (Apple Git-48)

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to