cURL / Mailing Lists / curl-library / Single Mail

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

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