curl-library
[imap] imap: Return folder listing if empty URL content is given.
From: Ben Greear <greearb_at_candelatech.com>
Date: Tue, 30 Mar 2010 11:12:04 -0700
Date: Tue, 30 Mar 2010 11:12:04 -0700
More work is needed to make imap useful, including parsing URLs
to grab folder and/or email ID. Folder list format might need
to be changed as well..it's pretty raw at the moment.
Signed-off-by: Ben Greear <greearb_at_candelatech.com>
--- :100644 100644 2ec2d2e... 869141d... M lib/imap.c :100644 100644 c6d74a4... 7dfc7c7... M lib/imap.h lib/imap.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/imap.h | 1 + 2 files changed, 194 insertions(+), 6 deletions(-) diff --git a/lib/imap.c b/lib/imap.c index 2ec2d2e..869141d 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -273,7 +273,8 @@ static int imap_endofresp(struct pingpong *pp, int *resp) *resp = line[id_len+1]; /* O, N or B */ return TRUE; } - else if(((imapc->state == IMAP_FETCH_HEADER) || + else if(((imapc->state == IMAP_LIST) || + (imapc->state == IMAP_FETCH_HEADER) || (imapc->state == IMAP_FETCH_BODY)) && !memcmp("* ", line, 2) ) { /* FETCH response we're interested in */ @@ -295,6 +296,7 @@ static void state(struct connectdata *conn, "SERVERGREET", "LOGIN", "STARTTLS", + "LIST", "SELECT", "FETCH_HEADER", "FETCH_BODY", @@ -525,6 +527,185 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn, return result; } +/* Grabs lines until we find a line that starts with 'starts'. Returns + * length in bytes. + */ +static size_t get_until_nstarts(const char* str, size_t max, const char* starts) { + size_t i; + size_t slen = strlen(starts); + /* Grab lines until done with all 'starts' elements */ + for (i = 0; i<max; i++) { + if (strncmp((str + i), starts, slen) == 0) { + /* grab entire line */ + i += slen; + for ( ; i< max; i++) { + /* Deal with \n\r and \r\n in case, maybe overly paranoid. */ + if ((str[i] == '\n') || (str[i] == '\r')) { + if ((i+1 < max) && ((str[i+1] == '\r') || (str[i+1] == '\n'))) { + i++; + } + break; + } + } + } + else { + break; + } + } + return i; +} + +/** Returns next instance of 'n', with case-insensitive search. If + * consumed is not NULL, then this will only search to the first newline + * and 'consumed' will be increased by the number of bytes it takes to + * consume the entire line, newline included. + */ +static const char* curl_strcasestr_ln(const char* h, const char* n, size_t* consumed) { + size_t lnh = strlen(h); + size_t lnn = strlen(n); + size_t i; + for (i = 0; i<lnh - lnn; i++) { + if (strncasecmp(h+i, n, lnn) == 0) { + if (consumed) { + *consumed = i + lnn; + for (; *consumed<lnh; (*consumed)++) { + if (h[*consumed] == '\n') + break; + } + } + return h+i; + } + } + return NULL; +} + +#if 0 +static void hexprintf(const char* s, size_t ln, const char* desc) { + size_t i; + printf("%s: ", desc); + for (i = 0; i<ln; i++) { + if (i % 16 == 15) + printf("%02hx\n", (unsigned short)(s[i])); + else + printf("%02hx ", (unsigned short)(s[i])); + } + printf("\n\n"); +} +#endif + +/* for the LIST response */ +static CURLcode imap_state_list_resp(struct connectdata *conn, + int imapcode, + imapstate instate) +{ + CURLcode result = CURLE_OK; + struct SessionHandle *data = conn->data; + struct imap_conn *imapc = &conn->proto.imapc; + struct FTP *imap = data->state.proto.imap; + struct pingpong *pp = &imapc->pp; + const char *ptr = data->state.buffer; + char* t2; + size_t i; + int got_eor = 0; + (void)instate; /* no use for this yet */ + + if('*' != imapcode) { + Curl_pgrsSetDownloadSize(data, 0); + state(conn, IMAP_STOP); + return CURLE_OK; + } + + /* Something like this comes "* LIST (\Flags) "/" "INBOX" */ + t2 = strstr(ptr, "* LIST "); + if (t2) { + char* tmps; + i = get_until_nstarts(t2, strlen(t2), "* LIST "); + if (i > 0 && t2[i-1] == '\r') + i--; /* seems \n is stripped from state.buffer, but not \r */ + tmps = malloc(i + 2); + memcpy(tmps, t2, i); + if (tmps[i] != '\n') { + tmps[i] = '\r'; + tmps[i+1] = '\n'; + i += 2; + } + /* hexprintf(tmps, i, "tmps"); */ + result = Curl_client_write(conn, CLIENTWRITE_BODY, tmps, i); + free(tmps); + } + + if(pp->cache) { + /* At this point there is a bunch of data in the header "cache" that is + actually body content, send it as body and then skip it. Do note + that there may even be additional "headers" after the body. */ + + /* hexprintf(pp->cache, pp->cache_size, "pp->cache"); */ + + i = get_until_nstarts(pp->cache, pp->cache_size, "* LIST "); + + if (i) { + result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, i); + if(result) + return result; + + /* we've now used parts of or the entire cache */ + if(pp->cache_size > i) { + if (curl_strcasestr_ln(pp->cache, " OK LIST completed", &i)) + got_eor = 1; + /* part of, move the trailing data to the start and reduce the size */ + memmove(pp->cache, pp->cache+i, + pp->cache_size - i); + pp->cache_size -= i; + pp->cache[pp->cache_size] = 0; /* ensure null term */ + } + else { + /* cache is drained */ + free(pp->cache); + pp->cache = NULL; + pp->cache_size = 0; + } + } + + /* Might should be case insensitive search ? */ + if((!pp->cache) || got_eor) { + /* printf("Completed, cache:\n%s\n", pp->cache?pp->cache:"NULL"); */ + /* the entire data is already transfered! */ + result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); + } + else { + /* printf("NOT completed, cache:\n%s\n", pp->cache); */ + fflush(stdout); + /* IMAP download */ + result=Curl_setup_transfer(conn, FIRSTSOCKET, 0, FALSE, + imap->bytecountp, + -1, NULL); /* no upload here */ + } + data->req.maxdownload = 0; /* no idea */ + } + else + /* We don't know how to parse this line */ + result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */ + + state(conn, IMAP_STOP); /* TODO: Maybe only STOP if we are done with tx? */ + return result; +} + +/* start the DO phase */ +static CURLcode imap_listing(struct connectdata *conn, const char* lst_args) +{ + CURLcode result = CURLE_OK; + const char *str; + + str = getcmdid(conn); + + result = imapsendf(conn, str, "%s LIST \"\" \"%s\"", str, lst_args); + if(result) + return result; + + state(conn, IMAP_LIST); + return result; +} + /* start the DO phase */ static CURLcode imap_select(struct connectdata *conn) { @@ -679,6 +860,10 @@ static CURLcode imap_statemach_act(struct connectdata *conn) result = imap_state_select_resp(conn, imapcode, imapc->state); break; + case IMAP_LIST: + result = imap_state_list_resp(conn, imapcode, imapc->state); + break; + case IMAP_LOGOUT: /* fallthrough, just stop! */ default: @@ -896,8 +1081,13 @@ CURLcode imap_perform(struct connectdata *conn, *dophase_done = FALSE; /* not done yet */ - /* start the first command in the DO phase */ - result = imap_select(conn); + if (strlen(conn->proto.imapc.mailbox)) + /* start the first command in the DO phase */ + result = imap_select(conn); + else + /* Just a listing then */ + result = imap_listing(conn, "*"); + if(result) return result; @@ -1015,9 +1205,6 @@ static CURLcode imap_parse_url_path(struct connectdata *conn) const char *path = data->state.path; int len; - if(!*path) - path = "INBOX"; - /* url decode the path and use this mailbox */ imapc->mailbox = curl_easy_unescape(data, path, 0, &len); if(!imapc->mailbox) diff --git a/lib/imap.h b/lib/imap.h index c6d74a4..7dfc7c7 100644 --- a/lib/imap.h +++ b/lib/imap.h @@ -33,6 +33,7 @@ typedef enum { a connect */ IMAP_LOGIN, IMAP_STARTTLS, + IMAP_LIST, IMAP_SELECT, IMAP_FETCH_HEADER, IMAP_FETCH_BODY, -- 1.6.2.5 ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.htmlReceived on 2010-03-30