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.html
Received on 2010-03-30