diff --git a/lib/curl_imap.c b/lib/curl_imap.c
index e1b25da..24f645e 100644
--- a/lib/curl_imap.c
+++ b/lib/curl_imap.c
@@ -78,9 +78,8 @@
 /* The last #include file should be: */
 #include "curl_memdebug.h"
 
-/* Local API functions */
+/* Local functions */
 static CURLcode imap_parse_url_path(struct connectdata *conn);
-static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
 static CURLcode imap_do(struct connectdata *conn, bool *done);
 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
                           bool premature);
@@ -93,6 +92,8 @@ static int imap_getsock(struct connectdata *conn,
 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
 static CURLcode imap_setup_connection(struct connectdata *conn);
 static CURLcode imap_state_upgrade_tls(struct connectdata *conn);
+static CURLcode imap_after_select(struct connectdata *conn);
+static CURLcode imap_perform_custom(struct connectdata *conn);
 
 /*
  * IMAP protocol handler.
@@ -116,7 +117,7 @@ const struct Curl_handler Curl_handler_imap = {
   PORT_IMAP,                        /* defport */
   CURLPROTO_IMAP,                   /* protocol */
   PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD
-  | PROTOPT_NOURLQUERY              /* flags */
+    | PROTOPT_NOURLQUERY            /* flags */
 };
 
 
@@ -143,13 +144,13 @@ const struct Curl_handler Curl_handler_imaps = {
   PORT_IMAPS,                       /* defport */
   CURLPROTO_IMAP | CURLPROTO_IMAPS, /* protocol */
   PROTOPT_CLOSEACTION | PROTOPT_SSL | PROTOPT_NEEDSPWD
-  | PROTOPT_NOURLQUERY              /* flags */
+    | PROTOPT_NOURLQUERY            /* flags */
 };
 #endif
 
 #ifndef CURL_DISABLE_HTTP
 /*
- * HTTP-proxyed IMAP protocol handler.
+ * HTTP-proxied IMAP protocol handler.
  */
 
 static const struct Curl_handler Curl_handler_imap_proxy = {
@@ -175,7 +176,7 @@ static const struct Curl_handler Curl_handler_imap_proxy = {
 
 #ifdef USE_SSL
 /*
- * HTTP-proxyed IMAPS protocol handler.
+ * HTTP-proxied IMAPS protocol handler.
  */
 
 static const struct Curl_handler Curl_handler_imaps_proxy = {
@@ -204,43 +205,36 @@ static const struct Curl_handler Curl_handler_imaps_proxy = {
  *
  * imap_sendf()
  *
- * Sends the formated string as an IMAP command to the server.
+ * Sends the formatted string as an IMAP command to the server. Next IMAP
+ * command tag is generated automatically and noted in order to be
+ * recognized properly in imap_endofresp() later.
+ *
+ * Uses ping-pong interface, designed to never block.
  *
- * Designed to never block.
  */
 static CURLcode imap_sendf(struct connectdata *conn,
-                           const char *idstr, /* command id to wait for */
                            const char *fmt, ...)
 {
-  CURLcode res;
+  CURLcode result;
   struct imap_conn *imapc = &conn->proto.imapc;
+  char *tagfmt;
   va_list ap;
   va_start(ap, fmt);
 
-  imapc->idstr = idstr;
+  /* Get the next ID, but wrap at 1000 for 3 digits */
+  imapc->cmdid = (imapc->cmdid + 1) % 1000;
+  snprintf(imapc->idstr, sizeof(imapc->idstr), "A%03d", imapc->cmdid);
 
-  res = Curl_pp_vsendf(&imapc->pp, fmt, ap);
+  tagfmt = aprintf("%s %s", imapc->idstr, fmt);
+  if(!tagfmt)
+    return CURLE_OUT_OF_MEMORY;
 
+  result = Curl_pp_vsendf(&imapc->pp, tagfmt, ap);
+
+  free(tagfmt);
   va_end(ap);
 
-  return res;
-}
-
-static const char *getcmdid(struct connectdata *conn)
-{
-  static const char * const ids[]= {
-    "A",
-    "B",
-    "C",
-    "D"
-  };
-
-  struct imap_conn *imapc = &conn->proto.imapc;
-
-  /* Get the next id, but wrap at end of table */
-  imapc->cmdid = (int)((imapc->cmdid + 1) % (sizeof(ids) / sizeof(ids[0])));
-
-  return ids[imapc->cmdid];
+  return result;
 }
 
 /***********************************************************************
@@ -266,7 +260,7 @@ static char* imap_atom(const char* str)
   if(!str)
     return NULL;
 
-  /* Count any unescapped characters */
+  /* Count any unescaped characters */
   p1 = str;
   while(*p1) {
     if(*p1 == '\\')
@@ -319,44 +313,133 @@ static char* imap_atom(const char* str)
   return newstr;
 }
 
-/* Function that checks for an ending imap status code at the start of the
-   given string. */
+/* Portable way to determine if character is an ASCII digit. */
+static bool imap_isdigit(char ch)
+{
+  switch(ch) {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      return TRUE;
+    default:
+      return FALSE;
+  }
+}
+
+/* Checks whether a specified IMAP untagged response is of specified type,
+ * i.e. whether it is in format "* <type> ..." or "* <number> <type> ...".
+ * The "* " part is supposed to be already checked by the caller. */
+static bool imap_matchresp(const char* line, size_t len, const char* type)
+{
+  const char* end = line + len;
+  size_t type_len = strlen(type);
+  line += 2;
+
+  if(line < end && imap_isdigit(*line)) {
+    /* Line is probably in the "* <number> <type>" format. Skip the number
+       and space separator. */
+    do
+      line++;
+    while(line < end && imap_isdigit(*line));
+    if(line == end || *line != ' ')
+      return FALSE;
+    line++;
+  }
+
+  if(line + type_len <= end && !memcmp(line, type, type_len) &&
+     (line + type_len == end || line[type_len] == ' '))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* Function that checks whether the currently received response is meaningful
+ * in the current state. It determines whether the received line is a valid
+ * tagged, untagged or continuation response and processes it accordingly. */
 static int imap_endofresp(struct pingpong *pp, int *resp)
 {
   char *line = pp->linestart_resp;
-  size_t len = pp->nread_resp;
+  size_t len = strchr(line, '\n') - line + 1;
   struct imap_conn *imapc = &pp->conn->proto.imapc;
+  struct IMAP *imap = pp->conn->data->state.proto.imap;
   const char *id = imapc->idstr;
   size_t id_len = strlen(id);
 
-  /* Do we have a generic command response? */
-  if(len >= id_len + 3) {
-    if(!memcmp(id, line, id_len) && line[id_len] == ' ') {
-      *resp = line[id_len + 1]; /* O, N or B */
-      return TRUE;
+  if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
+    /* Tagged end of response */
+    if(len >= id_len + 4 && !memcmp(line + id_len + 1, "OK ", 3))
+      *resp = 'O';
+    else if(len >= id_len + 4 && !memcmp(line + id_len + 1, "NO ", 3))
+      *resp = 'N';
+    else if(len >= id_len + 5 && !memcmp(line + id_len + 1, "BAD ", 4))
+      *resp = 'B';
+    else
+      *resp = -1;
+    return TRUE;
+  }
+  else if(!memcmp("* ", line, 2)) {
+    /* Untagged intermediate response */
+    switch(imapc->state) {
+      case IMAP_FETCH:
+        /* For FETCH, we need to act upon the untagged response -
+           what follows it is directly the message body. */
+        if(imap_matchresp(line, len, "FETCH")) {
+          *resp = '*';
+          return TRUE;
+        }
+        break;
+
+      case IMAP_SELECT:
+        /* If doing a SELECT, see if this is an UIDVALIDITY response.
+           If it is, store the value for further use. */
+        if(imap_matchresp(line, len, "OK")) {
+          char tmp[20];
+          if(sscanf(line + 5, "[UIDVALIDITY %19[0123456789]]", tmp) == 1)
+            imapc->mailbox_uidvalidity = strdup(tmp);
+        }
+        break;
+
+      case IMAP_CUSTOM:
+        /* When dealing with a custom command, we are interested in all
+           intermediate responses which match the parameter name. The
+           exceptions are SELECT and EXAMINE commands, for which no
+           filtering is (or can be easily) done. */
+        if(!strcmp(imap->custom_command, "SELECT") ||
+           !strcmp(imap->custom_command, "EXAMINE") ||
+           imap_matchresp(line, len, imap->custom_command)) {
+          *resp = '*';
+          return TRUE;
+        }
+        break;
+
+      default: /* Avoid compiler warning */
+        break;
     }
   }
+  else if(!memcmp("+ ", line, 2)) {
+    /* Untagged command continuation request */
+    /* Interesting only as a response to the APPEND command. */
+    switch(imapc->state) {
+      case IMAP_APPEND:
+        *resp = '+';
+        return TRUE;
 
-  /* Are we processing FETCH command responses? */
-  if(imapc->state == IMAP_FETCH) {
-    /* Do we have a valid response? */
-    if(len >= 2 && !memcmp("* ", line, 2)) {
-      *resp = '*';
-      return TRUE;
+      default: /* Avoid compiler warning */
+        break;
     }
   }
 
-  return FALSE; /* nothing for us */
+  return FALSE; /* Nothing for us, maybe this should be an error */
 }
 
+
+
 /* This is the ONLY way to change IMAP state! */
-static void state(struct connectdata *conn,
-                  imapstate newstate)
+static void state(struct connectdata *conn, imapstate newstate)
 {
   struct imap_conn *imapc = &conn->proto.imapc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
-  static const char * const names[]={
+  static const char * const names[IMAP_LAST]={
     "STOP",
     "SERVERGREET",
     "STARTTLS",
@@ -364,7 +447,11 @@ static void state(struct connectdata *conn,
     "LOGIN",
     "SELECT",
     "FETCH",
+    "FETCH_DONE",
+    "APPEND",
+    "APPEND_DONE",
     "LOGOUT",
+    "CUSTOM"
     /* LAST */
   };
 
@@ -379,14 +466,14 @@ static void state(struct connectdata *conn,
 static CURLcode imap_state_login(struct connectdata *conn)
 {
   CURLcode result;
-  struct FTP *imap = conn->data->state.proto.imap;
-  const char *str = getcmdid(conn);
-  char *user = imap_atom(imap->user);
-  char *passwd = imap_atom(imap->passwd);
+  char *user = imap_atom(conn->user);
+  char *passwd = imap_atom(conn->passwd);
 
-  /* send USER and password */
-  result = imap_sendf(conn, str, "%s LOGIN %s %s", str,
-                      user ? user : "", passwd ? passwd : "");
+  if(!user || !passwd)
+    result = CURLE_OUT_OF_MEMORY;
+  else
+    /* Send USER and password */
+    result = imap_sendf(conn, "LOGIN %s %s", user, passwd);
 
   Curl_safefree(user);
   Curl_safefree(passwd);
@@ -424,21 +511,21 @@ static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
 
-  (void)instate; /* no use for this yet */
+  (void)instate; /* No use for this yet */
 
   if(imapcode != 'O') {
     failf(data, "Got unexpected imap-server response");
-    return CURLE_FTP_WEIRD_SERVER_REPLY;
+    return CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix the error code */
   }
 
   if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
-       to TLS connection now */
-    const char *str = getcmdid(conn);
-    result = imap_sendf(conn, str, "%s STARTTLS", str);
+    /* We don't have a SSL/TLS connection yet, but SSL is requested.
+       Switch to TLS connection now. */
+    result = imap_sendf(conn, "STARTTLS");
     state(conn, IMAP_STARTTLS);
   }
   else
+    /* Otherwise continue straight to login. */
     result = imap_state_login(conn);
 
   return result;
@@ -452,7 +539,7 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
 
-  (void)instate; /* no use for this yet */
+  (void)instate; /* No use for this yet */
 
   if(imapcode != 'O') {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
@@ -502,7 +589,7 @@ static CURLcode imap_state_login_resp(struct connectdata *conn,
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
 
-  (void)instate; /* no use for this yet */
+  (void)instate; /* No use for this yet */
 
   if(imapcode != 'O') {
     failf(data, "Access denied. %c", imapcode);
@@ -515,45 +602,55 @@ static CURLcode imap_state_login_resp(struct connectdata *conn,
   return result;
 }
 
-/* Start the DO phase */
-static CURLcode imap_select(struct connectdata *conn)
+/* Initiates a SELECT operation to open a mailbox. In case no mailbox is to
+ * be selected or the connection has already opened this one, skips directly
+ * to the next operation. */
+static CURLcode imap_perform_select(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
-  const char *str = getcmdid(conn);
+  struct IMAP *imap = conn->data->state.proto.imap;
+  char* escaped;
 
-  result = imap_sendf(conn, str, "%s SELECT %s", str,
-                      imapc->mailbox?imapc->mailbox:"");
-  if(result)
-    return result;
+  if(imapc->mailbox && !strcmp(imap->mailbox, imapc->mailbox))
+    /* No mailbox to use or we are already in this one - go straight to
+       the next real operation. */
+    result = imap_after_select(conn);
+  else {
+    /* We need to switch mailboxes. Invalidate old information. */
+    Curl_safefree(imapc->mailbox);
+    imapc->mailbox = NULL;
+    Curl_safefree(imapc->mailbox_uidvalidity);
+    imapc->mailbox_uidvalidity = NULL;
 
-  state(conn, IMAP_SELECT);
+    escaped = imap_atom(imap->mailbox);
+    if(!escaped)
+      result = CURLE_OUT_OF_MEMORY;
+    else
+      result = imap_sendf(conn, "SELECT %s", escaped);
+    Curl_safefree(escaped);
+    if(result)
+      return result;
+
+    state(conn, IMAP_SELECT);
+  }
 
   return result;
 }
 
-static CURLcode imap_fetch(struct connectdata *conn)
+/* Initates a FETCH command to download a message. */
+static CURLcode imap_perform_fetch(struct connectdata *conn)
 {
-  CURLcode result = CURLE_OK;
-  const char *str = getcmdid(conn);
+  CURLcode result;
+  struct IMAP *imap = conn->data->state.proto.imap;
 
-  /* TODO: make this select the correct mail
-   * Use "1 body[text]" to get the full mail body of mail 1
-   */
-  result = imap_sendf(conn, str, "%s FETCH 1 BODY[TEXT]", str);
+  result = imap_sendf(conn, "FETCH %s BODY[%s]",
+                      imap->uid ? imap->uid : "1",
+                      imap->section ? imap->section : "");
   if(result)
     return result;
 
-  /*
-   * When issued, the server will respond with a single line similar to
-   * '* 1 FETCH (BODY[TEXT] {2021}'
-   *
-   * Identifying the fetch and how many bytes of contents we can expect. We
-   * must extract that number before continuing to "download as usual".
-   */
-
   state(conn, IMAP_FETCH);
-
   return result;
 }
 
@@ -564,20 +661,50 @@ static CURLcode imap_state_select_resp(struct connectdata *conn,
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct IMAP *imap = data->state.proto.imap;
 
-  (void)instate; /* no use for this yet */
+  (void)instate; /* No use for this yet */
 
   if(imapcode != 'O') {
-    failf(data, "Select failed");
-    result = CURLE_LOGIN_DENIED;
+    failf(data, "SELECT failed");
+    return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
   }
+
+  /* Note the currently opened mailbox on this connection. */
+  imapc->mailbox = strdup(imap->mailbox);
+
+  result = imap_after_select(conn);
+  return result;
+}
+
+/* Called after a successful SELECT or, in case no mailbox path was specified
+ * in the URL or the connection has already opened this identical mailbox,
+ * straight after connecting. Selects what to do next depending on the values
+ * in the per-request structure. */
+static CURLcode imap_after_select(struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct imap_conn *imapc = &conn->proto.imapc;
+  struct IMAP *imap = conn->data->state.proto.imap;
+
+  /* Check UIDVALIDITY if used and supported. */
+  if(imap->uidvalidity && imapc->mailbox_uidvalidity) {
+    if(strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
+      failf(conn->data, "Mailbox UIDVALIDITY has changed");
+      return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
+    }
+  }
+
+  if(imap->custom_command)
+    result = imap_perform_custom(conn);
   else
-    result = imap_fetch(conn);
+    result = imap_perform_fetch(conn);
 
   return result;
 }
 
-/* For the (first line of) FETCH BODY[TEXT] response */
+/* For the (first line of) FETCH response */
 static CURLcode imap_state_fetch_resp(struct connectdata *conn,
                                       int imapcode,
                                       imapstate instate)
@@ -585,55 +712,204 @@ static CURLcode imap_state_fetch_resp(struct connectdata *conn,
   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;
+  bool parsed = FALSE;
+  curl_off_t size;
 
-  (void)instate; /* no use for this yet */
+  (void)instate; /* No use for this yet */
 
   if('*' != imapcode) {
-    Curl_pgrsSetDownloadSize(data, 0);
     state(conn, IMAP_STOP);
-    return CURLE_OK;
+    return CURLE_RECV_ERROR; /* TODO: Fix the error code. */
   }
 
-  /* Something like this comes "* 1 FETCH (BODY[TEXT] {2021}\r" */
+
+  /* Something like this comes in: "* 1 FETCH (BODY[TEXT] {2021}\r".
+     Parse out the download length in curly brackets. */
   while(*ptr && (*ptr != '{'))
     ptr++;
-
   if(*ptr == '{') {
-    curl_off_t filesize = curlx_strtoofft(ptr + 1, NULL, 10);
-    if(filesize)
-      Curl_pgrsSetDownloadSize(data, filesize);
+    char* endptr;
+    size = curlx_strtoofft(ptr + 1, &endptr, 10);
+    if(endptr - ptr > 1 && endptr[0] == '}' && endptr[1] == '\r')
+      parsed = TRUE;
+  }
+  if(!parsed) {
+    failf(pp->conn->data, "Failed to parse FETCH response.\n");
+    return CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix the error code. */
+  }
 
-    infof(data, "Found %" FORMAT_OFF_TU " bytes to download\n", filesize);
 
-    /* At this point there might be a bunch of data in the header "cache" that
-       is actually body content. Call the PP function which sends it to the
-       library user as body and skips it. Do note that there may even be
-       additional responses ("headers") after the body. */
-    result = Curl_pp_extractbody(pp, &filesize);
-    if(result)
-      return result;
+  infof(data, "%" FORMAT_OFF_TU " bytes to download\n", size);
+  Curl_pgrsSetDownloadSize(data, size);
 
-    infof(data, "Filesize left: %" FORMAT_OFF_T "\n", filesize);
+  /* At this point there might be a bunch of data in the header "cache" that
+     is actually body content. Call the PP function which sends it to the
+     library user as body and skips it. Do note that there may even be
+     additional responses ("headers") after the body. */
+  result = Curl_pp_extractbody(pp, &size);
+  if(result)
+    return result;
 
-    if(!filesize)
-      /* the entire data is already transferred! */
-      Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  infof(data, "Filesize left: %" FORMAT_OFF_T "\n", size);
+
+  if(size == 0)
+    /* The entire data is already transferred! */
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  else
+    /* IMAP download */
+    Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE,
+                        NULL, -1, NULL); /* No upload here */
+
+  /* Stop now and let the core go from DO to PERFORM phase. */
+  state(conn, IMAP_STOP);
+
+  return result;
+}
+
+/* Handles final response to the FETCH command */
+static CURLcode imap_state_fetch_done_resp(struct connectdata *conn,
+                                           int imapcode,
+                                           imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+
+  (void)instate; /* No use for this yet */
+
+  /* Final response. Stop and return the final status. */
+  if('O' == imapcode)
+    result = CURLE_OK;
+  else
+    result = CURLE_RECV_ERROR; /* TODO: Fix error code. */
+  state(conn, IMAP_STOP);
+
+  return result;
+}
+
+/* Initates a APPEND command to upload a message. */
+static CURLcode imap_perform_append(struct connectdata *conn)
+{
+  CURLcode result;
+  struct IMAP *imap = conn->data->state.proto.imap;
+
+  if(conn->data->set.infilesize < 0) {
+    failf(conn->data, "Cannot APPEND with unknown input file size\n");
+    return CURLE_UPLOAD_FAILED;
+  }
+
+  result = imap_sendf(conn, "APPEND %s (\\Seen) {%" FORMAT_OFF_T "}",
+                      imap->mailbox, conn->data->set.infilesize);
+  if(result)
+    return result;
+
+  state(conn, IMAP_APPEND);
+  return result;
+}
+
+/* Parse responses to the APPEND command. */
+static CURLcode imap_state_append_resp(struct connectdata *conn,
+                                       int imapcode,
+                                       imapstate instate)
+{
+  struct SessionHandle *data = conn->data;
+
+  (void)instate; /* No use for this yet */
+
+  if(imapcode == '+') {
+    Curl_pgrsSetUploadSize(data, data->set.infilesize);
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* No download */
+                        FIRSTSOCKET, NULL);
+
+    /* Stop now and let the core go from DO to PERFORM phase. */
+    state(conn, IMAP_STOP);
+    return CURLE_OK;
+  }
+  else {
+    state(conn, IMAP_STOP);
+    return CURLE_UPLOAD_FAILED;
+  }
+}
+
+/* Handles final response to the FETCH command */
+static CURLcode imap_state_append_done_resp(struct connectdata *conn,
+                                            int imapcode,
+                                            imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+
+  (void)instate; /* No use for this yet */
+
+  /* Final response. Stop and return the final status. */
+  if('O' == imapcode)
+    result = CURLE_OK;
+  else
+    result = CURLE_RECV_ERROR; /* TODO: Fix error code. */
+  state(conn, IMAP_STOP);
+
+  return result;
+}
+
+/* Initiates a custom command as specified by the user. Effectively just
+ * sends it as it is to the server. */
+static CURLcode imap_perform_custom(struct connectdata *conn)
+{
+  struct IMAP *imap = conn->data->state.proto.imap;
+
+  CURLcode result = imap_sendf(conn, "%s%s",
+                               imap->custom_command, imap->custom_params);
+  if(result)
+    return result;
+
+  /* We don't know how much data will be received. */
+  Curl_pgrsSetDownloadSize(conn->data, -1);
+
+  /* Our imap_endofresp() will forward to the client any untagged responses
+     which are in form "* <command> ..." or "* <number> <command> ...", where
+     <command> is imap->custom_command. This works well for almost all
+     commands except STORE, which returns FETCH responses. That's why there
+     needs to be the following hack. */
+  if(strcmp(imap->custom_command, "STORE") == 0) {
+    free(imap->custom_command);
+    imap->custom_command = strdup("FETCH");
+    if(!imap->custom_command)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  state(conn, IMAP_CUSTOM);
+  return result;
+}
+
+/* Parses responses to a custom command. Handles only the final response -
+ * intermediate data is forwarded to the client directly in imap_endofresp(),
+ * where it is still available in the pingpong line buffer. */
+static CURLcode imap_state_custom_resp(struct connectdata *conn,
+                                       int imapcode,
+                                       imapstate instate)
+{
+  CURLcode result = CURLE_OK;
+  char *line = conn->data->state.buffer;
+  size_t len = strlen(line);
+
+  (void)instate; /* No use for this yet */
+
+  if('*' == imapcode) {
+    /* The client which asked for this custom command should know best
+       how to cope with the result, just send it as body. Add back the
+       LF character when saving. */
+    line[len] = '\n';
+    result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
+    line[len] = '\0';
+  }
+  else {
+    /* Final response. Stop and return the final status. */
+      if('O' == imapcode)
+      result = CURLE_OK;
     else
-      /* IMAP download */
-      Curl_setup_transfer(conn, FIRSTSOCKET, filesize, FALSE,
-                          imap->bytecountp, -1, NULL); /* no upload here */
+      result = CURLE_QUOTE_ERROR; /* TODO: Fix error code. */
 
-    data->req.maxdownload = filesize;
+    state(conn, IMAP_STOP);
   }
-  else
-    /* We don't know how to parse this line */
-    result = CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: fix this code */
-
-  /* End of do phase */
-  state(conn, IMAP_STOP);
 
   return result;
 }
@@ -660,44 +936,62 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
   if(result)
     return result;
 
-  if(imapcode) {
+  if(imapcode == -1)
+    return CURLE_FTP_WEIRD_SERVER_REPLY; /* TODO: Fix error code. */
+
+  if(imapcode)
     /* We have now received a full IMAP server response */
     switch(imapc->state) {
-    case IMAP_SERVERGREET:
-      result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_STARTTLS:
-      result = imap_state_starttls_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_LOGIN:
-      result = imap_state_login_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_FETCH:
-      result = imap_state_fetch_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_SELECT:
-      result = imap_state_select_resp(conn, imapcode, imapc->state);
-      break;
-
-    case IMAP_LOGOUT:
-      /* fallthrough, just stop! */
-    default:
-      /* internal error */
-      state(conn, IMAP_STOP);
-      break;
+      case IMAP_SERVERGREET:
+        result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_STARTTLS:
+        result = imap_state_starttls_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_LOGIN:
+        result = imap_state_login_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_FETCH:
+        result = imap_state_fetch_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_FETCH_DONE:
+        result = imap_state_fetch_done_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_APPEND:
+        result = imap_state_append_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_APPEND_DONE:
+        result = imap_state_append_done_resp(conn, imapcode, imapc->state);
+        break;
+
+      case IMAP_SELECT:
+        result = imap_state_select_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:
+        /* Internal error */
+        state(conn, IMAP_STOP);
+        break;
     }
-  }
 
   return result;
 }
 
 /* Called repeatedly until done from curl_multi.c */
-static CURLcode imap_multi_statemach(struct connectdata *conn,
-                                         bool *done)
+static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
 {
   struct imap_conn *imapc = &conn->proto.imapc;
   CURLcode result;
@@ -728,54 +1022,46 @@ static CURLcode imap_easy_statemach(struct connectdata *conn)
 }
 
 /* Allocate and initialize the struct IMAP for the current SessionHandle if
-   required */
+   required. */
 static CURLcode imap_init(struct connectdata *conn)
 {
   struct SessionHandle *data = conn->data;
-  struct FTP *imap = data->state.proto.imap;
+  struct IMAP *imap = data->state.proto.imap;
 
   if(!imap) {
-    imap = data->state.proto.imap = calloc(sizeof(struct FTP), 1);
+    imap = data->state.proto.imap = calloc(sizeof(struct IMAP), 1);
     if(!imap)
       return CURLE_OUT_OF_MEMORY;
   }
 
-  /* Get some initial data into the imap struct */
-  imap->bytecountp = &data->req.bytecount;
-
-  /* No need to duplicate user+password, the connectdata struct won't change
-     during a session, but we re-init them here since on subsequent inits
-     since the conn struct may have changed or been replaced.
-  */
-  imap->user = conn->user;
-  imap->passwd = conn->passwd;
-
   return CURLE_OK;
 }
 
 /***********************************************************************
  *
- * imap_connect() should do everything that is to be considered a part of
- * the connection phase.
+ * imap_connect()
+ *
+ * Should do everything that is to be considered a part of the connection
+ * phase.
  *
  * The variable 'done' points to will be TRUE if the protocol-layer connect
  * phase is done when this function returns, or FALSE is not. When called as
  * a part of the easy interface, it will always be TRUE.
+ *
  */
-static CURLcode imap_connect(struct connectdata *conn,
-                                 bool *done) /* see description above */
+static CURLcode imap_connect(struct connectdata *conn, bool *done)
 {
   CURLcode result;
   struct imap_conn *imapc = &conn->proto.imapc;
-  struct SessionHandle *data=conn->data;
+  struct SessionHandle *data = conn->data;
   struct pingpong *pp = &imapc->pp;
 
   *done = FALSE; /* default to not done yet */
 
-  /* If there already is a protocol-specific struct allocated for this
-     sessionhandle, deal with it */
+  /* Since connections can be re-used between SessionHandles, this might be a
+     connection already existing but on a fresh SessionHandle struct so we must
+     make sure we have a good 'struct IMAP' to play with. */
   Curl_reset_reqproto(conn);
-
   result = imap_init(conn);
   if(CURLE_OK != result)
     return result;
@@ -783,28 +1069,25 @@ static CURLcode imap_connect(struct connectdata *conn,
   /* We always support persistent connections on imap */
   conn->bits.close = FALSE;
 
-  pp->response_time = RESP_TIMEOUT; /* set default response time-out */
-  pp->statemach_act = imap_statemach_act;
-  pp->endofresp = imap_endofresp;
-  pp->conn = conn;
-
   if((conn->handler->flags & PROTOPT_SSL) &&
      data->state.used_interface != Curl_if_multi) {
-    /* IMAPS is simply imap with SSL for the control channel */
-    /* so perform the SSL initialization for this socket */
+    /* IMAPS is simply IMAP with SSL for the control channel.
+       Perform the SSL handshake using easy interface - BLOCKING. */
     result = Curl_ssl_connect(conn, FIRSTSOCKET);
     if(result)
       return result;
   }
 
-  /* Initialise the response reader stuff */
+  /* Initialise the response reader pingpong interface */
+  pp->response_time = RESP_TIMEOUT; /* Set default response time-out */
+  pp->statemach_act = imap_statemach_act;
+  pp->endofresp = imap_endofresp;
+  pp->conn = conn;
   Curl_pp_init(pp);
 
   /* Start off waiting for the server greeting response */
   state(conn, IMAP_SERVERGREET);
-
-  /* Start off with an id of '*' */
-  imapc->idstr = "*";
+  strcpy(imapc->idstr, "*"); /* Wait for an untagged response */
 
   if(data->state.used_interface == Curl_if_multi)
     result = imap_multi_statemach(conn, done);
@@ -825,31 +1108,66 @@ static CURLcode imap_connect(struct connectdata *conn,
  * performed.
  *
  * Input argument is already checked for validity.
+ *
  */
 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
                           bool premature)
 {
+  CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
-  struct FTP *imap = data->state.proto.imap;
-  CURLcode result=CURLE_OK;
+  struct IMAP *imap = data->state.proto.imap;
+  bool custom_command = (imap->custom_command != NULL);
 
   (void)premature;
 
   if(!imap)
     /* When the easy handle is removed from the multi while libcurl is still
-     * trying to resolve the host name, it seems that the imap struct is not
-     * yet initialized, but the removal action calls Curl_done() which calls
-     * this function. So we simply return success if no imap pointer is set.
-     */
+       trying to resolve the host name, it seems that the imap struct is not
+       yet initialized, but the removal action calls Curl_done() which calls
+       this function. So we simply return success if no imap pointer is set. */
     return CURLE_OK;
 
-  if(status) {
+  /* Release possibly allocated per-request information. */
+  Curl_safefree(imap->mailbox);
+  imap->mailbox = NULL;
+  Curl_safefree(imap->uidvalidity);
+  imap->uidvalidity = NULL;
+  Curl_safefree(imap->uid);
+  imap->uid = NULL;
+  Curl_safefree(imap->section);
+  imap->section = NULL;
+  Curl_safefree(imap->custom_command);
+  imap->custom_command = NULL;
+  Curl_safefree(imap->custom_params);
+  imap->custom_params = NULL;
+
+  if(status != CURLE_OK) {
     conn->bits.close = TRUE; /* marked for closure */
     result = status;         /* use the already set error code */
   }
+  else if(!data->set.connect_only) {
+    if(!custom_command) {
+      /* Handle responses after FETCH or APPEND transfer has finished. */
+      if(!data->set.upload)
+        state(conn, IMAP_FETCH_DONE);
+      else {
+        /* End the APPEND command first - send empty line with just CRLF. */
+        va_list dummy;
+        result = Curl_pp_vsendf(&conn->proto.imapc.pp, "", dummy);
+        if(result == CURLE_OK)
+          state(conn, IMAP_APPEND_DONE);
+      }
+    }
 
-  /* Clear the transfer mode for the next connection */
-  imap->transfer = FTPTRANSFER_BODY;
+    /* Run the state-machine
+
+       TODO: when the multi interface is used, this _really_ should be using
+       the imap_multi_statemach function but we have no general support for
+       non-blocking DONE operations, not in the multi state machine and with
+       Curl_done() invokes on several places in the code! */
+    if(result == CURLE_OK)
+      result = imap_easy_statemach(conn);
+  }
 
   return result;
 }
@@ -858,27 +1176,30 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
  *
  * imap_perform()
  *
- * This is the actual DO function for IMAP. Get a file/directory according to
- * the options previously setup.
+ * This is the actual DO function for IMAP. Retrieve/upload a message or
+ * do other things according to the options previously setup.
+ *
  */
-static CURLcode imap_perform(struct connectdata *conn, bool *connected,
-                             bool *dophase_done)
+static CURLcode imap_perform(struct connectdata *conn, bool *dophase_done)
 {
-  /* This is IMAP and no proxy */
   CURLcode result = CURLE_OK;
+  struct IMAP *imap = conn->data->state.proto.imap;
 
   DEBUGF(infof(conn->data, "DO phase starts\n"));
-
-  if(conn->data->set.opt_no_body) {
-    /* Requested no body means no transfer */
-    struct FTP *imap = conn->data->state.proto.imap;
-    imap->transfer = FTPTRANSFER_INFO;
-  }
-
   *dophase_done = FALSE; /* not done yet */
 
-  /* Start the first command in the DO phase */
-  result = imap_select(conn);
+  /* Start the first command in the DO phase. */
+  if(conn->data->set.upload)
+    /* APPEND doesn't need a SELECTed mailbox. */
+    result = imap_perform_append(conn);
+  else if(imap->custom_command && !imap->mailbox)
+    /* No mailbox was specified for a custom request, assume the user knows
+     * what they are doing and the command does not need a mailbox. */
+    result = imap_perform_custom(conn);
+  else
+    /* Otherwise always start with SELECT (although it won't be performed
+     * if the same mailbox has been already selected on this connection). */
+    result = imap_perform_select(conn);
   if(result)
     return result;
 
@@ -887,9 +1208,8 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
     result = imap_multi_statemach(conn, dophase_done);
   else {
     result = imap_easy_statemach(conn);
-    *dophase_done = TRUE; /* with the easy interface we are done here */
+    *dophase_done = TRUE; /* With the easy interface we are done here */
   }
-  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
     DEBUGF(infof(conn->data, "DO phase is complete\n"));
@@ -905,56 +1225,100 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
  * parts etc as a wrapper to the actual DO function (imap_perform).
  *
  * The input argument is already checked for validity.
+ *
  */
 static CURLcode imap_do(struct connectdata *conn, bool *done)
 {
-  CURLcode retcode = CURLE_OK;
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  struct IMAP *imap;
 
   *done = FALSE; /* default to false */
 
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct IMAP' to play with. For new connections,
-    the struct IMAP is allocated and setup in the imap_connect() function.
-  */
+  /* Since connections can be re-used between SessionHandles, this might be a
+     connection already existing but on a fresh SessionHandle struct so we must
+     make sure we have a good 'struct IMAP' to play with. For new connections,
+     the struct IMAP is allocated and setup in the imap_connect() function. */
   Curl_reset_reqproto(conn);
-  retcode = imap_init(conn);
-  if(retcode)
-    return retcode;
+  result = imap_init(conn);
+  if(result)
+    return result;
+  imap = data->state.proto.imap;
 
-  /* Parse the URL path */
-  retcode = imap_parse_url_path(conn);
-  if(retcode)
-    return retcode;
 
-  retcode = imap_regular_transfer(conn, done);
+  /* Parse the URL path into 'mailbox' and other variables like 'uidvalidity'
+     or 'uid' in the per-request IMAP structure (data->state.proto.imap). */
+  result = imap_parse_url_path(conn);
+  if(result)
+    return result;
 
-  return retcode;
+
+  /* If a custom command was specified, we don't need to understand it, but we
+     have to at least parse the command name (first word), so that untagged
+     responses from the server to this command can be detected. */
+  if(data->set.str[STRING_CUSTOMREQUEST]) {
+    const char* req = data->set.str[STRING_CUSTOMREQUEST];
+    const char* params = req;
+    while(*params && *params != ' ')
+      params++;
+
+    imap->custom_command = malloc(params - req + 1);
+    if(!imap->custom_command)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(imap->custom_command, req, params - req);
+    imap->custom_command[params - req] = '\0';
+
+    imap->custom_params = strdup(params);
+    if(!imap->custom_params)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+
+  if(!imap->custom_command && !imap->mailbox) {
+    failf(data, "IMAP FETCH/APPEND needs a mailbox name.\n");
+    return CURLE_URL_MALFORMAT;
+  }
+
+
+  data->req.size = -1; /* make sure this is unknown at this point */
+  Curl_pgrsSetUploadCounter(data, 0);
+  Curl_pgrsSetDownloadCounter(data, 0);
+  Curl_pgrsSetUploadSize(data, 0);
+  Curl_pgrsSetDownloadSize(data, 0);
+
+
+  result = imap_perform(conn, done);
+
+
+  if(CURLE_OK == result) {
+    if(!*done)
+      /* The DO phase has not completed yet */
+      return CURLE_OK;
+  }
+
+  return result;
 }
 
 /***********************************************************************
  *
- * imap_logout()
+ * imap_perform_logout()
  *
  * This should be called before calling sclose().  We should then wait for the
  * response from the server before returning. The calling code should then try
  * to close the connection.
  *
  */
-static CURLcode imap_logout(struct connectdata *conn)
+static CURLcode imap_perform_logout(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
-  const char *str = getcmdid(conn);
 
-  result = imap_sendf(conn, str, "%s LOGOUT", str, NULL);
+  result = imap_sendf(conn, "LOGOUT");
   if(result)
     return result;
 
   state(conn, IMAP_LOGOUT);
 
   result = imap_easy_statemach(conn);
-
   return result;
 }
 
@@ -964,10 +1328,11 @@ static CURLcode imap_logout(struct connectdata *conn)
  *
  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
  * resources. BLOCKING.
+ *
  */
 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
 {
-  struct imap_conn *imapc= &conn->proto.imapc;
+  struct imap_conn *imapc = &conn->proto.imapc;
 
   /* We cannot send quit unconditionally. If this connection is stale or
      bad in any way, sending quit and waiting around here will make the
@@ -976,48 +1341,158 @@ static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
   /* The IMAP session may or may not have been allocated/setup at this
      point! */
   if(!dead_connection && imapc->pp.conn)
-    (void)imap_logout(conn); /* ignore errors on the LOGOUT */
+    (void)imap_perform_logout(conn); /* ignore errors on the LOGOUT */
 
   /* Disconnect from the server */
   Curl_pp_disconnect(&imapc->pp);
 
-  /* Cleanup our connection based variables */
+  /* Cleanup our possibly allocated per-connection information */
   Curl_safefree(imapc->mailbox);
+  imapc->mailbox = NULL;
+  Curl_safefree(imapc->mailbox_uidvalidity);
+  imapc->mailbox_uidvalidity = NULL;
 
   return CURLE_OK;
 }
 
 /***********************************************************************
  *
+ * imap_is_bchar()
+ *
+ * Portable test whether specified char is a "bchar" as defined in the
+ * grammar of RFC 5092 (IMAP URL Scheme).
+ * Used by imap_parse_url_path() below.
+ *
+ */
+static bool imap_is_bchar(char ch)
+{
+  switch (ch) {
+    /* bchar */
+    case ':': case '@': case '/':
+    /* bchar -> achar */
+    case '&': case '=':
+    /* bchar -> achar -> uchar -> unreserved */
+    case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+    case '7': case '8': case '9':
+    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+    case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+    case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+    case 'V': case 'W': case 'X': case 'Y': case 'Z':
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+    case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+    case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+    case 'v': case 'w': case 'x': case 'y': case 'z':
+    case '-': case '.': case '_': case '~':
+    /* bchar -> achar -> uchar -> sub-delims-sh */
+    case '!': case '$': case '\'': case '(': case ')': case '*':
+    case '+': case ',':
+    /* bchar -> achar -> uchar -> pct-encoded */
+    case '%': /* HEXDIG chars are already included above */
+      return true;
+    default:
+      return false;
+  }
+}
+
+/***********************************************************************
+ *
  * imap_parse_url_path()
  *
- * Parse the URL path into separate path components.
+ * Parse the IMAP URL from SessionHandle.state.path into folder or
+ * message path and other components and fill the information into
+ * related fields in per-request SessionHandle.state.proto.imap struct.
  *
  */
 static CURLcode imap_parse_url_path(struct connectdata *conn)
 {
-  /* The imap struct is already inited in imap_connect() */
-  struct imap_conn *imapc = &conn->proto.imapc;
   struct SessionHandle *data = conn->data;
-  const char *path = data->state.path;
+  struct IMAP *imap = data->state.proto.imap;
+  const char *begin;
+  const char *ptr;
+  CURLcode result;
 
-  if(!*path)
-    path = "INBOX";
+  /* First see how much of the URL is a valid valid path
+     and decode it. Skip the initial slash. */
+  begin = data->state.path + 1;
+  ptr = begin;
+  while(imap_is_bchar(*ptr))
+    ptr++;
+  if(ptr != begin) {
+    /* Also remove the trailing slash if present, */
+    const char* end = ptr;
+    if(end > begin && end[-1] == '/')
+      end--;
+    result = Curl_urldecode(data, begin, end - begin,
+                            &imap->mailbox, NULL, TRUE);
+    if(CURLE_OK != result)
+      return result;
+  }
+  else {
+    imap->mailbox = NULL;
+  }
 
-  /* URL decode the path and use this mailbox */
-  return Curl_urldecode(data, path, 0, &imapc->mailbox, NULL, TRUE);
-}
+  /* Next there can be any number of parameters in the form ";NAME=VALUE" */
+  while(*ptr == ';') {
+    char *name;
+    char *value;
+    size_t valuelen;
 
-/* Call this when the DO phase has completed */
-static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
-{
-  struct FTP *imap = conn->data->state.proto.imap;
+    /* Decode the parameter name */
+    begin = ++ptr;
+    while(*ptr && *ptr != '=')
+      ptr++;
+    if(!*ptr)
+      return CURLE_URL_MALFORMAT;
+    result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
+    if(CURLE_OK != result)
+      return result;
 
-  (void)connected;
+    /* Decode the parameter value */
+    begin = ++ptr;
+    while(imap_is_bchar(*ptr))
+      ptr++;
+    result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
+    if(CURLE_OK != result) {
+      free(name);
+      return result;
+    }
 
-  if(imap->transfer != FTPTRANSFER_BODY)
-    /* no data to transfer */
-    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
+
+    /* Process known parameters. Unknown ones trigger URL_MALFORMAT error.
+       UIDVALIDITY, UID and SECTION create another virtual URL level, so they
+       should be followed by a slash, which needs to be stripped. */
+    if(Curl_raw_equal(name, "UIDVALIDITY") && !imap->uidvalidity) {
+      if(valuelen > 0 && value[valuelen - 1] == '/')
+        value[valuelen - 1] = '\0';
+      imap->uidvalidity = value;
+      value = NULL;
+    }
+    else if(Curl_raw_equal(name, "UID") && !imap->uid) {
+      if(valuelen > 0 && value[valuelen - 1] == '/')
+        value[valuelen - 1] = '\0';
+      imap->uid = value;
+      value = NULL;
+    }
+    else if(Curl_raw_equal(name, "SECTION") && !imap->section) {
+      if(valuelen > 0 && value[valuelen - 1] == '/')
+        value[valuelen - 1] = '\0';
+      imap->section = value;
+      value = NULL;
+    }
+    else {
+      Curl_safefree(name);
+      Curl_safefree(value);
+      return CURLE_URL_MALFORMAT;
+    }
+
+    Curl_safefree(name);
+    Curl_safefree(value);
+  }
+
+  /* Any extra stuff we didn't know how to process is an error. */
+  if(*ptr)
+    return CURLE_URL_MALFORMAT;
 
   return CURLE_OK;
 }
@@ -1030,56 +1505,14 @@ static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
   if(result)
     DEBUGF(infof(conn->data, "DO phase failed\n"));
   else {
-    if(*dophase_done) {
-      result = imap_dophase_done(conn, FALSE /* not connected */);
-
+    if(*dophase_done)
       DEBUGF(infof(conn->data, "DO phase is complete\n"));
-    }
   }
 
   return result;
 }
 
-/***********************************************************************
- *
- * imap_regular_transfer()
- *
- * The input argument is already checked for validity.
- *
- * Performs all commands done before a regular transfer between a local and a
- * remote host.
- */
-static CURLcode imap_regular_transfer(struct connectdata *conn,
-                                      bool *dophase_done)
-{
-  CURLcode result = CURLE_OK;
-  bool connected = FALSE;
-  struct SessionHandle *data = conn->data;
-
-  /* Make sure size is unknown at this point */
-  data->req.size = -1;
-
-  Curl_pgrsSetUploadCounter(data, 0);
-  Curl_pgrsSetDownloadCounter(data, 0);
-  Curl_pgrsSetUploadSize(data, 0);
-  Curl_pgrsSetDownloadSize(data, 0);
-
-  result = imap_perform(conn, &connected, dophase_done);
-
-  if(CURLE_OK == result) {
-    if(!*dophase_done)
-      /* The DO phase has not completed yet */
-      return CURLE_OK;
-
-    result = imap_dophase_done(conn, connected);
-    if(result)
-      return result;
-  }
-
-  return result;
-}
-
-static CURLcode imap_setup_connection(struct connectdata * conn)
+static CURLcode imap_setup_connection(struct connectdata *conn)
 {
   struct SessionHandle *data = conn->data;
 
@@ -1097,10 +1530,9 @@ static CURLcode imap_setup_connection(struct connectdata * conn)
       return CURLE_UNSUPPORTED_PROTOCOL;
 #endif
     }
-
     /* We explicitly mark this connection as persistent here as we're doing
        IMAP over HTTP and thus we accidentally avoid setting this value
-       otherwise */
+       otherwise. */
     conn->bits.close = FALSE;
 #else
     failf(data, "IMAP over http proxy requires HTTP support built-in!");
@@ -1108,8 +1540,6 @@ static CURLcode imap_setup_connection(struct connectdata * conn)
 #endif
   }
 
-  data->state.path++;   /* don't include the initial slash */
-
   return CURLE_OK;
 }
 
