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