imap.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC2195 CRAM-MD5 authentication
00022  * RFC2595 Using TLS with IMAP, POP3 and ACAP
00023  * RFC2831 DIGEST-MD5 authentication
00024  * RFC3501 IMAPv4 protocol
00025  * RFC4422 Simple Authentication and Security Layer (SASL)
00026  * RFC4616 PLAIN authentication
00027  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
00028  * RFC4959 IMAP Extension for SASL Initial Client Response
00029  * RFC5092 IMAP URL Scheme
00030  * RFC6749 OAuth 2.0 Authorization Framework
00031  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
00032  *
00033  ***************************************************************************/
00034 
00035 #include "curl_setup.h"
00036 
00037 #ifndef CURL_DISABLE_IMAP
00038 
00039 #ifdef HAVE_NETINET_IN_H
00040 #include <netinet/in.h>
00041 #endif
00042 #ifdef HAVE_ARPA_INET_H
00043 #include <arpa/inet.h>
00044 #endif
00045 #ifdef HAVE_UTSNAME_H
00046 #include <sys/utsname.h>
00047 #endif
00048 #ifdef HAVE_NETDB_H
00049 #include <netdb.h>
00050 #endif
00051 #ifdef __VMS
00052 #include <in.h>
00053 #include <inet.h>
00054 #endif
00055 
00056 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
00057 #undef in_addr_t
00058 #define in_addr_t unsigned long
00059 #endif
00060 
00061 #include <curl/curl.h>
00062 #include "urldata.h"
00063 #include "sendf.h"
00064 #include "hostip.h"
00065 #include "progress.h"
00066 #include "transfer.h"
00067 #include "escape.h"
00068 #include "http.h" /* for HTTP proxy tunnel stuff */
00069 #include "socks.h"
00070 #include "imap.h"
00071 #include "strtoofft.h"
00072 #include "strcase.h"
00073 #include "vtls/vtls.h"
00074 #include "connect.h"
00075 #include "strerror.h"
00076 #include "select.h"
00077 #include "multiif.h"
00078 #include "url.h"
00079 #include "strcase.h"
00080 #include "curl_sasl.h"
00081 #include "warnless.h"
00082 
00083 /* The last 3 #include files should be in this order */
00084 #include "curl_printf.h"
00085 #include "curl_memory.h"
00086 #include "memdebug.h"
00087 
00088 /* Local API functions */
00089 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
00090 static CURLcode imap_do(struct connectdata *conn, bool *done);
00091 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
00092                           bool premature);
00093 static CURLcode imap_connect(struct connectdata *conn, bool *done);
00094 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
00095 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
00096 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
00097                         int numsocks);
00098 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
00099 static CURLcode imap_setup_connection(struct connectdata *conn);
00100 static char *imap_atom(const char *str, bool escape_only);
00101 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
00102 static CURLcode imap_parse_url_options(struct connectdata *conn);
00103 static CURLcode imap_parse_url_path(struct connectdata *conn);
00104 static CURLcode imap_parse_custom_request(struct connectdata *conn);
00105 static CURLcode imap_perform_authenticate(struct connectdata *conn,
00106                                           const char *mech,
00107                                           const char *initresp);
00108 static CURLcode imap_continue_authenticate(struct connectdata *conn,
00109                                            const char *resp);
00110 static void imap_get_message(char *buffer, char **outptr);
00111 
00112 /*
00113  * IMAP protocol handler.
00114  */
00115 
00116 const struct Curl_handler Curl_handler_imap = {
00117   "IMAP",                           /* scheme */
00118   imap_setup_connection,            /* setup_connection */
00119   imap_do,                          /* do_it */
00120   imap_done,                        /* done */
00121   ZERO_NULL,                        /* do_more */
00122   imap_connect,                     /* connect_it */
00123   imap_multi_statemach,             /* connecting */
00124   imap_doing,                       /* doing */
00125   imap_getsock,                     /* proto_getsock */
00126   imap_getsock,                     /* doing_getsock */
00127   ZERO_NULL,                        /* domore_getsock */
00128   ZERO_NULL,                        /* perform_getsock */
00129   imap_disconnect,                  /* disconnect */
00130   ZERO_NULL,                        /* readwrite */
00131   PORT_IMAP,                        /* defport */
00132   CURLPROTO_IMAP,                   /* protocol */
00133   PROTOPT_CLOSEACTION               /* flags */
00134 };
00135 
00136 #ifdef USE_SSL
00137 /*
00138  * IMAPS protocol handler.
00139  */
00140 
00141 const struct Curl_handler Curl_handler_imaps = {
00142   "IMAPS",                          /* scheme */
00143   imap_setup_connection,            /* setup_connection */
00144   imap_do,                          /* do_it */
00145   imap_done,                        /* done */
00146   ZERO_NULL,                        /* do_more */
00147   imap_connect,                     /* connect_it */
00148   imap_multi_statemach,             /* connecting */
00149   imap_doing,                       /* doing */
00150   imap_getsock,                     /* proto_getsock */
00151   imap_getsock,                     /* doing_getsock */
00152   ZERO_NULL,                        /* domore_getsock */
00153   ZERO_NULL,                        /* perform_getsock */
00154   imap_disconnect,                  /* disconnect */
00155   ZERO_NULL,                        /* readwrite */
00156   PORT_IMAPS,                       /* defport */
00157   CURLPROTO_IMAPS,                  /* protocol */
00158   PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
00159 };
00160 #endif
00161 
00162 #ifndef CURL_DISABLE_HTTP
00163 /*
00164  * HTTP-proxyed IMAP protocol handler.
00165  */
00166 
00167 static const struct Curl_handler Curl_handler_imap_proxy = {
00168   "IMAP",                               /* scheme */
00169   Curl_http_setup_conn,                 /* setup_connection */
00170   Curl_http,                            /* do_it */
00171   Curl_http_done,                       /* done */
00172   ZERO_NULL,                            /* do_more */
00173   ZERO_NULL,                            /* connect_it */
00174   ZERO_NULL,                            /* connecting */
00175   ZERO_NULL,                            /* doing */
00176   ZERO_NULL,                            /* proto_getsock */
00177   ZERO_NULL,                            /* doing_getsock */
00178   ZERO_NULL,                            /* domore_getsock */
00179   ZERO_NULL,                            /* perform_getsock */
00180   ZERO_NULL,                            /* disconnect */
00181   ZERO_NULL,                            /* readwrite */
00182   PORT_IMAP,                            /* defport */
00183   CURLPROTO_HTTP,                       /* protocol */
00184   PROTOPT_NONE                          /* flags */
00185 };
00186 
00187 #ifdef USE_SSL
00188 /*
00189  * HTTP-proxyed IMAPS protocol handler.
00190  */
00191 
00192 static const struct Curl_handler Curl_handler_imaps_proxy = {
00193   "IMAPS",                              /* scheme */
00194   Curl_http_setup_conn,                 /* setup_connection */
00195   Curl_http,                            /* do_it */
00196   Curl_http_done,                       /* done */
00197   ZERO_NULL,                            /* do_more */
00198   ZERO_NULL,                            /* connect_it */
00199   ZERO_NULL,                            /* connecting */
00200   ZERO_NULL,                            /* doing */
00201   ZERO_NULL,                            /* proto_getsock */
00202   ZERO_NULL,                            /* doing_getsock */
00203   ZERO_NULL,                            /* domore_getsock */
00204   ZERO_NULL,                            /* perform_getsock */
00205   ZERO_NULL,                            /* disconnect */
00206   ZERO_NULL,                            /* readwrite */
00207   PORT_IMAPS,                           /* defport */
00208   CURLPROTO_HTTP,                       /* protocol */
00209   PROTOPT_NONE                          /* flags */
00210 };
00211 #endif
00212 #endif
00213 
00214 /* SASL parameters for the imap protocol */
00215 static const struct SASLproto saslimap = {
00216   "imap",                     /* The service name */
00217   '+',                        /* Code received when continuation is expected */
00218   'O',                        /* Code to receive upon authentication success */
00219   0,                          /* Maximum initial response length (no max) */
00220   imap_perform_authenticate,  /* Send authentication command */
00221   imap_continue_authenticate, /* Send authentication continuation */
00222   imap_get_message            /* Get SASL response message */
00223 };
00224 
00225 
00226 #ifdef USE_SSL
00227 static void imap_to_imaps(struct connectdata *conn)
00228 {
00229   /* Change the connection handler */
00230   conn->handler = &Curl_handler_imaps;
00231 
00232   /* Set the connection's upgraded to TLS flag */
00233   conn->tls_upgraded = TRUE;
00234 }
00235 #else
00236 #define imap_to_imaps(x) Curl_nop_stmt
00237 #endif
00238 
00239 /***********************************************************************
00240  *
00241  * imap_matchresp()
00242  *
00243  * Determines whether the untagged response is related to the specified
00244  * command by checking if it is in format "* <command-name> ..." or
00245  * "* <number> <command-name> ...".
00246  *
00247  * The "* " marker is assumed to have already been checked by the caller.
00248  */
00249 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
00250 {
00251   const char *end = line + len;
00252   size_t cmd_len = strlen(cmd);
00253 
00254   /* Skip the untagged response marker */
00255   line += 2;
00256 
00257   /* Do we have a number after the marker? */
00258   if(line < end && ISDIGIT(*line)) {
00259     /* Skip the number */
00260     do
00261       line++;
00262     while(line < end && ISDIGIT(*line));
00263 
00264     /* Do we have the space character? */
00265     if(line == end || *line != ' ')
00266       return FALSE;
00267 
00268     line++;
00269   }
00270 
00271   /* Does the command name match and is it followed by a space character or at
00272      the end of line? */
00273   if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
00274      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
00275     return TRUE;
00276 
00277   return FALSE;
00278 }
00279 
00280 /***********************************************************************
00281  *
00282  * imap_endofresp()
00283  *
00284  * Checks whether the given string is a valid tagged, untagged or continuation
00285  * response which can be processed by the response handler.
00286  */
00287 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
00288                            int *resp)
00289 {
00290   struct IMAP *imap = conn->data->req.protop;
00291   struct imap_conn *imapc = &conn->proto.imapc;
00292   const char *id = imapc->resptag;
00293   size_t id_len = strlen(id);
00294 
00295   /* Do we have a tagged command response? */
00296   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
00297     line += id_len + 1;
00298     len -= id_len + 1;
00299 
00300     if(len >= 2 && !memcmp(line, "OK", 2))
00301       *resp = 'O';
00302     else if(len >= 2 && !memcmp(line, "NO", 2))
00303       *resp = 'N';
00304     else if(len >= 3 && !memcmp(line, "BAD", 3))
00305       *resp = 'B';
00306     else {
00307       failf(conn->data, "Bad tagged response");
00308       *resp = -1;
00309     }
00310 
00311     return TRUE;
00312   }
00313 
00314   /* Do we have an untagged command response? */
00315   if(len >= 2 && !memcmp("* ", line, 2)) {
00316     switch(imapc->state) {
00317       /* States which are interested in untagged responses */
00318       case IMAP_CAPABILITY:
00319         if(!imap_matchresp(line, len, "CAPABILITY"))
00320           return FALSE;
00321         break;
00322 
00323       case IMAP_LIST:
00324         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
00325           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
00326            (strcmp(imap->custom, "STORE") ||
00327             !imap_matchresp(line, len, "FETCH")) &&
00328            strcmp(imap->custom, "SELECT") &&
00329            strcmp(imap->custom, "EXAMINE") &&
00330            strcmp(imap->custom, "SEARCH") &&
00331            strcmp(imap->custom, "EXPUNGE") &&
00332            strcmp(imap->custom, "LSUB") &&
00333            strcmp(imap->custom, "UID") &&
00334            strcmp(imap->custom, "NOOP")))
00335           return FALSE;
00336         break;
00337 
00338       case IMAP_SELECT:
00339         /* SELECT is special in that its untagged responses do not have a
00340            common prefix so accept anything! */
00341         break;
00342 
00343       case IMAP_FETCH:
00344         if(!imap_matchresp(line, len, "FETCH"))
00345           return FALSE;
00346         break;
00347 
00348       case IMAP_SEARCH:
00349         if(!imap_matchresp(line, len, "SEARCH"))
00350           return FALSE;
00351         break;
00352 
00353       /* Ignore other untagged responses */
00354       default:
00355         return FALSE;
00356     }
00357 
00358     *resp = '*';
00359     return TRUE;
00360   }
00361 
00362   /* Do we have a continuation response? This should be a + symbol followed by
00363      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
00364      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
00365      some e-mail servers ignore this and only send a single + instead. */
00366   if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
00367      (len >= 2 && !memcmp("+ ", line, 2)))) {
00368     switch(imapc->state) {
00369       /* States which are interested in continuation responses */
00370       case IMAP_AUTHENTICATE:
00371       case IMAP_APPEND:
00372         *resp = '+';
00373         break;
00374 
00375       default:
00376         failf(conn->data, "Unexpected continuation response");
00377         *resp = -1;
00378         break;
00379     }
00380 
00381     return TRUE;
00382   }
00383 
00384   return FALSE; /* Nothing for us */
00385 }
00386 
00387 /***********************************************************************
00388  *
00389  * imap_get_message()
00390  *
00391  * Gets the authentication message from the response buffer.
00392  */
00393 static void imap_get_message(char *buffer, char **outptr)
00394 {
00395   size_t len = 0;
00396   char *message = NULL;
00397 
00398   /* Find the start of the message */
00399   for(message = buffer + 2; *message == ' ' || *message == '\t'; message++)
00400     ;
00401 
00402   /* Find the end of the message */
00403   for(len = strlen(message); len--;)
00404     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
00405         message[len] != '\t')
00406       break;
00407 
00408   /* Terminate the message */
00409   if(++len) {
00410     message[len] = '\0';
00411   }
00412 
00413   *outptr = message;
00414 }
00415 
00416 /***********************************************************************
00417  *
00418  * state()
00419  *
00420  * This is the ONLY way to change IMAP state!
00421  */
00422 static void state(struct connectdata *conn, imapstate newstate)
00423 {
00424   struct imap_conn *imapc = &conn->proto.imapc;
00425 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00426   /* for debug purposes */
00427   static const char * const names[]={
00428     "STOP",
00429     "SERVERGREET",
00430     "CAPABILITY",
00431     "STARTTLS",
00432     "UPGRADETLS",
00433     "AUTHENTICATE",
00434     "LOGIN",
00435     "LIST",
00436     "SELECT",
00437     "FETCH",
00438     "FETCH_FINAL",
00439     "APPEND",
00440     "APPEND_FINAL",
00441     "SEARCH",
00442     "LOGOUT",
00443     /* LAST */
00444   };
00445 
00446   if(imapc->state != newstate)
00447     infof(conn->data, "IMAP %p state change from %s to %s\n",
00448           (void *)imapc, names[imapc->state], names[newstate]);
00449 #endif
00450 
00451   imapc->state = newstate;
00452 }
00453 
00454 /***********************************************************************
00455  *
00456  * imap_perform_capability()
00457  *
00458  * Sends the CAPABILITY command in order to obtain a list of server side
00459  * supported capabilities.
00460  */
00461 static CURLcode imap_perform_capability(struct connectdata *conn)
00462 {
00463   CURLcode result = CURLE_OK;
00464   struct imap_conn *imapc = &conn->proto.imapc;
00465 
00466   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
00467   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
00468   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
00469 
00470   /* Send the CAPABILITY command */
00471   result = imap_sendf(conn, "CAPABILITY");
00472 
00473   if(!result)
00474     state(conn, IMAP_CAPABILITY);
00475 
00476   return result;
00477 }
00478 
00479 /***********************************************************************
00480  *
00481  * imap_perform_starttls()
00482  *
00483  * Sends the STARTTLS command to start the upgrade to TLS.
00484  */
00485 static CURLcode imap_perform_starttls(struct connectdata *conn)
00486 {
00487   CURLcode result = CURLE_OK;
00488 
00489   /* Send the STARTTLS command */
00490   result = imap_sendf(conn, "STARTTLS");
00491 
00492   if(!result)
00493     state(conn, IMAP_STARTTLS);
00494 
00495   return result;
00496 }
00497 
00498 /***********************************************************************
00499  *
00500  * imap_perform_upgrade_tls()
00501  *
00502  * Performs the upgrade to TLS.
00503  */
00504 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
00505 {
00506   CURLcode result = CURLE_OK;
00507   struct imap_conn *imapc = &conn->proto.imapc;
00508 
00509   /* Start the SSL connection */
00510   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
00511 
00512   if(!result) {
00513     if(imapc->state != IMAP_UPGRADETLS)
00514       state(conn, IMAP_UPGRADETLS);
00515 
00516     if(imapc->ssldone) {
00517       imap_to_imaps(conn);
00518       result = imap_perform_capability(conn);
00519     }
00520   }
00521 
00522   return result;
00523 }
00524 
00525 /***********************************************************************
00526  *
00527  * imap_perform_login()
00528  *
00529  * Sends a clear text LOGIN command to authenticate with.
00530  */
00531 static CURLcode imap_perform_login(struct connectdata *conn)
00532 {
00533   CURLcode result = CURLE_OK;
00534   char *user;
00535   char *passwd;
00536 
00537   /* Check we have a username and password to authenticate with and end the
00538      connect phase if we don't */
00539   if(!conn->bits.user_passwd) {
00540     state(conn, IMAP_STOP);
00541 
00542     return result;
00543   }
00544 
00545   /* Make sure the username and password are in the correct atom format */
00546   user = imap_atom(conn->user, false);
00547   passwd = imap_atom(conn->passwd, false);
00548 
00549   /* Send the LOGIN command */
00550   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
00551                       passwd ? passwd : "");
00552 
00553   free(user);
00554   free(passwd);
00555 
00556   if(!result)
00557     state(conn, IMAP_LOGIN);
00558 
00559   return result;
00560 }
00561 
00562 /***********************************************************************
00563  *
00564  * imap_perform_authenticate()
00565  *
00566  * Sends an AUTHENTICATE command allowing the client to login with the given
00567  * SASL authentication mechanism.
00568  */
00569 static CURLcode imap_perform_authenticate(struct connectdata *conn,
00570                                           const char *mech,
00571                                           const char *initresp)
00572 {
00573   CURLcode result = CURLE_OK;
00574 
00575   if(initresp) {
00576     /* Send the AUTHENTICATE command with the initial response */
00577     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
00578   }
00579   else {
00580     /* Send the AUTHENTICATE command */
00581     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
00582   }
00583 
00584   return result;
00585 }
00586 
00587 /***********************************************************************
00588  *
00589  * imap_continue_authenticate()
00590  *
00591  * Sends SASL continuation data or cancellation.
00592  */
00593 static CURLcode imap_continue_authenticate(struct connectdata *conn,
00594                                            const char *resp)
00595 {
00596   struct imap_conn *imapc = &conn->proto.imapc;
00597 
00598   return Curl_pp_sendf(&imapc->pp, "%s", resp);
00599 }
00600 
00601 /***********************************************************************
00602  *
00603  * imap_perform_authentication()
00604  *
00605  * Initiates the authentication sequence, with the appropriate SASL
00606  * authentication mechanism, falling back to clear text should a common
00607  * mechanism not be available between the client and server.
00608  */
00609 static CURLcode imap_perform_authentication(struct connectdata *conn)
00610 {
00611   CURLcode result = CURLE_OK;
00612   struct imap_conn *imapc = &conn->proto.imapc;
00613   saslprogress progress;
00614 
00615   /* Check we have enough data to authenticate with and end the
00616      connect phase if we don't */
00617   if(!Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
00618     state(conn, IMAP_STOP);
00619     return result;
00620   }
00621 
00622   /* Calculate the SASL login details */
00623   result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
00624 
00625   if(!result) {
00626     if(progress == SASL_INPROGRESS)
00627       state(conn, IMAP_AUTHENTICATE);
00628     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
00629       /* Perform clear text authentication */
00630       result = imap_perform_login(conn);
00631     else {
00632       /* Other mechanisms not supported */
00633       infof(conn->data, "No known authentication mechanisms supported!\n");
00634       result = CURLE_LOGIN_DENIED;
00635     }
00636   }
00637 
00638   return result;
00639 }
00640 
00641 /***********************************************************************
00642  *
00643  * imap_perform_list()
00644  *
00645  * Sends a LIST command or an alternative custom request.
00646  */
00647 static CURLcode imap_perform_list(struct connectdata *conn)
00648 {
00649   CURLcode result = CURLE_OK;
00650   struct Curl_easy *data = conn->data;
00651   struct IMAP *imap = data->req.protop;
00652   char *mailbox;
00653 
00654   if(imap->custom)
00655     /* Send the custom request */
00656     result = imap_sendf(conn, "%s%s", imap->custom,
00657                         imap->custom_params ? imap->custom_params : "");
00658   else {
00659     /* Make sure the mailbox is in the correct atom format if necessary */
00660     mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
00661     if(!mailbox)
00662       return CURLE_OUT_OF_MEMORY;
00663 
00664     /* Send the LIST command */
00665     result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
00666 
00667     free(mailbox);
00668   }
00669 
00670   if(!result)
00671     state(conn, IMAP_LIST);
00672 
00673   return result;
00674 }
00675 
00676 /***********************************************************************
00677  *
00678  * imap_perform_select()
00679  *
00680  * Sends a SELECT command to ask the server to change the selected mailbox.
00681  */
00682 static CURLcode imap_perform_select(struct connectdata *conn)
00683 {
00684   CURLcode result = CURLE_OK;
00685   struct Curl_easy *data = conn->data;
00686   struct IMAP *imap = data->req.protop;
00687   struct imap_conn *imapc = &conn->proto.imapc;
00688   char *mailbox;
00689 
00690   /* Invalidate old information as we are switching mailboxes */
00691   Curl_safefree(imapc->mailbox);
00692   Curl_safefree(imapc->mailbox_uidvalidity);
00693 
00694   /* Check we have a mailbox */
00695   if(!imap->mailbox) {
00696     failf(conn->data, "Cannot SELECT without a mailbox.");
00697     return CURLE_URL_MALFORMAT;
00698   }
00699 
00700   /* Make sure the mailbox is in the correct atom format */
00701   mailbox = imap_atom(imap->mailbox, false);
00702   if(!mailbox)
00703     return CURLE_OUT_OF_MEMORY;
00704 
00705   /* Send the SELECT command */
00706   result = imap_sendf(conn, "SELECT %s", mailbox);
00707 
00708   free(mailbox);
00709 
00710   if(!result)
00711     state(conn, IMAP_SELECT);
00712 
00713   return result;
00714 }
00715 
00716 /***********************************************************************
00717  *
00718  * imap_perform_fetch()
00719  *
00720  * Sends a FETCH command to initiate the download of a message.
00721  */
00722 static CURLcode imap_perform_fetch(struct connectdata *conn)
00723 {
00724   CURLcode result = CURLE_OK;
00725   struct IMAP *imap = conn->data->req.protop;
00726 
00727   /* Check we have a UID */
00728   if(!imap->uid) {
00729     failf(conn->data, "Cannot FETCH without a UID.");
00730     return CURLE_URL_MALFORMAT;
00731   }
00732 
00733   /* Send the FETCH command */
00734   if(imap->partial)
00735     result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
00736                         imap->uid,
00737                         imap->section ? imap->section : "",
00738                         imap->partial);
00739   else
00740     result = imap_sendf(conn, "FETCH %s BODY[%s]",
00741                         imap->uid,
00742                         imap->section ? imap->section : "");
00743 
00744   if(!result)
00745     state(conn, IMAP_FETCH);
00746 
00747   return result;
00748 }
00749 
00750 /***********************************************************************
00751  *
00752  * imap_perform_append()
00753  *
00754  * Sends an APPEND command to initiate the upload of a message.
00755  */
00756 static CURLcode imap_perform_append(struct connectdata *conn)
00757 {
00758   CURLcode result = CURLE_OK;
00759   struct IMAP *imap = conn->data->req.protop;
00760   char *mailbox;
00761 
00762   /* Check we have a mailbox */
00763   if(!imap->mailbox) {
00764     failf(conn->data, "Cannot APPEND without a mailbox.");
00765     return CURLE_URL_MALFORMAT;
00766   }
00767 
00768   /* Check we know the size of the upload */
00769   if(conn->data->state.infilesize < 0) {
00770     failf(conn->data, "Cannot APPEND with unknown input file size\n");
00771     return CURLE_UPLOAD_FAILED;
00772   }
00773 
00774   /* Make sure the mailbox is in the correct atom format */
00775   mailbox = imap_atom(imap->mailbox, false);
00776   if(!mailbox)
00777     return CURLE_OUT_OF_MEMORY;
00778 
00779   /* Send the APPEND command */
00780   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
00781                       mailbox, conn->data->state.infilesize);
00782 
00783   free(mailbox);
00784 
00785   if(!result)
00786     state(conn, IMAP_APPEND);
00787 
00788   return result;
00789 }
00790 
00791 /***********************************************************************
00792  *
00793  * imap_perform_search()
00794  *
00795  * Sends a SEARCH command.
00796  */
00797 static CURLcode imap_perform_search(struct connectdata *conn)
00798 {
00799   CURLcode result = CURLE_OK;
00800   struct IMAP *imap = conn->data->req.protop;
00801 
00802   /* Check we have a query string */
00803   if(!imap->query) {
00804     failf(conn->data, "Cannot SEARCH without a query string.");
00805     return CURLE_URL_MALFORMAT;
00806   }
00807 
00808   /* Send the SEARCH command */
00809   result = imap_sendf(conn, "SEARCH %s", imap->query);
00810 
00811   if(!result)
00812     state(conn, IMAP_SEARCH);
00813 
00814   return result;
00815 }
00816 
00817 /***********************************************************************
00818  *
00819  * imap_perform_logout()
00820  *
00821  * Performs the logout action prior to sclose() being called.
00822  */
00823 static CURLcode imap_perform_logout(struct connectdata *conn)
00824 {
00825   CURLcode result = CURLE_OK;
00826 
00827   /* Send the LOGOUT command */
00828   result = imap_sendf(conn, "LOGOUT");
00829 
00830   if(!result)
00831     state(conn, IMAP_LOGOUT);
00832 
00833   return result;
00834 }
00835 
00836 /* For the initial server greeting */
00837 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
00838                                             int imapcode,
00839                                             imapstate instate)
00840 {
00841   CURLcode result = CURLE_OK;
00842   struct Curl_easy *data = conn->data;
00843 
00844   (void)instate; /* no use for this yet */
00845 
00846   if(imapcode != 'O') {
00847     failf(data, "Got unexpected imap-server response");
00848     result = CURLE_WEIRD_SERVER_REPLY;
00849   }
00850   else
00851     result = imap_perform_capability(conn);
00852 
00853   return result;
00854 }
00855 
00856 /* For CAPABILITY responses */
00857 static CURLcode imap_state_capability_resp(struct connectdata *conn,
00858                                            int imapcode,
00859                                            imapstate instate)
00860 {
00861   CURLcode result = CURLE_OK;
00862   struct Curl_easy *data = conn->data;
00863   struct imap_conn *imapc = &conn->proto.imapc;
00864   const char *line = data->state.buffer;
00865   size_t wordlen;
00866 
00867   (void)instate; /* no use for this yet */
00868 
00869   /* Do we have a untagged response? */
00870   if(imapcode == '*') {
00871     line += 2;
00872 
00873     /* Loop through the data line */
00874     for(;;) {
00875       while(*line &&
00876             (*line == ' ' || *line == '\t' ||
00877               *line == '\r' || *line == '\n')) {
00878 
00879         line++;
00880       }
00881 
00882       if(!*line)
00883         break;
00884 
00885       /* Extract the word */
00886       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
00887             line[wordlen] != '\t' && line[wordlen] != '\r' &&
00888             line[wordlen] != '\n';)
00889         wordlen++;
00890 
00891       /* Does the server support the STARTTLS capability? */
00892       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
00893         imapc->tls_supported = TRUE;
00894 
00895       /* Has the server explicitly disabled clear text authentication? */
00896       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
00897         imapc->login_disabled = TRUE;
00898 
00899       /* Does the server support the SASL-IR capability? */
00900       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
00901         imapc->ir_supported = TRUE;
00902 
00903       /* Do we have a SASL based authentication mechanism? */
00904       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
00905         size_t llen;
00906         unsigned int mechbit;
00907 
00908         line += 5;
00909         wordlen -= 5;
00910 
00911         /* Test the word for a matching authentication mechanism */
00912         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
00913         if(mechbit && llen == wordlen)
00914           imapc->sasl.authmechs |= mechbit;
00915       }
00916 
00917       line += wordlen;
00918     }
00919   }
00920   else if(imapcode == 'O') {
00921     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
00922       /* We don't have a SSL/TLS connection yet, but SSL is requested */
00923       if(imapc->tls_supported)
00924         /* Switch to TLS connection now */
00925         result = imap_perform_starttls(conn);
00926       else if(data->set.use_ssl == CURLUSESSL_TRY)
00927         /* Fallback and carry on with authentication */
00928         result = imap_perform_authentication(conn);
00929       else {
00930         failf(data, "STARTTLS not supported.");
00931         result = CURLE_USE_SSL_FAILED;
00932       }
00933     }
00934     else
00935       result = imap_perform_authentication(conn);
00936   }
00937   else
00938     result = imap_perform_authentication(conn);
00939 
00940   return result;
00941 }
00942 
00943 /* For STARTTLS responses */
00944 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
00945                                          int imapcode,
00946                                          imapstate instate)
00947 {
00948   CURLcode result = CURLE_OK;
00949   struct Curl_easy *data = conn->data;
00950 
00951   (void)instate; /* no use for this yet */
00952 
00953   if(imapcode != 'O') {
00954     if(data->set.use_ssl != CURLUSESSL_TRY) {
00955       failf(data, "STARTTLS denied. %c", imapcode);
00956       result = CURLE_USE_SSL_FAILED;
00957     }
00958     else
00959       result = imap_perform_authentication(conn);
00960   }
00961   else
00962     result = imap_perform_upgrade_tls(conn);
00963 
00964   return result;
00965 }
00966 
00967 /* For SASL authentication responses */
00968 static CURLcode imap_state_auth_resp(struct connectdata *conn,
00969                                      int imapcode,
00970                                      imapstate instate)
00971 {
00972   CURLcode result = CURLE_OK;
00973   struct Curl_easy *data = conn->data;
00974   struct imap_conn *imapc = &conn->proto.imapc;
00975   saslprogress progress;
00976 
00977   (void)instate; /* no use for this yet */
00978 
00979   result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
00980   if(!result)
00981     switch(progress) {
00982     case SASL_DONE:
00983       state(conn, IMAP_STOP);  /* Authenticated */
00984       break;
00985     case SASL_IDLE:            /* No mechanism left after cancellation */
00986       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
00987         /* Perform clear text authentication */
00988         result = imap_perform_login(conn);
00989       else {
00990         failf(data, "Authentication cancelled");
00991         result = CURLE_LOGIN_DENIED;
00992       }
00993       break;
00994     default:
00995       break;
00996     }
00997 
00998   return result;
00999 }
01000 
01001 /* For LOGIN responses */
01002 static CURLcode imap_state_login_resp(struct connectdata *conn,
01003                                       int imapcode,
01004                                       imapstate instate)
01005 {
01006   CURLcode result = CURLE_OK;
01007   struct Curl_easy *data = conn->data;
01008 
01009   (void)instate; /* no use for this yet */
01010 
01011   if(imapcode != 'O') {
01012     failf(data, "Access denied. %c", imapcode);
01013     result = CURLE_LOGIN_DENIED;
01014   }
01015   else
01016     /* End of connect phase */
01017     state(conn, IMAP_STOP);
01018 
01019   return result;
01020 }
01021 
01022 /* For LIST and SEARCH responses */
01023 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
01024                                            int imapcode,
01025                                            imapstate instate)
01026 {
01027   CURLcode result = CURLE_OK;
01028   char *line = conn->data->state.buffer;
01029   size_t len = strlen(line);
01030 
01031   (void)instate; /* No use for this yet */
01032 
01033   if(imapcode == '*') {
01034     /* Temporarily add the LF character back and send as body to the client */
01035     line[len] = '\n';
01036     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
01037     line[len] = '\0';
01038   }
01039   else if(imapcode != 'O')
01040     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
01041   else
01042     /* End of DO phase */
01043     state(conn, IMAP_STOP);
01044 
01045   return result;
01046 }
01047 
01048 /* For SELECT responses */
01049 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
01050                                        imapstate instate)
01051 {
01052   CURLcode result = CURLE_OK;
01053   struct Curl_easy *data = conn->data;
01054   struct IMAP *imap = conn->data->req.protop;
01055   struct imap_conn *imapc = &conn->proto.imapc;
01056   const char *line = data->state.buffer;
01057   char tmp[20];
01058 
01059   (void)instate; /* no use for this yet */
01060 
01061   if(imapcode == '*') {
01062     /* See if this is an UIDVALIDITY response */
01063     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
01064       Curl_safefree(imapc->mailbox_uidvalidity);
01065       imapc->mailbox_uidvalidity = strdup(tmp);
01066     }
01067   }
01068   else if(imapcode == 'O') {
01069     /* Check if the UIDVALIDITY has been specified and matches */
01070     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
01071        strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
01072       failf(conn->data, "Mailbox UIDVALIDITY has changed");
01073       result = CURLE_REMOTE_FILE_NOT_FOUND;
01074     }
01075     else {
01076       /* Note the currently opened mailbox on this connection */
01077       imapc->mailbox = strdup(imap->mailbox);
01078 
01079       if(imap->custom)
01080         result = imap_perform_list(conn);
01081       else if(imap->query)
01082         result = imap_perform_search(conn);
01083       else
01084         result = imap_perform_fetch(conn);
01085     }
01086   }
01087   else {
01088     failf(data, "Select failed");
01089     result = CURLE_LOGIN_DENIED;
01090   }
01091 
01092   return result;
01093 }
01094 
01095 /* For the (first line of the) FETCH responses */
01096 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
01097                                       imapstate instate)
01098 {
01099   CURLcode result = CURLE_OK;
01100   struct Curl_easy *data = conn->data;
01101   struct imap_conn *imapc = &conn->proto.imapc;
01102   struct pingpong *pp = &imapc->pp;
01103   const char *ptr = data->state.buffer;
01104   bool parsed = FALSE;
01105   curl_off_t size = 0;
01106 
01107   (void)instate; /* no use for this yet */
01108 
01109   if(imapcode != '*') {
01110     Curl_pgrsSetDownloadSize(data, -1);
01111     state(conn, IMAP_STOP);
01112     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
01113   }
01114 
01115   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
01116      the continuation data contained within the curly brackets */
01117   while(*ptr && (*ptr != '{'))
01118     ptr++;
01119 
01120   if(*ptr == '{') {
01121     char *endptr;
01122     size = curlx_strtoofft(ptr + 1, &endptr, 10);
01123     if(endptr - ptr > 1 && endptr[0] == '}' &&
01124        endptr[1] == '\r' && endptr[2] == '\0')
01125       parsed = TRUE;
01126   }
01127 
01128   if(parsed) {
01129     infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
01130           size);
01131     Curl_pgrsSetDownloadSize(data, size);
01132 
01133     if(pp->cache) {
01134       /* At this point there is a bunch of data in the header "cache" that is
01135          actually body content, send it as body and then skip it. Do note
01136          that there may even be additional "headers" after the body. */
01137       size_t chunk = pp->cache_size;
01138 
01139       if(chunk > (size_t)size)
01140         /* The conversion from curl_off_t to size_t is always fine here */
01141         chunk = (size_t)size;
01142 
01143       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
01144       if(result)
01145         return result;
01146 
01147       data->req.bytecount += chunk;
01148 
01149       infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
01150             " bytes, %" CURL_FORMAT_CURL_OFF_TU
01151             " bytes are left for transfer\n", (curl_off_t)chunk,
01152             size - chunk);
01153 
01154       /* Have we used the entire cache or just part of it?*/
01155       if(pp->cache_size > chunk) {
01156         /* Only part of it so shrink the cache to fit the trailing data */
01157         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
01158         pp->cache_size -= chunk;
01159       }
01160       else {
01161         /* Free the cache */
01162         Curl_safefree(pp->cache);
01163 
01164         /* Reset the cache size */
01165         pp->cache_size = 0;
01166       }
01167     }
01168 
01169     if(data->req.bytecount == size)
01170       /* The entire data is already transferred! */
01171       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
01172     else {
01173       /* IMAP download */
01174       data->req.maxdownload = size;
01175       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
01176     }
01177   }
01178   else {
01179     /* We don't know how to parse this line */
01180     failf(pp->conn->data, "Failed to parse FETCH response.");
01181     result = CURLE_WEIRD_SERVER_REPLY;
01182   }
01183 
01184   /* End of DO phase */
01185   state(conn, IMAP_STOP);
01186 
01187   return result;
01188 }
01189 
01190 /* For final FETCH responses performed after the download */
01191 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
01192                                             int imapcode,
01193                                             imapstate instate)
01194 {
01195   CURLcode result = CURLE_OK;
01196 
01197   (void)instate; /* No use for this yet */
01198 
01199   if(imapcode != 'O')
01200     result = CURLE_WEIRD_SERVER_REPLY;
01201   else
01202     /* End of DONE phase */
01203     state(conn, IMAP_STOP);
01204 
01205   return result;
01206 }
01207 
01208 /* For APPEND responses */
01209 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
01210                                        imapstate instate)
01211 {
01212   CURLcode result = CURLE_OK;
01213   struct Curl_easy *data = conn->data;
01214 
01215   (void)instate; /* No use for this yet */
01216 
01217   if(imapcode != '+') {
01218     result = CURLE_UPLOAD_FAILED;
01219   }
01220   else {
01221     /* Set the progress upload size */
01222     Curl_pgrsSetUploadSize(data, data->state.infilesize);
01223 
01224     /* IMAP upload */
01225     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
01226 
01227     /* End of DO phase */
01228     state(conn, IMAP_STOP);
01229   }
01230 
01231   return result;
01232 }
01233 
01234 /* For final APPEND responses performed after the upload */
01235 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
01236                                              int imapcode,
01237                                              imapstate instate)
01238 {
01239   CURLcode result = CURLE_OK;
01240 
01241   (void)instate; /* No use for this yet */
01242 
01243   if(imapcode != 'O')
01244     result = CURLE_UPLOAD_FAILED;
01245   else
01246     /* End of DONE phase */
01247     state(conn, IMAP_STOP);
01248 
01249   return result;
01250 }
01251 
01252 static CURLcode imap_statemach_act(struct connectdata *conn)
01253 {
01254   CURLcode result = CURLE_OK;
01255   curl_socket_t sock = conn->sock[FIRSTSOCKET];
01256   int imapcode;
01257   struct imap_conn *imapc = &conn->proto.imapc;
01258   struct pingpong *pp = &imapc->pp;
01259   size_t nread = 0;
01260 
01261   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
01262   if(imapc->state == IMAP_UPGRADETLS)
01263     return imap_perform_upgrade_tls(conn);
01264 
01265   /* Flush any data that needs to be sent */
01266   if(pp->sendleft)
01267     return Curl_pp_flushsend(pp);
01268 
01269   do {
01270     /* Read the response from the server */
01271     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
01272     if(result)
01273       return result;
01274 
01275     /* Was there an error parsing the response line? */
01276     if(imapcode == -1)
01277       return CURLE_WEIRD_SERVER_REPLY;
01278 
01279     if(!imapcode)
01280       break;
01281 
01282     /* We have now received a full IMAP server response */
01283     switch(imapc->state) {
01284     case IMAP_SERVERGREET:
01285       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
01286       break;
01287 
01288     case IMAP_CAPABILITY:
01289       result = imap_state_capability_resp(conn, imapcode, imapc->state);
01290       break;
01291 
01292     case IMAP_STARTTLS:
01293       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
01294       break;
01295 
01296     case IMAP_AUTHENTICATE:
01297       result = imap_state_auth_resp(conn, imapcode, imapc->state);
01298       break;
01299 
01300     case IMAP_LOGIN:
01301       result = imap_state_login_resp(conn, imapcode, imapc->state);
01302       break;
01303 
01304     case IMAP_LIST:
01305       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
01306       break;
01307 
01308     case IMAP_SELECT:
01309       result = imap_state_select_resp(conn, imapcode, imapc->state);
01310       break;
01311 
01312     case IMAP_FETCH:
01313       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
01314       break;
01315 
01316     case IMAP_FETCH_FINAL:
01317       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
01318       break;
01319 
01320     case IMAP_APPEND:
01321       result = imap_state_append_resp(conn, imapcode, imapc->state);
01322       break;
01323 
01324     case IMAP_APPEND_FINAL:
01325       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
01326       break;
01327 
01328     case IMAP_SEARCH:
01329       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
01330       break;
01331 
01332     case IMAP_LOGOUT:
01333       /* fallthrough, just stop! */
01334     default:
01335       /* internal error */
01336       state(conn, IMAP_STOP);
01337       break;
01338     }
01339   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
01340 
01341   return result;
01342 }
01343 
01344 /* Called repeatedly until done from multi.c */
01345 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
01346 {
01347   CURLcode result = CURLE_OK;
01348   struct imap_conn *imapc = &conn->proto.imapc;
01349 
01350   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
01351     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
01352     if(result || !imapc->ssldone)
01353       return result;
01354   }
01355 
01356   result = Curl_pp_statemach(&imapc->pp, FALSE);
01357   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
01358 
01359   return result;
01360 }
01361 
01362 static CURLcode imap_block_statemach(struct connectdata *conn)
01363 {
01364   CURLcode result = CURLE_OK;
01365   struct imap_conn *imapc = &conn->proto.imapc;
01366 
01367   while(imapc->state != IMAP_STOP && !result)
01368     result = Curl_pp_statemach(&imapc->pp, TRUE);
01369 
01370   return result;
01371 }
01372 
01373 /* Allocate and initialize the struct IMAP for the current Curl_easy if
01374    required */
01375 static CURLcode imap_init(struct connectdata *conn)
01376 {
01377   CURLcode result = CURLE_OK;
01378   struct Curl_easy *data = conn->data;
01379   struct IMAP *imap;
01380 
01381   imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
01382   if(!imap)
01383     result = CURLE_OUT_OF_MEMORY;
01384 
01385   return result;
01386 }
01387 
01388 /* For the IMAP "protocol connect" and "doing" phases only */
01389 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
01390                         int numsocks)
01391 {
01392   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
01393 }
01394 
01395 /***********************************************************************
01396  *
01397  * imap_connect()
01398  *
01399  * This function should do everything that is to be considered a part of the
01400  * connection phase.
01401  *
01402  * The variable 'done' points to will be TRUE if the protocol-layer connect
01403  * phase is done when this function returns, or FALSE if not.
01404  */
01405 static CURLcode imap_connect(struct connectdata *conn, bool *done)
01406 {
01407   CURLcode result = CURLE_OK;
01408   struct imap_conn *imapc = &conn->proto.imapc;
01409   struct pingpong *pp = &imapc->pp;
01410 
01411   *done = FALSE; /* default to not done yet */
01412 
01413   /* We always support persistent connections in IMAP */
01414   connkeep(conn, "IMAP default");
01415 
01416   /* Set the default response time-out */
01417   pp->response_time = RESP_TIMEOUT;
01418   pp->statemach_act = imap_statemach_act;
01419   pp->endofresp = imap_endofresp;
01420   pp->conn = conn;
01421 
01422   /* Set the default preferred authentication type and mechanism */
01423   imapc->preftype = IMAP_TYPE_ANY;
01424   Curl_sasl_init(&imapc->sasl, &saslimap);
01425 
01426   /* Initialise the pingpong layer */
01427   Curl_pp_init(pp);
01428 
01429   /* Parse the URL options */
01430   result = imap_parse_url_options(conn);
01431   if(result)
01432     return result;
01433 
01434   /* Start off waiting for the server greeting response */
01435   state(conn, IMAP_SERVERGREET);
01436 
01437   /* Start off with an response id of '*' */
01438   strcpy(imapc->resptag, "*");
01439 
01440   result = imap_multi_statemach(conn, done);
01441 
01442   return result;
01443 }
01444 
01445 /***********************************************************************
01446  *
01447  * imap_done()
01448  *
01449  * The DONE function. This does what needs to be done after a single DO has
01450  * performed.
01451  *
01452  * Input argument is already checked for validity.
01453  */
01454 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
01455                           bool premature)
01456 {
01457   CURLcode result = CURLE_OK;
01458   struct Curl_easy *data = conn->data;
01459   struct IMAP *imap = data->req.protop;
01460 
01461   (void)premature;
01462 
01463   if(!imap)
01464     return CURLE_OK;
01465 
01466   if(status) {
01467     connclose(conn, "IMAP done with bad status"); /* marked for closure */
01468     result = status;         /* use the already set error code */
01469   }
01470   else if(!data->set.connect_only && !imap->custom &&
01471           (imap->uid || data->set.upload)) {
01472     /* Handle responses after FETCH or APPEND transfer has finished */
01473     if(!data->set.upload)
01474       state(conn, IMAP_FETCH_FINAL);
01475     else {
01476       /* End the APPEND command first by sending an empty line */
01477       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
01478       if(!result)
01479         state(conn, IMAP_APPEND_FINAL);
01480     }
01481 
01482     /* Run the state-machine
01483 
01484        TODO: when the multi interface is used, this _really_ should be using
01485        the imap_multi_statemach function but we have no general support for
01486        non-blocking DONE operations!
01487     */
01488     if(!result)
01489       result = imap_block_statemach(conn);
01490   }
01491 
01492   /* Cleanup our per-request based variables */
01493   Curl_safefree(imap->mailbox);
01494   Curl_safefree(imap->uidvalidity);
01495   Curl_safefree(imap->uid);
01496   Curl_safefree(imap->section);
01497   Curl_safefree(imap->partial);
01498   Curl_safefree(imap->query);
01499   Curl_safefree(imap->custom);
01500   Curl_safefree(imap->custom_params);
01501 
01502   /* Clear the transfer mode for the next request */
01503   imap->transfer = FTPTRANSFER_BODY;
01504 
01505   return result;
01506 }
01507 
01508 /***********************************************************************
01509  *
01510  * imap_perform()
01511  *
01512  * This is the actual DO function for IMAP. Fetch or append a message, or do
01513  * other things according to the options previously setup.
01514  */
01515 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
01516                              bool *dophase_done)
01517 {
01518   /* This is IMAP and no proxy */
01519   CURLcode result = CURLE_OK;
01520   struct Curl_easy *data = conn->data;
01521   struct IMAP *imap = data->req.protop;
01522   struct imap_conn *imapc = &conn->proto.imapc;
01523   bool selected = FALSE;
01524 
01525   DEBUGF(infof(conn->data, "DO phase starts\n"));
01526 
01527   if(conn->data->set.opt_no_body) {
01528     /* Requested no body means no transfer */
01529     imap->transfer = FTPTRANSFER_INFO;
01530   }
01531 
01532   *dophase_done = FALSE; /* not done yet */
01533 
01534   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
01535      has already been selected on this connection */
01536   if(imap->mailbox && imapc->mailbox &&
01537      !strcmp(imap->mailbox, imapc->mailbox) &&
01538      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
01539       !strcmp(imap->uidvalidity, imapc->mailbox_uidvalidity)))
01540     selected = TRUE;
01541 
01542   /* Start the first command in the DO phase */
01543   if(conn->data->set.upload)
01544     /* APPEND can be executed directly */
01545     result = imap_perform_append(conn);
01546   else if(imap->custom && (selected || !imap->mailbox))
01547     /* Custom command using the same mailbox or no mailbox */
01548     result = imap_perform_list(conn);
01549   else if(!imap->custom && selected && imap->uid)
01550     /* FETCH from the same mailbox */
01551     result = imap_perform_fetch(conn);
01552   else if(!imap->custom && selected && imap->query)
01553     /* SEARCH the current mailbox */
01554     result = imap_perform_search(conn);
01555   else if(imap->mailbox && !selected &&
01556          (imap->custom || imap->uid || imap->query))
01557     /* SELECT the mailbox */
01558     result = imap_perform_select(conn);
01559   else
01560     /* LIST */
01561     result = imap_perform_list(conn);
01562 
01563   if(result)
01564     return result;
01565 
01566   /* Run the state-machine */
01567   result = imap_multi_statemach(conn, dophase_done);
01568 
01569   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
01570 
01571   if(*dophase_done)
01572     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01573 
01574   return result;
01575 }
01576 
01577 /***********************************************************************
01578  *
01579  * imap_do()
01580  *
01581  * This function is registered as 'curl_do' function. It decodes the path
01582  * parts etc as a wrapper to the actual DO function (imap_perform).
01583  *
01584  * The input argument is already checked for validity.
01585  */
01586 static CURLcode imap_do(struct connectdata *conn, bool *done)
01587 {
01588   CURLcode result = CURLE_OK;
01589 
01590   *done = FALSE; /* default to false */
01591 
01592   /* Parse the URL path */
01593   result = imap_parse_url_path(conn);
01594   if(result)
01595     return result;
01596 
01597   /* Parse the custom request */
01598   result = imap_parse_custom_request(conn);
01599   if(result)
01600     return result;
01601 
01602   result = imap_regular_transfer(conn, done);
01603 
01604   return result;
01605 }
01606 
01607 /***********************************************************************
01608  *
01609  * imap_disconnect()
01610  *
01611  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
01612  * resources. BLOCKING.
01613  */
01614 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
01615 {
01616   struct imap_conn *imapc = &conn->proto.imapc;
01617 
01618   /* We cannot send quit unconditionally. If this connection is stale or
01619      bad in any way, sending quit and waiting around here will make the
01620      disconnect wait in vain and cause more problems than we need to. */
01621 
01622   /* The IMAP session may or may not have been allocated/setup at this
01623      point! */
01624   if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
01625     if(!imap_perform_logout(conn))
01626       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
01627 
01628   /* Disconnect from the server */
01629   Curl_pp_disconnect(&imapc->pp);
01630 
01631   /* Cleanup the SASL module */
01632   Curl_sasl_cleanup(conn, imapc->sasl.authused);
01633 
01634   /* Cleanup our connection based variables */
01635   Curl_safefree(imapc->mailbox);
01636   Curl_safefree(imapc->mailbox_uidvalidity);
01637 
01638   return CURLE_OK;
01639 }
01640 
01641 /* Call this when the DO phase has completed */
01642 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
01643 {
01644   struct IMAP *imap = conn->data->req.protop;
01645 
01646   (void)connected;
01647 
01648   if(imap->transfer != FTPTRANSFER_BODY)
01649     /* no data to transfer */
01650     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
01651 
01652   return CURLE_OK;
01653 }
01654 
01655 /* Called from multi.c while DOing */
01656 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
01657 {
01658   CURLcode result = imap_multi_statemach(conn, dophase_done);
01659 
01660   if(result)
01661     DEBUGF(infof(conn->data, "DO phase failed\n"));
01662   else if(*dophase_done) {
01663     result = imap_dophase_done(conn, FALSE /* not connected */);
01664 
01665     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01666   }
01667 
01668   return result;
01669 }
01670 
01671 /***********************************************************************
01672  *
01673  * imap_regular_transfer()
01674  *
01675  * The input argument is already checked for validity.
01676  *
01677  * Performs all commands done before a regular transfer between a local and a
01678  * remote host.
01679  */
01680 static CURLcode imap_regular_transfer(struct connectdata *conn,
01681                                       bool *dophase_done)
01682 {
01683   CURLcode result = CURLE_OK;
01684   bool connected = FALSE;
01685   struct Curl_easy *data = conn->data;
01686 
01687   /* Make sure size is unknown at this point */
01688   data->req.size = -1;
01689 
01690   /* Set the progress data */
01691   Curl_pgrsSetUploadCounter(data, 0);
01692   Curl_pgrsSetDownloadCounter(data, 0);
01693   Curl_pgrsSetUploadSize(data, -1);
01694   Curl_pgrsSetDownloadSize(data, -1);
01695 
01696   /* Carry out the perform */
01697   result = imap_perform(conn, &connected, dophase_done);
01698 
01699   /* Perform post DO phase operations if necessary */
01700   if(!result && *dophase_done)
01701     result = imap_dophase_done(conn, connected);
01702 
01703   return result;
01704 }
01705 
01706 static CURLcode imap_setup_connection(struct connectdata *conn)
01707 {
01708   struct Curl_easy *data = conn->data;
01709 
01710   /* Initialise the IMAP layer */
01711   CURLcode result = imap_init(conn);
01712   if(result)
01713     return result;
01714 
01715   /* Clear the TLS upgraded flag */
01716   conn->tls_upgraded = FALSE;
01717 
01718   /* Set up the proxy if necessary */
01719   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
01720     /* Unless we have asked to tunnel IMAP operations through the proxy, we
01721        switch and use HTTP operations only */
01722 #ifndef CURL_DISABLE_HTTP
01723     if(conn->handler == &Curl_handler_imap)
01724       conn->handler = &Curl_handler_imap_proxy;
01725     else {
01726 #ifdef USE_SSL
01727       conn->handler = &Curl_handler_imaps_proxy;
01728 #else
01729       failf(data, "IMAPS not supported!");
01730       return CURLE_UNSUPPORTED_PROTOCOL;
01731 #endif
01732     }
01733 
01734     /* set it up as an HTTP connection instead */
01735     return conn->handler->setup_connection(conn);
01736 #else
01737     failf(data, "IMAP over http proxy requires HTTP support built-in!");
01738     return CURLE_UNSUPPORTED_PROTOCOL;
01739 #endif
01740   }
01741 
01742   data->state.path++;   /* don't include the initial slash */
01743 
01744   return CURLE_OK;
01745 }
01746 
01747 /***********************************************************************
01748  *
01749  * imap_sendf()
01750  *
01751  * Sends the formated string as an IMAP command to the server.
01752  *
01753  * Designed to never block.
01754  */
01755 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
01756 {
01757   CURLcode result = CURLE_OK;
01758   struct imap_conn *imapc = &conn->proto.imapc;
01759   char *taggedfmt;
01760   va_list ap;
01761 
01762   DEBUGASSERT(fmt);
01763 
01764   /* Calculate the next command ID wrapping at 3 digits */
01765   imapc->cmdid = (imapc->cmdid + 1) % 1000;
01766 
01767   /* Calculate the tag based on the connection ID and command ID */
01768   snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
01769            'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
01770 
01771   /* Prefix the format with the tag */
01772   taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
01773   if(!taggedfmt)
01774     return CURLE_OUT_OF_MEMORY;
01775 
01776   /* Send the data with the tag */
01777   va_start(ap, fmt);
01778   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
01779   va_end(ap);
01780 
01781   free(taggedfmt);
01782 
01783   return result;
01784 }
01785 
01786 /***********************************************************************
01787  *
01788  * imap_atom()
01789  *
01790  * Checks the input string for characters that need escaping and returns an
01791  * atom ready for sending to the server.
01792  *
01793  * The returned string needs to be freed.
01794  *
01795  */
01796 static char *imap_atom(const char *str, bool escape_only)
01797 {
01798   /* !checksrc! disable PARENBRACE 1 */
01799   const char atom_specials[] = "(){ %*]";
01800   const char *p1;
01801   char *p2;
01802   size_t backsp_count = 0;
01803   size_t quote_count = 0;
01804   bool others_exists = FALSE;
01805   size_t newlen = 0;
01806   char *newstr = NULL;
01807 
01808   if(!str)
01809     return NULL;
01810 
01811   /* Look for "atom-specials", counting the backslash and quote characters as
01812      these will need escapping */
01813   p1 = str;
01814   while(*p1) {
01815     if(*p1 == '\\')
01816       backsp_count++;
01817     else if(*p1 == '"')
01818       quote_count++;
01819     else if(!escape_only) {
01820       const char *p3 = atom_specials;
01821 
01822       while(*p3 && !others_exists) {
01823         if(*p1 == *p3)
01824           others_exists = TRUE;
01825 
01826         p3++;
01827       }
01828     }
01829 
01830     p1++;
01831   }
01832 
01833   /* Does the input contain any "atom-special" characters? */
01834   if(!backsp_count && !quote_count && !others_exists)
01835     return strdup(str);
01836 
01837   /* Calculate the new string length */
01838   newlen = strlen(str) + backsp_count + quote_count + (others_exists ? 2 : 0);
01839 
01840   /* Allocate the new string */
01841   newstr = (char *) malloc((newlen + 1) * sizeof(char));
01842   if(!newstr)
01843     return NULL;
01844 
01845   /* Surround the string in quotes if necessary */
01846   p2 = newstr;
01847   if(others_exists) {
01848     newstr[0] = '"';
01849     newstr[newlen - 1] = '"';
01850     p2++;
01851   }
01852 
01853   /* Copy the string, escaping backslash and quote characters along the way */
01854   p1 = str;
01855   while(*p1) {
01856     if(*p1 == '\\' || *p1 == '"') {
01857       *p2 = '\\';
01858       p2++;
01859     }
01860 
01861    *p2 = *p1;
01862 
01863     p1++;
01864     p2++;
01865   }
01866 
01867   /* Terminate the string */
01868   newstr[newlen] = '\0';
01869 
01870   return newstr;
01871 }
01872 
01873 /***********************************************************************
01874  *
01875  * imap_is_bchar()
01876  *
01877  * Portable test of whether the specified char is a "bchar" as defined in the
01878  * grammar of RFC-5092.
01879  */
01880 static bool imap_is_bchar(char ch)
01881 {
01882   switch(ch) {
01883     /* bchar */
01884     case ':': case '@': case '/':
01885     /* bchar -> achar */
01886     case '&': case '=':
01887     /* bchar -> achar -> uchar -> unreserved */
01888     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
01889     case '7': case '8': case '9':
01890     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
01891     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
01892     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
01893     case 'V': case 'W': case 'X': case 'Y': case 'Z':
01894     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
01895     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
01896     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
01897     case 'v': case 'w': case 'x': case 'y': case 'z':
01898     case '-': case '.': case '_': case '~':
01899     /* bchar -> achar -> uchar -> sub-delims-sh */
01900     case '!': case '$': case '\'': case '(': case ')': case '*':
01901     case '+': case ',':
01902     /* bchar -> achar -> uchar -> pct-encoded */
01903     case '%': /* HEXDIG chars are already included above */
01904       return true;
01905 
01906     default:
01907       return false;
01908   }
01909 }
01910 
01911 /***********************************************************************
01912  *
01913  * imap_parse_url_options()
01914  *
01915  * Parse the URL login options.
01916  */
01917 static CURLcode imap_parse_url_options(struct connectdata *conn)
01918 {
01919   CURLcode result = CURLE_OK;
01920   struct imap_conn *imapc = &conn->proto.imapc;
01921   const char *ptr = conn->options;
01922 
01923   imapc->sasl.resetprefs = TRUE;
01924 
01925   while(!result && ptr && *ptr) {
01926     const char *key = ptr;
01927     const char *value;
01928 
01929     while(*ptr && *ptr != '=')
01930         ptr++;
01931 
01932     value = ptr + 1;
01933 
01934     while(*ptr && *ptr != ';')
01935       ptr++;
01936 
01937     if(strncasecompare(key, "AUTH=", 5))
01938       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
01939                                                value, ptr - value);
01940     else
01941       result = CURLE_URL_MALFORMAT;
01942 
01943     if(*ptr == ';')
01944       ptr++;
01945   }
01946 
01947   switch(imapc->sasl.prefmech) {
01948   case SASL_AUTH_NONE:
01949     imapc->preftype = IMAP_TYPE_NONE;
01950     break;
01951   case SASL_AUTH_DEFAULT:
01952     imapc->preftype = IMAP_TYPE_ANY;
01953     break;
01954   default:
01955     imapc->preftype = IMAP_TYPE_SASL;
01956     break;
01957   }
01958 
01959   return result;
01960 }
01961 
01962 /***********************************************************************
01963  *
01964  * imap_parse_url_path()
01965  *
01966  * Parse the URL path into separate path components.
01967  *
01968  */
01969 static CURLcode imap_parse_url_path(struct connectdata *conn)
01970 {
01971   /* The imap struct is already initialised in imap_connect() */
01972   CURLcode result = CURLE_OK;
01973   struct Curl_easy *data = conn->data;
01974   struct IMAP *imap = data->req.protop;
01975   const char *begin = data->state.path;
01976   const char *ptr = begin;
01977 
01978   /* See how much of the URL is a valid path and decode it */
01979   while(imap_is_bchar(*ptr))
01980     ptr++;
01981 
01982   if(ptr != begin) {
01983     /* Remove the trailing slash if present */
01984     const char *end = ptr;
01985     if(end > begin && end[-1] == '/')
01986       end--;
01987 
01988     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
01989                             TRUE);
01990     if(result)
01991       return result;
01992   }
01993   else
01994     imap->mailbox = NULL;
01995 
01996   /* There can be any number of parameters in the form ";NAME=VALUE" */
01997   while(*ptr == ';') {
01998     char *name;
01999     char *value;
02000     size_t valuelen;
02001 
02002     /* Find the length of the name parameter */
02003     begin = ++ptr;
02004     while(*ptr && *ptr != '=')
02005       ptr++;
02006 
02007     if(!*ptr)
02008       return CURLE_URL_MALFORMAT;
02009 
02010     /* Decode the name parameter */
02011     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
02012     if(result)
02013       return result;
02014 
02015     /* Find the length of the value parameter */
02016     begin = ++ptr;
02017     while(imap_is_bchar(*ptr))
02018       ptr++;
02019 
02020     /* Decode the value parameter */
02021     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
02022     if(result) {
02023       free(name);
02024       return result;
02025     }
02026 
02027     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
02028 
02029     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
02030        PARTIAL) stripping of the trailing slash character if it is present.
02031 
02032        Note: Unknown parameters trigger a URL_MALFORMAT error. */
02033     if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
02034       if(valuelen > 0 && value[valuelen - 1] == '/')
02035         value[valuelen - 1] = '\0';
02036 
02037       imap->uidvalidity = value;
02038       value = NULL;
02039     }
02040     else if(strcasecompare(name, "UID") && !imap->uid) {
02041       if(valuelen > 0 && value[valuelen - 1] == '/')
02042         value[valuelen - 1] = '\0';
02043 
02044       imap->uid = value;
02045       value = NULL;
02046     }
02047     else if(strcasecompare(name, "SECTION") && !imap->section) {
02048       if(valuelen > 0 && value[valuelen - 1] == '/')
02049         value[valuelen - 1] = '\0';
02050 
02051       imap->section = value;
02052       value = NULL;
02053     }
02054     else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
02055       if(valuelen > 0 && value[valuelen - 1] == '/')
02056         value[valuelen - 1] = '\0';
02057 
02058       imap->partial = value;
02059       value = NULL;
02060     }
02061     else {
02062       free(name);
02063       free(value);
02064 
02065       return CURLE_URL_MALFORMAT;
02066     }
02067 
02068     free(name);
02069     free(value);
02070   }
02071 
02072   /* Does the URL contain a query parameter? Only valid when we have a mailbox
02073      and no UID as per RFC-5092 */
02074   if(imap->mailbox && !imap->uid && *ptr == '?') {
02075     /* Find the length of the query parameter */
02076     begin = ++ptr;
02077     while(imap_is_bchar(*ptr))
02078       ptr++;
02079 
02080     /* Decode the query parameter */
02081     result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
02082                             TRUE);
02083     if(result)
02084       return result;
02085   }
02086 
02087   /* Any extra stuff at the end of the URL is an error */
02088   if(*ptr)
02089     return CURLE_URL_MALFORMAT;
02090 
02091   return CURLE_OK;
02092 }
02093 
02094 /***********************************************************************
02095  *
02096  * imap_parse_custom_request()
02097  *
02098  * Parse the custom request.
02099  */
02100 static CURLcode imap_parse_custom_request(struct connectdata *conn)
02101 {
02102   CURLcode result = CURLE_OK;
02103   struct Curl_easy *data = conn->data;
02104   struct IMAP *imap = data->req.protop;
02105   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
02106 
02107   if(custom) {
02108     /* URL decode the custom request */
02109     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
02110 
02111     /* Extract the parameters if specified */
02112     if(!result) {
02113       const char *params = imap->custom;
02114 
02115       while(*params && *params != ' ')
02116         params++;
02117 
02118       if(*params) {
02119         imap->custom_params = strdup(params);
02120         imap->custom[params - imap->custom] = '\0';
02121 
02122         if(!imap->custom_params)
02123           result = CURLE_OUT_OF_MEMORY;
02124       }
02125     }
02126   }
02127 
02128   return result;
02129 }
02130 
02131 #endif /* CURL_DISABLE_IMAP */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:05