From 820746961b7606517cafd3d0de32bbf51e5677b1 Mon Sep 17 00:00:00 2001 From: Dionysios Kalofonos 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 {}\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 {}\r\n */ }; extern const struct Curl_handler Curl_handler_imap; -- 1.8.5.2 (Apple Git-48)