• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC2195 CRAM-MD5 authentication
22  * RFC2595 Using TLS with IMAP, POP3 and ACAP
23  * RFC2831 DIGEST-MD5 authentication
24  * RFC3501 IMAPv4 protocol
25  * RFC4422 Simple Authentication and Security Layer (SASL)
26  * RFC4616 PLAIN authentication
27  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28  * RFC4959 IMAP Extension for SASL Initial Client Response
29  * RFC5092 IMAP URL Scheme
30  * RFC6749 OAuth 2.0 Authorization Framework
31  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32  *
33  ***************************************************************************/
34 
35 #include "curl_setup.h"
36 
37 #ifndef CURL_DISABLE_IMAP
38 
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55 
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60 
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "hostip.h"
65 #include "progress.h"
66 #include "transfer.h"
67 #include "escape.h"
68 #include "http.h" /* for HTTP proxy tunnel stuff */
69 #include "socks.h"
70 #include "imap.h"
71 #include "mime.h"
72 #include "strtoofft.h"
73 #include "strcase.h"
74 #include "vtls/vtls.h"
75 #include "connect.h"
76 #include "strerror.h"
77 #include "select.h"
78 #include "multiif.h"
79 #include "url.h"
80 #include "strcase.h"
81 #include "curl_sasl.h"
82 #include "warnless.h"
83 
84 /* The last 3 #include files should be in this order */
85 #include "curl_printf.h"
86 #include "curl_memory.h"
87 #include "memdebug.h"
88 
89 /* Local API functions */
90 static CURLcode imap_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode imap_do(struct connectdata *conn, bool *done);
92 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
93                           bool premature);
94 static CURLcode imap_connect(struct connectdata *conn, bool *done);
95 static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
97 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
98                         int numsocks);
99 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode imap_setup_connection(struct connectdata *conn);
101 static char *imap_atom(const char *str, bool escape_only);
102 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...);
103 static CURLcode imap_parse_url_options(struct connectdata *conn);
104 static CURLcode imap_parse_url_path(struct connectdata *conn);
105 static CURLcode imap_parse_custom_request(struct connectdata *conn);
106 static CURLcode imap_perform_authenticate(struct connectdata *conn,
107                                           const char *mech,
108                                           const char *initresp);
109 static CURLcode imap_continue_authenticate(struct connectdata *conn,
110                                            const char *resp);
111 static void imap_get_message(char *buffer, char **outptr);
112 
113 /*
114  * IMAP protocol handler.
115  */
116 
117 const struct Curl_handler Curl_handler_imap = {
118   "IMAP",                           /* scheme */
119   imap_setup_connection,            /* setup_connection */
120   imap_do,                          /* do_it */
121   imap_done,                        /* done */
122   ZERO_NULL,                        /* do_more */
123   imap_connect,                     /* connect_it */
124   imap_multi_statemach,             /* connecting */
125   imap_doing,                       /* doing */
126   imap_getsock,                     /* proto_getsock */
127   imap_getsock,                     /* doing_getsock */
128   ZERO_NULL,                        /* domore_getsock */
129   ZERO_NULL,                        /* perform_getsock */
130   imap_disconnect,                  /* disconnect */
131   ZERO_NULL,                        /* readwrite */
132   ZERO_NULL,                        /* connection_check */
133   PORT_IMAP,                        /* defport */
134   CURLPROTO_IMAP,                   /* protocol */
135   PROTOPT_CLOSEACTION|              /* flags */
136   PROTOPT_URLOPTIONS
137 };
138 
139 #ifdef USE_SSL
140 /*
141  * IMAPS protocol handler.
142  */
143 
144 const struct Curl_handler Curl_handler_imaps = {
145   "IMAPS",                          /* scheme */
146   imap_setup_connection,            /* setup_connection */
147   imap_do,                          /* do_it */
148   imap_done,                        /* done */
149   ZERO_NULL,                        /* do_more */
150   imap_connect,                     /* connect_it */
151   imap_multi_statemach,             /* connecting */
152   imap_doing,                       /* doing */
153   imap_getsock,                     /* proto_getsock */
154   imap_getsock,                     /* doing_getsock */
155   ZERO_NULL,                        /* domore_getsock */
156   ZERO_NULL,                        /* perform_getsock */
157   imap_disconnect,                  /* disconnect */
158   ZERO_NULL,                        /* readwrite */
159   ZERO_NULL,                        /* connection_check */
160   PORT_IMAPS,                       /* defport */
161   CURLPROTO_IMAPS,                  /* protocol */
162   PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
163 };
164 #endif
165 
166 #define IMAP_RESP_OK       1
167 #define IMAP_RESP_NOT_OK   2
168 #define IMAP_RESP_PREAUTH  3
169 
170 /* SASL parameters for the imap protocol */
171 static const struct SASLproto saslimap = {
172   "imap",                     /* The service name */
173   '+',                        /* Code received when continuation is expected */
174   IMAP_RESP_OK,               /* Code to receive upon authentication success */
175   0,                          /* Maximum initial response length (no max) */
176   imap_perform_authenticate,  /* Send authentication command */
177   imap_continue_authenticate, /* Send authentication continuation */
178   imap_get_message            /* Get SASL response message */
179 };
180 
181 
182 #ifdef USE_SSL
imap_to_imaps(struct connectdata * conn)183 static void imap_to_imaps(struct connectdata *conn)
184 {
185   /* Change the connection handler */
186   conn->handler = &Curl_handler_imaps;
187 
188   /* Set the connection's upgraded to TLS flag */
189   conn->tls_upgraded = TRUE;
190 }
191 #else
192 #define imap_to_imaps(x) Curl_nop_stmt
193 #endif
194 
195 /***********************************************************************
196  *
197  * imap_matchresp()
198  *
199  * Determines whether the untagged response is related to the specified
200  * command by checking if it is in format "* <command-name> ..." or
201  * "* <number> <command-name> ...".
202  *
203  * The "* " marker is assumed to have already been checked by the caller.
204  */
imap_matchresp(const char * line,size_t len,const char * cmd)205 static bool imap_matchresp(const char *line, size_t len, const char *cmd)
206 {
207   const char *end = line + len;
208   size_t cmd_len = strlen(cmd);
209 
210   /* Skip the untagged response marker */
211   line += 2;
212 
213   /* Do we have a number after the marker? */
214   if(line < end && ISDIGIT(*line)) {
215     /* Skip the number */
216     do
217       line++;
218     while(line < end && ISDIGIT(*line));
219 
220     /* Do we have the space character? */
221     if(line == end || *line != ' ')
222       return FALSE;
223 
224     line++;
225   }
226 
227   /* Does the command name match and is it followed by a space character or at
228      the end of line? */
229   if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
230      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
231     return TRUE;
232 
233   return FALSE;
234 }
235 
236 /***********************************************************************
237  *
238  * imap_endofresp()
239  *
240  * Checks whether the given string is a valid tagged, untagged or continuation
241  * response which can be processed by the response handler.
242  */
imap_endofresp(struct connectdata * conn,char * line,size_t len,int * resp)243 static bool imap_endofresp(struct connectdata *conn, char *line, size_t len,
244                            int *resp)
245 {
246   struct IMAP *imap = conn->data->req.protop;
247   struct imap_conn *imapc = &conn->proto.imapc;
248   const char *id = imapc->resptag;
249   size_t id_len = strlen(id);
250 
251   /* Do we have a tagged command response? */
252   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
253     line += id_len + 1;
254     len -= id_len + 1;
255 
256     if(len >= 2 && !memcmp(line, "OK", 2))
257       *resp = IMAP_RESP_OK;
258     else if(len >= 7 && !memcmp(line, "PREAUTH", 7))
259       *resp = IMAP_RESP_PREAUTH;
260     else
261       *resp = IMAP_RESP_NOT_OK;
262 
263     return TRUE;
264   }
265 
266   /* Do we have an untagged command response? */
267   if(len >= 2 && !memcmp("* ", line, 2)) {
268     switch(imapc->state) {
269       /* States which are interested in untagged responses */
270       case IMAP_CAPABILITY:
271         if(!imap_matchresp(line, len, "CAPABILITY"))
272           return FALSE;
273         break;
274 
275       case IMAP_LIST:
276         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
277           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
278            (!strcasecompare(imap->custom, "STORE") ||
279             !imap_matchresp(line, len, "FETCH")) &&
280            !strcasecompare(imap->custom, "SELECT") &&
281            !strcasecompare(imap->custom, "EXAMINE") &&
282            !strcasecompare(imap->custom, "SEARCH") &&
283            !strcasecompare(imap->custom, "EXPUNGE") &&
284            !strcasecompare(imap->custom, "LSUB") &&
285            !strcasecompare(imap->custom, "UID") &&
286            !strcasecompare(imap->custom, "NOOP")))
287           return FALSE;
288         break;
289 
290       case IMAP_SELECT:
291         /* SELECT is special in that its untagged responses do not have a
292            common prefix so accept anything! */
293         break;
294 
295       case IMAP_FETCH:
296         if(!imap_matchresp(line, len, "FETCH"))
297           return FALSE;
298         break;
299 
300       case IMAP_SEARCH:
301         if(!imap_matchresp(line, len, "SEARCH"))
302           return FALSE;
303         break;
304 
305       /* Ignore other untagged responses */
306       default:
307         return FALSE;
308     }
309 
310     *resp = '*';
311     return TRUE;
312   }
313 
314   /* Do we have a continuation response? This should be a + symbol followed by
315      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
316      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
317      some e-mail servers ignore this and only send a single + instead. */
318   if(imap && !imap->custom && ((len == 3 && !memcmp("+", line, 1)) ||
319      (len >= 2 && !memcmp("+ ", line, 2)))) {
320     switch(imapc->state) {
321       /* States which are interested in continuation responses */
322       case IMAP_AUTHENTICATE:
323       case IMAP_APPEND:
324         *resp = '+';
325         break;
326 
327       default:
328         failf(conn->data, "Unexpected continuation response");
329         *resp = -1;
330         break;
331     }
332 
333     return TRUE;
334   }
335 
336   return FALSE; /* Nothing for us */
337 }
338 
339 /***********************************************************************
340  *
341  * imap_get_message()
342  *
343  * Gets the authentication message from the response buffer.
344  */
imap_get_message(char * buffer,char ** outptr)345 static void imap_get_message(char *buffer, char **outptr)
346 {
347   size_t len = strlen(buffer);
348   char *message = NULL;
349 
350   if(len > 2) {
351     /* Find the start of the message */
352     len -= 2;
353     for(message = buffer + 2; *message == ' ' || *message == '\t';
354         message++, len--)
355       ;
356 
357     /* Find the end of the message */
358     for(; len--;)
359       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
360          message[len] != '\t')
361         break;
362 
363     /* Terminate the message */
364     if(++len) {
365       message[len] = '\0';
366     }
367   }
368   else
369     /* junk input => zero length output */
370     message = &buffer[len];
371 
372   *outptr = message;
373 }
374 
375 /***********************************************************************
376  *
377  * state()
378  *
379  * This is the ONLY way to change IMAP state!
380  */
state(struct connectdata * conn,imapstate newstate)381 static void state(struct connectdata *conn, imapstate newstate)
382 {
383   struct imap_conn *imapc = &conn->proto.imapc;
384 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
385   /* for debug purposes */
386   static const char * const names[]={
387     "STOP",
388     "SERVERGREET",
389     "CAPABILITY",
390     "STARTTLS",
391     "UPGRADETLS",
392     "AUTHENTICATE",
393     "LOGIN",
394     "LIST",
395     "SELECT",
396     "FETCH",
397     "FETCH_FINAL",
398     "APPEND",
399     "APPEND_FINAL",
400     "SEARCH",
401     "LOGOUT",
402     /* LAST */
403   };
404 
405   if(imapc->state != newstate)
406     infof(conn->data, "IMAP %p state change from %s to %s\n",
407           (void *)imapc, names[imapc->state], names[newstate]);
408 #endif
409 
410   imapc->state = newstate;
411 }
412 
413 /***********************************************************************
414  *
415  * imap_perform_capability()
416  *
417  * Sends the CAPABILITY command in order to obtain a list of server side
418  * supported capabilities.
419  */
imap_perform_capability(struct connectdata * conn)420 static CURLcode imap_perform_capability(struct connectdata *conn)
421 {
422   CURLcode result = CURLE_OK;
423   struct imap_conn *imapc = &conn->proto.imapc;
424 
425   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
426   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
427   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
428 
429   /* Send the CAPABILITY command */
430   result = imap_sendf(conn, "CAPABILITY");
431 
432   if(!result)
433     state(conn, IMAP_CAPABILITY);
434 
435   return result;
436 }
437 
438 /***********************************************************************
439  *
440  * imap_perform_starttls()
441  *
442  * Sends the STARTTLS command to start the upgrade to TLS.
443  */
imap_perform_starttls(struct connectdata * conn)444 static CURLcode imap_perform_starttls(struct connectdata *conn)
445 {
446   CURLcode result = CURLE_OK;
447 
448   /* Send the STARTTLS command */
449   result = imap_sendf(conn, "STARTTLS");
450 
451   if(!result)
452     state(conn, IMAP_STARTTLS);
453 
454   return result;
455 }
456 
457 /***********************************************************************
458  *
459  * imap_perform_upgrade_tls()
460  *
461  * Performs the upgrade to TLS.
462  */
imap_perform_upgrade_tls(struct connectdata * conn)463 static CURLcode imap_perform_upgrade_tls(struct connectdata *conn)
464 {
465   CURLcode result = CURLE_OK;
466   struct imap_conn *imapc = &conn->proto.imapc;
467 
468   /* Start the SSL connection */
469   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
470 
471   if(!result) {
472     if(imapc->state != IMAP_UPGRADETLS)
473       state(conn, IMAP_UPGRADETLS);
474 
475     if(imapc->ssldone) {
476       imap_to_imaps(conn);
477       result = imap_perform_capability(conn);
478     }
479   }
480 
481   return result;
482 }
483 
484 /***********************************************************************
485  *
486  * imap_perform_login()
487  *
488  * Sends a clear text LOGIN command to authenticate with.
489  */
imap_perform_login(struct connectdata * conn)490 static CURLcode imap_perform_login(struct connectdata *conn)
491 {
492   CURLcode result = CURLE_OK;
493   char *user;
494   char *passwd;
495 
496   /* Check we have a username and password to authenticate with and end the
497      connect phase if we don't */
498   if(!conn->bits.user_passwd) {
499     state(conn, IMAP_STOP);
500 
501     return result;
502   }
503 
504   /* Make sure the username and password are in the correct atom format */
505   user = imap_atom(conn->user, false);
506   passwd = imap_atom(conn->passwd, false);
507 
508   /* Send the LOGIN command */
509   result = imap_sendf(conn, "LOGIN %s %s", user ? user : "",
510                       passwd ? passwd : "");
511 
512   free(user);
513   free(passwd);
514 
515   if(!result)
516     state(conn, IMAP_LOGIN);
517 
518   return result;
519 }
520 
521 /***********************************************************************
522  *
523  * imap_perform_authenticate()
524  *
525  * Sends an AUTHENTICATE command allowing the client to login with the given
526  * SASL authentication mechanism.
527  */
imap_perform_authenticate(struct connectdata * conn,const char * mech,const char * initresp)528 static CURLcode imap_perform_authenticate(struct connectdata *conn,
529                                           const char *mech,
530                                           const char *initresp)
531 {
532   CURLcode result = CURLE_OK;
533 
534   if(initresp) {
535     /* Send the AUTHENTICATE command with the initial response */
536     result = imap_sendf(conn, "AUTHENTICATE %s %s", mech, initresp);
537   }
538   else {
539     /* Send the AUTHENTICATE command */
540     result = imap_sendf(conn, "AUTHENTICATE %s", mech);
541   }
542 
543   return result;
544 }
545 
546 /***********************************************************************
547  *
548  * imap_continue_authenticate()
549  *
550  * Sends SASL continuation data or cancellation.
551  */
imap_continue_authenticate(struct connectdata * conn,const char * resp)552 static CURLcode imap_continue_authenticate(struct connectdata *conn,
553                                            const char *resp)
554 {
555   struct imap_conn *imapc = &conn->proto.imapc;
556 
557   return Curl_pp_sendf(&imapc->pp, "%s", resp);
558 }
559 
560 /***********************************************************************
561  *
562  * imap_perform_authentication()
563  *
564  * Initiates the authentication sequence, with the appropriate SASL
565  * authentication mechanism, falling back to clear text should a common
566  * mechanism not be available between the client and server.
567  */
imap_perform_authentication(struct connectdata * conn)568 static CURLcode imap_perform_authentication(struct connectdata *conn)
569 {
570   CURLcode result = CURLE_OK;
571   struct imap_conn *imapc = &conn->proto.imapc;
572   saslprogress progress;
573 
574   /* Check if already authenticated OR if there is enough data to authenticate
575      with and end the connect phase if we don't */
576   if(imapc->preauth ||
577      !Curl_sasl_can_authenticate(&imapc->sasl, conn)) {
578     state(conn, IMAP_STOP);
579     return result;
580   }
581 
582   /* Calculate the SASL login details */
583   result = Curl_sasl_start(&imapc->sasl, conn, imapc->ir_supported, &progress);
584 
585   if(!result) {
586     if(progress == SASL_INPROGRESS)
587       state(conn, IMAP_AUTHENTICATE);
588     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
589       /* Perform clear text authentication */
590       result = imap_perform_login(conn);
591     else {
592       /* Other mechanisms not supported */
593       infof(conn->data, "No known authentication mechanisms supported!\n");
594       result = CURLE_LOGIN_DENIED;
595     }
596   }
597 
598   return result;
599 }
600 
601 /***********************************************************************
602  *
603  * imap_perform_list()
604  *
605  * Sends a LIST command or an alternative custom request.
606  */
imap_perform_list(struct connectdata * conn)607 static CURLcode imap_perform_list(struct connectdata *conn)
608 {
609   CURLcode result = CURLE_OK;
610   struct Curl_easy *data = conn->data;
611   struct IMAP *imap = data->req.protop;
612   char *mailbox;
613 
614   if(imap->custom)
615     /* Send the custom request */
616     result = imap_sendf(conn, "%s%s", imap->custom,
617                         imap->custom_params ? imap->custom_params : "");
618   else {
619     /* Make sure the mailbox is in the correct atom format if necessary */
620     mailbox = imap->mailbox ? imap_atom(imap->mailbox, true) : strdup("");
621     if(!mailbox)
622       return CURLE_OUT_OF_MEMORY;
623 
624     /* Send the LIST command */
625     result = imap_sendf(conn, "LIST \"%s\" *", mailbox);
626 
627     free(mailbox);
628   }
629 
630   if(!result)
631     state(conn, IMAP_LIST);
632 
633   return result;
634 }
635 
636 /***********************************************************************
637  *
638  * imap_perform_select()
639  *
640  * Sends a SELECT command to ask the server to change the selected mailbox.
641  */
imap_perform_select(struct connectdata * conn)642 static CURLcode imap_perform_select(struct connectdata *conn)
643 {
644   CURLcode result = CURLE_OK;
645   struct Curl_easy *data = conn->data;
646   struct IMAP *imap = data->req.protop;
647   struct imap_conn *imapc = &conn->proto.imapc;
648   char *mailbox;
649 
650   /* Invalidate old information as we are switching mailboxes */
651   Curl_safefree(imapc->mailbox);
652   Curl_safefree(imapc->mailbox_uidvalidity);
653 
654   /* Check we have a mailbox */
655   if(!imap->mailbox) {
656     failf(conn->data, "Cannot SELECT without a mailbox.");
657     return CURLE_URL_MALFORMAT;
658   }
659 
660   /* Make sure the mailbox is in the correct atom format */
661   mailbox = imap_atom(imap->mailbox, false);
662   if(!mailbox)
663     return CURLE_OUT_OF_MEMORY;
664 
665   /* Send the SELECT command */
666   result = imap_sendf(conn, "SELECT %s", mailbox);
667 
668   free(mailbox);
669 
670   if(!result)
671     state(conn, IMAP_SELECT);
672 
673   return result;
674 }
675 
676 /***********************************************************************
677  *
678  * imap_perform_fetch()
679  *
680  * Sends a FETCH command to initiate the download of a message.
681  */
imap_perform_fetch(struct connectdata * conn)682 static CURLcode imap_perform_fetch(struct connectdata *conn)
683 {
684   CURLcode result = CURLE_OK;
685   struct IMAP *imap = conn->data->req.protop;
686 
687   /* Check we have a UID */
688   if(!imap->uid) {
689     failf(conn->data, "Cannot FETCH without a UID.");
690     return CURLE_URL_MALFORMAT;
691   }
692 
693   /* Send the FETCH command */
694   if(imap->partial)
695     result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
696                         imap->uid,
697                         imap->section ? imap->section : "",
698                         imap->partial);
699   else
700     result = imap_sendf(conn, "FETCH %s BODY[%s]",
701                         imap->uid,
702                         imap->section ? imap->section : "");
703 
704   if(!result)
705     state(conn, IMAP_FETCH);
706 
707   return result;
708 }
709 
710 /***********************************************************************
711  *
712  * imap_perform_append()
713  *
714  * Sends an APPEND command to initiate the upload of a message.
715  */
imap_perform_append(struct connectdata * conn)716 static CURLcode imap_perform_append(struct connectdata *conn)
717 {
718   CURLcode result = CURLE_OK;
719   struct Curl_easy *data = conn->data;
720   struct IMAP *imap = data->req.protop;
721   char *mailbox;
722 
723   /* Check we have a mailbox */
724   if(!imap->mailbox) {
725     failf(data, "Cannot APPEND without a mailbox.");
726     return CURLE_URL_MALFORMAT;
727   }
728 
729   /* Prepare the mime data if some. */
730   if(data->set.mimepost.kind != MIMEKIND_NONE) {
731     /* Use the whole structure as data. */
732     data->set.mimepost.flags &= ~MIME_BODY_ONLY;
733 
734     /* Add external headers and mime version. */
735     curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
736     result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
737                                        NULL, MIMESTRATEGY_MAIL);
738 
739     if(!result)
740       if(!Curl_checkheaders(conn, "Mime-Version"))
741         result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
742                                       "Mime-Version: 1.0");
743 
744     /* Make sure we will read the entire mime structure. */
745     if(!result)
746       result = Curl_mime_rewind(&data->set.mimepost);
747 
748     if(result)
749       return result;
750 
751     data->state.infilesize = Curl_mime_size(&data->set.mimepost);
752 
753     /* Read from mime structure. */
754     data->state.fread_func = (curl_read_callback) Curl_mime_read;
755     data->state.in = (void *) &data->set.mimepost;
756   }
757 
758   /* Check we know the size of the upload */
759   if(data->state.infilesize < 0) {
760     failf(data, "Cannot APPEND with unknown input file size\n");
761     return CURLE_UPLOAD_FAILED;
762   }
763 
764   /* Make sure the mailbox is in the correct atom format */
765   mailbox = imap_atom(imap->mailbox, false);
766   if(!mailbox)
767     return CURLE_OUT_OF_MEMORY;
768 
769   /* Send the APPEND command */
770   result = imap_sendf(conn, "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
771                       mailbox, data->state.infilesize);
772 
773   free(mailbox);
774 
775   if(!result)
776     state(conn, IMAP_APPEND);
777 
778   return result;
779 }
780 
781 /***********************************************************************
782  *
783  * imap_perform_search()
784  *
785  * Sends a SEARCH command.
786  */
imap_perform_search(struct connectdata * conn)787 static CURLcode imap_perform_search(struct connectdata *conn)
788 {
789   CURLcode result = CURLE_OK;
790   struct IMAP *imap = conn->data->req.protop;
791 
792   /* Check we have a query string */
793   if(!imap->query) {
794     failf(conn->data, "Cannot SEARCH without a query string.");
795     return CURLE_URL_MALFORMAT;
796   }
797 
798   /* Send the SEARCH command */
799   result = imap_sendf(conn, "SEARCH %s", imap->query);
800 
801   if(!result)
802     state(conn, IMAP_SEARCH);
803 
804   return result;
805 }
806 
807 /***********************************************************************
808  *
809  * imap_perform_logout()
810  *
811  * Performs the logout action prior to sclose() being called.
812  */
imap_perform_logout(struct connectdata * conn)813 static CURLcode imap_perform_logout(struct connectdata *conn)
814 {
815   CURLcode result = CURLE_OK;
816 
817   /* Send the LOGOUT command */
818   result = imap_sendf(conn, "LOGOUT");
819 
820   if(!result)
821     state(conn, IMAP_LOGOUT);
822 
823   return result;
824 }
825 
826 /* For the initial server greeting */
imap_state_servergreet_resp(struct connectdata * conn,int imapcode,imapstate instate)827 static CURLcode imap_state_servergreet_resp(struct connectdata *conn,
828                                             int imapcode,
829                                             imapstate instate)
830 {
831   struct Curl_easy *data = conn->data;
832   (void)instate; /* no use for this yet */
833 
834   if(imapcode == IMAP_RESP_PREAUTH) {
835     /* PREAUTH */
836     struct imap_conn *imapc = &conn->proto.imapc;
837     imapc->preauth = TRUE;
838     infof(data, "PREAUTH connection, already authenticated!\n");
839   }
840   else if(imapcode != IMAP_RESP_OK) {
841     failf(data, "Got unexpected imap-server response");
842     return CURLE_WEIRD_SERVER_REPLY;
843   }
844 
845   return imap_perform_capability(conn);
846 }
847 
848 /* For CAPABILITY responses */
imap_state_capability_resp(struct connectdata * conn,int imapcode,imapstate instate)849 static CURLcode imap_state_capability_resp(struct connectdata *conn,
850                                            int imapcode,
851                                            imapstate instate)
852 {
853   CURLcode result = CURLE_OK;
854   struct Curl_easy *data = conn->data;
855   struct imap_conn *imapc = &conn->proto.imapc;
856   const char *line = data->state.buffer;
857   size_t wordlen;
858 
859   (void)instate; /* no use for this yet */
860 
861   /* Do we have a untagged response? */
862   if(imapcode == '*') {
863     line += 2;
864 
865     /* Loop through the data line */
866     for(;;) {
867       while(*line &&
868             (*line == ' ' || *line == '\t' ||
869               *line == '\r' || *line == '\n')) {
870 
871         line++;
872       }
873 
874       if(!*line)
875         break;
876 
877       /* Extract the word */
878       for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
879             line[wordlen] != '\t' && line[wordlen] != '\r' &&
880             line[wordlen] != '\n';)
881         wordlen++;
882 
883       /* Does the server support the STARTTLS capability? */
884       if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
885         imapc->tls_supported = TRUE;
886 
887       /* Has the server explicitly disabled clear text authentication? */
888       else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
889         imapc->login_disabled = TRUE;
890 
891       /* Does the server support the SASL-IR capability? */
892       else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
893         imapc->ir_supported = TRUE;
894 
895       /* Do we have a SASL based authentication mechanism? */
896       else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
897         size_t llen;
898         unsigned int mechbit;
899 
900         line += 5;
901         wordlen -= 5;
902 
903         /* Test the word for a matching authentication mechanism */
904         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
905         if(mechbit && llen == wordlen)
906           imapc->sasl.authmechs |= mechbit;
907       }
908 
909       line += wordlen;
910     }
911   }
912   else if(imapcode == IMAP_RESP_OK) {
913     if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
914       /* We don't have a SSL/TLS connection yet, but SSL is requested */
915       if(imapc->tls_supported)
916         /* Switch to TLS connection now */
917         result = imap_perform_starttls(conn);
918       else if(data->set.use_ssl == CURLUSESSL_TRY)
919         /* Fallback and carry on with authentication */
920         result = imap_perform_authentication(conn);
921       else {
922         failf(data, "STARTTLS not supported.");
923         result = CURLE_USE_SSL_FAILED;
924       }
925     }
926     else
927       result = imap_perform_authentication(conn);
928   }
929   else
930     result = imap_perform_authentication(conn);
931 
932   return result;
933 }
934 
935 /* For STARTTLS responses */
imap_state_starttls_resp(struct connectdata * conn,int imapcode,imapstate instate)936 static CURLcode imap_state_starttls_resp(struct connectdata *conn,
937                                          int imapcode,
938                                          imapstate instate)
939 {
940   CURLcode result = CURLE_OK;
941   struct Curl_easy *data = conn->data;
942 
943   (void)instate; /* no use for this yet */
944 
945   if(imapcode != IMAP_RESP_OK) {
946     if(data->set.use_ssl != CURLUSESSL_TRY) {
947       failf(data, "STARTTLS denied");
948       result = CURLE_USE_SSL_FAILED;
949     }
950     else
951       result = imap_perform_authentication(conn);
952   }
953   else
954     result = imap_perform_upgrade_tls(conn);
955 
956   return result;
957 }
958 
959 /* For SASL authentication responses */
imap_state_auth_resp(struct connectdata * conn,int imapcode,imapstate instate)960 static CURLcode imap_state_auth_resp(struct connectdata *conn,
961                                      int imapcode,
962                                      imapstate instate)
963 {
964   CURLcode result = CURLE_OK;
965   struct Curl_easy *data = conn->data;
966   struct imap_conn *imapc = &conn->proto.imapc;
967   saslprogress progress;
968 
969   (void)instate; /* no use for this yet */
970 
971   result = Curl_sasl_continue(&imapc->sasl, conn, imapcode, &progress);
972   if(!result)
973     switch(progress) {
974     case SASL_DONE:
975       state(conn, IMAP_STOP);  /* Authenticated */
976       break;
977     case SASL_IDLE:            /* No mechanism left after cancellation */
978       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
979         /* Perform clear text authentication */
980         result = imap_perform_login(conn);
981       else {
982         failf(data, "Authentication cancelled");
983         result = CURLE_LOGIN_DENIED;
984       }
985       break;
986     default:
987       break;
988     }
989 
990   return result;
991 }
992 
993 /* For LOGIN responses */
imap_state_login_resp(struct connectdata * conn,int imapcode,imapstate instate)994 static CURLcode imap_state_login_resp(struct connectdata *conn,
995                                       int imapcode,
996                                       imapstate instate)
997 {
998   CURLcode result = CURLE_OK;
999   struct Curl_easy *data = conn->data;
1000 
1001   (void)instate; /* no use for this yet */
1002 
1003   if(imapcode != IMAP_RESP_OK) {
1004     failf(data, "Access denied. %c", imapcode);
1005     result = CURLE_LOGIN_DENIED;
1006   }
1007   else
1008     /* End of connect phase */
1009     state(conn, IMAP_STOP);
1010 
1011   return result;
1012 }
1013 
1014 /* For LIST and SEARCH responses */
imap_state_listsearch_resp(struct connectdata * conn,int imapcode,imapstate instate)1015 static CURLcode imap_state_listsearch_resp(struct connectdata *conn,
1016                                            int imapcode,
1017                                            imapstate instate)
1018 {
1019   CURLcode result = CURLE_OK;
1020   char *line = conn->data->state.buffer;
1021   size_t len = strlen(line);
1022 
1023   (void)instate; /* No use for this yet */
1024 
1025   if(imapcode == '*') {
1026     /* Temporarily add the LF character back and send as body to the client */
1027     line[len] = '\n';
1028     result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1029     line[len] = '\0';
1030   }
1031   else if(imapcode != IMAP_RESP_OK)
1032     result = CURLE_QUOTE_ERROR; /* TODO: Fix error code */
1033   else
1034     /* End of DO phase */
1035     state(conn, IMAP_STOP);
1036 
1037   return result;
1038 }
1039 
1040 /* For SELECT responses */
imap_state_select_resp(struct connectdata * conn,int imapcode,imapstate instate)1041 static CURLcode imap_state_select_resp(struct connectdata *conn, int imapcode,
1042                                        imapstate instate)
1043 {
1044   CURLcode result = CURLE_OK;
1045   struct Curl_easy *data = conn->data;
1046   struct IMAP *imap = conn->data->req.protop;
1047   struct imap_conn *imapc = &conn->proto.imapc;
1048   const char *line = data->state.buffer;
1049   char tmp[20];
1050 
1051   (void)instate; /* no use for this yet */
1052 
1053   if(imapcode == '*') {
1054     /* See if this is an UIDVALIDITY response */
1055     if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
1056       Curl_safefree(imapc->mailbox_uidvalidity);
1057       imapc->mailbox_uidvalidity = strdup(tmp);
1058     }
1059   }
1060   else if(imapcode == IMAP_RESP_OK) {
1061     /* Check if the UIDVALIDITY has been specified and matches */
1062     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1063        !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1064       failf(conn->data, "Mailbox UIDVALIDITY has changed");
1065       result = CURLE_REMOTE_FILE_NOT_FOUND;
1066     }
1067     else {
1068       /* Note the currently opened mailbox on this connection */
1069       imapc->mailbox = strdup(imap->mailbox);
1070 
1071       if(imap->custom)
1072         result = imap_perform_list(conn);
1073       else if(imap->query)
1074         result = imap_perform_search(conn);
1075       else
1076         result = imap_perform_fetch(conn);
1077     }
1078   }
1079   else {
1080     failf(data, "Select failed");
1081     result = CURLE_LOGIN_DENIED;
1082   }
1083 
1084   return result;
1085 }
1086 
1087 /* For the (first line of the) FETCH responses */
imap_state_fetch_resp(struct connectdata * conn,int imapcode,imapstate instate)1088 static CURLcode imap_state_fetch_resp(struct connectdata *conn, int imapcode,
1089                                       imapstate instate)
1090 {
1091   CURLcode result = CURLE_OK;
1092   struct Curl_easy *data = conn->data;
1093   struct imap_conn *imapc = &conn->proto.imapc;
1094   struct pingpong *pp = &imapc->pp;
1095   const char *ptr = data->state.buffer;
1096   bool parsed = FALSE;
1097   curl_off_t size = 0;
1098 
1099   (void)instate; /* no use for this yet */
1100 
1101   if(imapcode != '*') {
1102     Curl_pgrsSetDownloadSize(data, -1);
1103     state(conn, IMAP_STOP);
1104     return CURLE_REMOTE_FILE_NOT_FOUND; /* TODO: Fix error code */
1105   }
1106 
1107   /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1108      the continuation data contained within the curly brackets */
1109   while(*ptr && (*ptr != '{'))
1110     ptr++;
1111 
1112   if(*ptr == '{') {
1113     char *endptr;
1114     if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size)) {
1115       if(endptr - ptr > 1 && endptr[0] == '}' &&
1116          endptr[1] == '\r' && endptr[2] == '\0')
1117         parsed = TRUE;
1118     }
1119   }
1120 
1121   if(parsed) {
1122     infof(data, "Found %" CURL_FORMAT_CURL_OFF_TU " bytes to download\n",
1123           size);
1124     Curl_pgrsSetDownloadSize(data, size);
1125 
1126     if(pp->cache) {
1127       /* At this point there is a bunch of data in the header "cache" that is
1128          actually body content, send it as body and then skip it. Do note
1129          that there may even be additional "headers" after the body. */
1130       size_t chunk = pp->cache_size;
1131 
1132       if(chunk > (size_t)size)
1133         /* The conversion from curl_off_t to size_t is always fine here */
1134         chunk = (size_t)size;
1135 
1136       if(!chunk) {
1137         /* no size, we're done with the data */
1138         state(conn, IMAP_STOP);
1139         return CURLE_OK;
1140       }
1141       result = Curl_client_write(conn, CLIENTWRITE_BODY, pp->cache, chunk);
1142       if(result)
1143         return result;
1144 
1145       data->req.bytecount += chunk;
1146 
1147       infof(data, "Written %" CURL_FORMAT_CURL_OFF_TU
1148             " bytes, %" CURL_FORMAT_CURL_OFF_TU
1149             " bytes are left for transfer\n", (curl_off_t)chunk,
1150             size - chunk);
1151 
1152       /* Have we used the entire cache or just part of it?*/
1153       if(pp->cache_size > chunk) {
1154         /* Only part of it so shrink the cache to fit the trailing data */
1155         memmove(pp->cache, pp->cache + chunk, pp->cache_size - chunk);
1156         pp->cache_size -= chunk;
1157       }
1158       else {
1159         /* Free the cache */
1160         Curl_safefree(pp->cache);
1161 
1162         /* Reset the cache size */
1163         pp->cache_size = 0;
1164       }
1165     }
1166 
1167     if(data->req.bytecount == size)
1168       /* The entire data is already transferred! */
1169       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1170     else {
1171       /* IMAP download */
1172       data->req.maxdownload = size;
1173       Curl_setup_transfer(conn, FIRSTSOCKET, size, FALSE, NULL, -1, NULL);
1174     }
1175   }
1176   else {
1177     /* We don't know how to parse this line */
1178     failf(pp->conn->data, "Failed to parse FETCH response.");
1179     result = CURLE_WEIRD_SERVER_REPLY;
1180   }
1181 
1182   /* End of DO phase */
1183   state(conn, IMAP_STOP);
1184 
1185   return result;
1186 }
1187 
1188 /* For final FETCH responses performed after the download */
imap_state_fetch_final_resp(struct connectdata * conn,int imapcode,imapstate instate)1189 static CURLcode imap_state_fetch_final_resp(struct connectdata *conn,
1190                                             int imapcode,
1191                                             imapstate instate)
1192 {
1193   CURLcode result = CURLE_OK;
1194 
1195   (void)instate; /* No use for this yet */
1196 
1197   if(imapcode != IMAP_RESP_OK)
1198     result = CURLE_WEIRD_SERVER_REPLY;
1199   else
1200     /* End of DONE phase */
1201     state(conn, IMAP_STOP);
1202 
1203   return result;
1204 }
1205 
1206 /* For APPEND responses */
imap_state_append_resp(struct connectdata * conn,int imapcode,imapstate instate)1207 static CURLcode imap_state_append_resp(struct connectdata *conn, int imapcode,
1208                                        imapstate instate)
1209 {
1210   CURLcode result = CURLE_OK;
1211   struct Curl_easy *data = conn->data;
1212 
1213   (void)instate; /* No use for this yet */
1214 
1215   if(imapcode != '+') {
1216     result = CURLE_UPLOAD_FAILED;
1217   }
1218   else {
1219     /* Set the progress upload size */
1220     Curl_pgrsSetUploadSize(data, data->state.infilesize);
1221 
1222     /* IMAP upload */
1223     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1224 
1225     /* End of DO phase */
1226     state(conn, IMAP_STOP);
1227   }
1228 
1229   return result;
1230 }
1231 
1232 /* For final APPEND responses performed after the upload */
imap_state_append_final_resp(struct connectdata * conn,int imapcode,imapstate instate)1233 static CURLcode imap_state_append_final_resp(struct connectdata *conn,
1234                                              int imapcode,
1235                                              imapstate instate)
1236 {
1237   CURLcode result = CURLE_OK;
1238 
1239   (void)instate; /* No use for this yet */
1240 
1241   if(imapcode != IMAP_RESP_OK)
1242     result = CURLE_UPLOAD_FAILED;
1243   else
1244     /* End of DONE phase */
1245     state(conn, IMAP_STOP);
1246 
1247   return result;
1248 }
1249 
imap_statemach_act(struct connectdata * conn)1250 static CURLcode imap_statemach_act(struct connectdata *conn)
1251 {
1252   CURLcode result = CURLE_OK;
1253   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1254   int imapcode;
1255   struct imap_conn *imapc = &conn->proto.imapc;
1256   struct pingpong *pp = &imapc->pp;
1257   size_t nread = 0;
1258 
1259   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1260   if(imapc->state == IMAP_UPGRADETLS)
1261     return imap_perform_upgrade_tls(conn);
1262 
1263   /* Flush any data that needs to be sent */
1264   if(pp->sendleft)
1265     return Curl_pp_flushsend(pp);
1266 
1267   do {
1268     /* Read the response from the server */
1269     result = Curl_pp_readresp(sock, pp, &imapcode, &nread);
1270     if(result)
1271       return result;
1272 
1273     /* Was there an error parsing the response line? */
1274     if(imapcode == -1)
1275       return CURLE_WEIRD_SERVER_REPLY;
1276 
1277     if(!imapcode)
1278       break;
1279 
1280     /* We have now received a full IMAP server response */
1281     switch(imapc->state) {
1282     case IMAP_SERVERGREET:
1283       result = imap_state_servergreet_resp(conn, imapcode, imapc->state);
1284       break;
1285 
1286     case IMAP_CAPABILITY:
1287       result = imap_state_capability_resp(conn, imapcode, imapc->state);
1288       break;
1289 
1290     case IMAP_STARTTLS:
1291       result = imap_state_starttls_resp(conn, imapcode, imapc->state);
1292       break;
1293 
1294     case IMAP_AUTHENTICATE:
1295       result = imap_state_auth_resp(conn, imapcode, imapc->state);
1296       break;
1297 
1298     case IMAP_LOGIN:
1299       result = imap_state_login_resp(conn, imapcode, imapc->state);
1300       break;
1301 
1302     case IMAP_LIST:
1303       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1304       break;
1305 
1306     case IMAP_SELECT:
1307       result = imap_state_select_resp(conn, imapcode, imapc->state);
1308       break;
1309 
1310     case IMAP_FETCH:
1311       result = imap_state_fetch_resp(conn, imapcode, imapc->state);
1312       break;
1313 
1314     case IMAP_FETCH_FINAL:
1315       result = imap_state_fetch_final_resp(conn, imapcode, imapc->state);
1316       break;
1317 
1318     case IMAP_APPEND:
1319       result = imap_state_append_resp(conn, imapcode, imapc->state);
1320       break;
1321 
1322     case IMAP_APPEND_FINAL:
1323       result = imap_state_append_final_resp(conn, imapcode, imapc->state);
1324       break;
1325 
1326     case IMAP_SEARCH:
1327       result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
1328       break;
1329 
1330     case IMAP_LOGOUT:
1331       /* fallthrough, just stop! */
1332     default:
1333       /* internal error */
1334       state(conn, IMAP_STOP);
1335       break;
1336     }
1337   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1338 
1339   return result;
1340 }
1341 
1342 /* Called repeatedly until done from multi.c */
imap_multi_statemach(struct connectdata * conn,bool * done)1343 static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done)
1344 {
1345   CURLcode result = CURLE_OK;
1346   struct imap_conn *imapc = &conn->proto.imapc;
1347 
1348   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1349     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &imapc->ssldone);
1350     if(result || !imapc->ssldone)
1351       return result;
1352   }
1353 
1354   result = Curl_pp_statemach(&imapc->pp, FALSE);
1355   *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE;
1356 
1357   return result;
1358 }
1359 
imap_block_statemach(struct connectdata * conn)1360 static CURLcode imap_block_statemach(struct connectdata *conn)
1361 {
1362   CURLcode result = CURLE_OK;
1363   struct imap_conn *imapc = &conn->proto.imapc;
1364 
1365   while(imapc->state != IMAP_STOP && !result)
1366     result = Curl_pp_statemach(&imapc->pp, TRUE);
1367 
1368   return result;
1369 }
1370 
1371 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1372    required */
imap_init(struct connectdata * conn)1373 static CURLcode imap_init(struct connectdata *conn)
1374 {
1375   CURLcode result = CURLE_OK;
1376   struct Curl_easy *data = conn->data;
1377   struct IMAP *imap;
1378 
1379   imap = data->req.protop = calloc(sizeof(struct IMAP), 1);
1380   if(!imap)
1381     result = CURLE_OUT_OF_MEMORY;
1382 
1383   return result;
1384 }
1385 
1386 /* For the IMAP "protocol connect" and "doing" phases only */
imap_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)1387 static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
1388                         int numsocks)
1389 {
1390   return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
1391 }
1392 
1393 /***********************************************************************
1394  *
1395  * imap_connect()
1396  *
1397  * This function should do everything that is to be considered a part of the
1398  * connection phase.
1399  *
1400  * The variable 'done' points to will be TRUE if the protocol-layer connect
1401  * phase is done when this function returns, or FALSE if not.
1402  */
imap_connect(struct connectdata * conn,bool * done)1403 static CURLcode imap_connect(struct connectdata *conn, bool *done)
1404 {
1405   CURLcode result = CURLE_OK;
1406   struct imap_conn *imapc = &conn->proto.imapc;
1407   struct pingpong *pp = &imapc->pp;
1408 
1409   *done = FALSE; /* default to not done yet */
1410 
1411   /* We always support persistent connections in IMAP */
1412   connkeep(conn, "IMAP default");
1413 
1414   /* Set the default response time-out */
1415   pp->response_time = RESP_TIMEOUT;
1416   pp->statemach_act = imap_statemach_act;
1417   pp->endofresp = imap_endofresp;
1418   pp->conn = conn;
1419 
1420   /* Set the default preferred authentication type and mechanism */
1421   imapc->preftype = IMAP_TYPE_ANY;
1422   Curl_sasl_init(&imapc->sasl, &saslimap);
1423 
1424   /* Initialise the pingpong layer */
1425   Curl_pp_init(pp);
1426 
1427   /* Parse the URL options */
1428   result = imap_parse_url_options(conn);
1429   if(result)
1430     return result;
1431 
1432   /* Start off waiting for the server greeting response */
1433   state(conn, IMAP_SERVERGREET);
1434 
1435   /* Start off with an response id of '*' */
1436   strcpy(imapc->resptag, "*");
1437 
1438   result = imap_multi_statemach(conn, done);
1439 
1440   return result;
1441 }
1442 
1443 /***********************************************************************
1444  *
1445  * imap_done()
1446  *
1447  * The DONE function. This does what needs to be done after a single DO has
1448  * performed.
1449  *
1450  * Input argument is already checked for validity.
1451  */
imap_done(struct connectdata * conn,CURLcode status,bool premature)1452 static CURLcode imap_done(struct connectdata *conn, CURLcode status,
1453                           bool premature)
1454 {
1455   CURLcode result = CURLE_OK;
1456   struct Curl_easy *data = conn->data;
1457   struct IMAP *imap = data->req.protop;
1458 
1459   (void)premature;
1460 
1461   if(!imap)
1462     return CURLE_OK;
1463 
1464   if(status) {
1465     connclose(conn, "IMAP done with bad status"); /* marked for closure */
1466     result = status;         /* use the already set error code */
1467   }
1468   else if(!data->set.connect_only && !imap->custom &&
1469           (imap->uid || data->set.upload ||
1470           data->set.mimepost.kind != MIMEKIND_NONE)) {
1471     /* Handle responses after FETCH or APPEND transfer has finished */
1472     if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
1473       state(conn, IMAP_FETCH_FINAL);
1474     else {
1475       /* End the APPEND command first by sending an empty line */
1476       result = Curl_pp_sendf(&conn->proto.imapc.pp, "%s", "");
1477       if(!result)
1478         state(conn, IMAP_APPEND_FINAL);
1479     }
1480 
1481     /* Run the state-machine
1482 
1483        TODO: when the multi interface is used, this _really_ should be using
1484        the imap_multi_statemach function but we have no general support for
1485        non-blocking DONE operations!
1486     */
1487     if(!result)
1488       result = imap_block_statemach(conn);
1489   }
1490 
1491   /* Cleanup our per-request based variables */
1492   Curl_safefree(imap->mailbox);
1493   Curl_safefree(imap->uidvalidity);
1494   Curl_safefree(imap->uid);
1495   Curl_safefree(imap->section);
1496   Curl_safefree(imap->partial);
1497   Curl_safefree(imap->query);
1498   Curl_safefree(imap->custom);
1499   Curl_safefree(imap->custom_params);
1500 
1501   /* Clear the transfer mode for the next request */
1502   imap->transfer = FTPTRANSFER_BODY;
1503 
1504   return result;
1505 }
1506 
1507 /***********************************************************************
1508  *
1509  * imap_perform()
1510  *
1511  * This is the actual DO function for IMAP. Fetch or append a message, or do
1512  * other things according to the options previously setup.
1513  */
imap_perform(struct connectdata * conn,bool * connected,bool * dophase_done)1514 static CURLcode imap_perform(struct connectdata *conn, bool *connected,
1515                              bool *dophase_done)
1516 {
1517   /* This is IMAP and no proxy */
1518   CURLcode result = CURLE_OK;
1519   struct Curl_easy *data = conn->data;
1520   struct IMAP *imap = data->req.protop;
1521   struct imap_conn *imapc = &conn->proto.imapc;
1522   bool selected = FALSE;
1523 
1524   DEBUGF(infof(conn->data, "DO phase starts\n"));
1525 
1526   if(conn->data->set.opt_no_body) {
1527     /* Requested no body means no transfer */
1528     imap->transfer = FTPTRANSFER_INFO;
1529   }
1530 
1531   *dophase_done = FALSE; /* not done yet */
1532 
1533   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1534      has already been selected on this connection */
1535   if(imap->mailbox && imapc->mailbox &&
1536      strcasecompare(imap->mailbox, imapc->mailbox) &&
1537      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1538       strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1539     selected = TRUE;
1540 
1541   /* Start the first command in the DO phase */
1542   if(conn->data->set.upload || data->set.mimepost.kind != MIMEKIND_NONE)
1543     /* APPEND can be executed directly */
1544     result = imap_perform_append(conn);
1545   else if(imap->custom && (selected || !imap->mailbox))
1546     /* Custom command using the same mailbox or no mailbox */
1547     result = imap_perform_list(conn);
1548   else if(!imap->custom && selected && imap->uid)
1549     /* FETCH from the same mailbox */
1550     result = imap_perform_fetch(conn);
1551   else if(!imap->custom && selected && imap->query)
1552     /* SEARCH the current mailbox */
1553     result = imap_perform_search(conn);
1554   else if(imap->mailbox && !selected &&
1555          (imap->custom || imap->uid || imap->query))
1556     /* SELECT the mailbox */
1557     result = imap_perform_select(conn);
1558   else
1559     /* LIST */
1560     result = imap_perform_list(conn);
1561 
1562   if(result)
1563     return result;
1564 
1565   /* Run the state-machine */
1566   result = imap_multi_statemach(conn, dophase_done);
1567 
1568   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1569 
1570   if(*dophase_done)
1571     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1572 
1573   return result;
1574 }
1575 
1576 /***********************************************************************
1577  *
1578  * imap_do()
1579  *
1580  * This function is registered as 'curl_do' function. It decodes the path
1581  * parts etc as a wrapper to the actual DO function (imap_perform).
1582  *
1583  * The input argument is already checked for validity.
1584  */
imap_do(struct connectdata * conn,bool * done)1585 static CURLcode imap_do(struct connectdata *conn, bool *done)
1586 {
1587   CURLcode result = CURLE_OK;
1588 
1589   *done = FALSE; /* default to false */
1590 
1591   /* Parse the URL path */
1592   result = imap_parse_url_path(conn);
1593   if(result)
1594     return result;
1595 
1596   /* Parse the custom request */
1597   result = imap_parse_custom_request(conn);
1598   if(result)
1599     return result;
1600 
1601   result = imap_regular_transfer(conn, done);
1602 
1603   return result;
1604 }
1605 
1606 /***********************************************************************
1607  *
1608  * imap_disconnect()
1609  *
1610  * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1611  * resources. BLOCKING.
1612  */
imap_disconnect(struct connectdata * conn,bool dead_connection)1613 static CURLcode imap_disconnect(struct connectdata *conn, bool dead_connection)
1614 {
1615   struct imap_conn *imapc = &conn->proto.imapc;
1616 
1617   /* We cannot send quit unconditionally. If this connection is stale or
1618      bad in any way, sending quit and waiting around here will make the
1619      disconnect wait in vain and cause more problems than we need to. */
1620 
1621   /* The IMAP session may or may not have been allocated/setup at this
1622      point! */
1623   if(!dead_connection && imapc->pp.conn && imapc->pp.conn->bits.protoconnstart)
1624     if(!imap_perform_logout(conn))
1625       (void)imap_block_statemach(conn); /* ignore errors on LOGOUT */
1626 
1627   /* Disconnect from the server */
1628   Curl_pp_disconnect(&imapc->pp);
1629 
1630   /* Cleanup the SASL module */
1631   Curl_sasl_cleanup(conn, imapc->sasl.authused);
1632 
1633   /* Cleanup our connection based variables */
1634   Curl_safefree(imapc->mailbox);
1635   Curl_safefree(imapc->mailbox_uidvalidity);
1636 
1637   return CURLE_OK;
1638 }
1639 
1640 /* Call this when the DO phase has completed */
imap_dophase_done(struct connectdata * conn,bool connected)1641 static CURLcode imap_dophase_done(struct connectdata *conn, bool connected)
1642 {
1643   struct IMAP *imap = conn->data->req.protop;
1644 
1645   (void)connected;
1646 
1647   if(imap->transfer != FTPTRANSFER_BODY)
1648     /* no data to transfer */
1649     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1650 
1651   return CURLE_OK;
1652 }
1653 
1654 /* Called from multi.c while DOing */
imap_doing(struct connectdata * conn,bool * dophase_done)1655 static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done)
1656 {
1657   CURLcode result = imap_multi_statemach(conn, dophase_done);
1658 
1659   if(result)
1660     DEBUGF(infof(conn->data, "DO phase failed\n"));
1661   else if(*dophase_done) {
1662     result = imap_dophase_done(conn, FALSE /* not connected */);
1663 
1664     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1665   }
1666 
1667   return result;
1668 }
1669 
1670 /***********************************************************************
1671  *
1672  * imap_regular_transfer()
1673  *
1674  * The input argument is already checked for validity.
1675  *
1676  * Performs all commands done before a regular transfer between a local and a
1677  * remote host.
1678  */
imap_regular_transfer(struct connectdata * conn,bool * dophase_done)1679 static CURLcode imap_regular_transfer(struct connectdata *conn,
1680                                       bool *dophase_done)
1681 {
1682   CURLcode result = CURLE_OK;
1683   bool connected = FALSE;
1684   struct Curl_easy *data = conn->data;
1685 
1686   /* Make sure size is unknown at this point */
1687   data->req.size = -1;
1688 
1689   /* Set the progress data */
1690   Curl_pgrsSetUploadCounter(data, 0);
1691   Curl_pgrsSetDownloadCounter(data, 0);
1692   Curl_pgrsSetUploadSize(data, -1);
1693   Curl_pgrsSetDownloadSize(data, -1);
1694 
1695   /* Carry out the perform */
1696   result = imap_perform(conn, &connected, dophase_done);
1697 
1698   /* Perform post DO phase operations if necessary */
1699   if(!result && *dophase_done)
1700     result = imap_dophase_done(conn, connected);
1701 
1702   return result;
1703 }
1704 
imap_setup_connection(struct connectdata * conn)1705 static CURLcode imap_setup_connection(struct connectdata *conn)
1706 {
1707   struct Curl_easy *data = conn->data;
1708 
1709   /* Initialise the IMAP layer */
1710   CURLcode result = imap_init(conn);
1711   if(result)
1712     return result;
1713 
1714   /* Clear the TLS upgraded flag */
1715   conn->tls_upgraded = FALSE;
1716   data->state.path++;   /* don't include the initial slash */
1717 
1718   return CURLE_OK;
1719 }
1720 
1721 /***********************************************************************
1722  *
1723  * imap_sendf()
1724  *
1725  * Sends the formatted string as an IMAP command to the server.
1726  *
1727  * Designed to never block.
1728  */
imap_sendf(struct connectdata * conn,const char * fmt,...)1729 static CURLcode imap_sendf(struct connectdata *conn, const char *fmt, ...)
1730 {
1731   CURLcode result = CURLE_OK;
1732   struct imap_conn *imapc = &conn->proto.imapc;
1733   char *taggedfmt;
1734   va_list ap;
1735 
1736   DEBUGASSERT(fmt);
1737 
1738   /* Calculate the next command ID wrapping at 3 digits */
1739   imapc->cmdid = (imapc->cmdid + 1) % 1000;
1740 
1741   /* Calculate the tag based on the connection ID and command ID */
1742   snprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1743            'A' + curlx_sltosi(conn->connection_id % 26), imapc->cmdid);
1744 
1745   /* Prefix the format with the tag */
1746   taggedfmt = aprintf("%s %s", imapc->resptag, fmt);
1747   if(!taggedfmt)
1748     return CURLE_OUT_OF_MEMORY;
1749 
1750   /* Send the data with the tag */
1751   va_start(ap, fmt);
1752   result = Curl_pp_vsendf(&imapc->pp, taggedfmt, ap);
1753   va_end(ap);
1754 
1755   free(taggedfmt);
1756 
1757   return result;
1758 }
1759 
1760 /***********************************************************************
1761  *
1762  * imap_atom()
1763  *
1764  * Checks the input string for characters that need escaping and returns an
1765  * atom ready for sending to the server.
1766  *
1767  * The returned string needs to be freed.
1768  *
1769  */
imap_atom(const char * str,bool escape_only)1770 static char *imap_atom(const char *str, bool escape_only)
1771 {
1772   /* !checksrc! disable PARENBRACE 1 */
1773   const char atom_specials[] = "(){ %*]";
1774   const char *p1;
1775   char *p2;
1776   size_t backsp_count = 0;
1777   size_t quote_count = 0;
1778   bool others_exists = FALSE;
1779   size_t newlen = 0;
1780   char *newstr = NULL;
1781 
1782   if(!str)
1783     return NULL;
1784 
1785   /* Look for "atom-specials", counting the backslash and quote characters as
1786      these will need escapping */
1787   p1 = str;
1788   while(*p1) {
1789     if(*p1 == '\\')
1790       backsp_count++;
1791     else if(*p1 == '"')
1792       quote_count++;
1793     else if(!escape_only) {
1794       const char *p3 = atom_specials;
1795 
1796       while(*p3 && !others_exists) {
1797         if(*p1 == *p3)
1798           others_exists = TRUE;
1799 
1800         p3++;
1801       }
1802     }
1803 
1804     p1++;
1805   }
1806 
1807   /* Does the input contain any "atom-special" characters? */
1808   if(!backsp_count && !quote_count && !others_exists)
1809     return strdup(str);
1810 
1811   /* Calculate the new string length */
1812   newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
1813 
1814   /* Allocate the new string */
1815   newstr = (char *) malloc((newlen + 1) * sizeof(char));
1816   if(!newstr)
1817     return NULL;
1818 
1819   /* Surround the string in quotes if necessary */
1820   p2 = newstr;
1821   if(!escape_only) {
1822     newstr[0] = '"';
1823     newstr[newlen - 1] = '"';
1824     p2++;
1825   }
1826 
1827   /* Copy the string, escaping backslash and quote characters along the way */
1828   p1 = str;
1829   while(*p1) {
1830     if(*p1 == '\\' || *p1 == '"') {
1831       *p2 = '\\';
1832       p2++;
1833     }
1834 
1835    *p2 = *p1;
1836 
1837     p1++;
1838     p2++;
1839   }
1840 
1841   /* Terminate the string */
1842   newstr[newlen] = '\0';
1843 
1844   return newstr;
1845 }
1846 
1847 /***********************************************************************
1848  *
1849  * imap_is_bchar()
1850  *
1851  * Portable test of whether the specified char is a "bchar" as defined in the
1852  * grammar of RFC-5092.
1853  */
imap_is_bchar(char ch)1854 static bool imap_is_bchar(char ch)
1855 {
1856   switch(ch) {
1857     /* bchar */
1858     case ':': case '@': case '/':
1859     /* bchar -> achar */
1860     case '&': case '=':
1861     /* bchar -> achar -> uchar -> unreserved */
1862     case '0': case '1': case '2': case '3': case '4': case '5': case '6':
1863     case '7': case '8': case '9':
1864     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
1865     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
1866     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
1867     case 'V': case 'W': case 'X': case 'Y': case 'Z':
1868     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
1869     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
1870     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
1871     case 'v': case 'w': case 'x': case 'y': case 'z':
1872     case '-': case '.': case '_': case '~':
1873     /* bchar -> achar -> uchar -> sub-delims-sh */
1874     case '!': case '$': case '\'': case '(': case ')': case '*':
1875     case '+': case ',':
1876     /* bchar -> achar -> uchar -> pct-encoded */
1877     case '%': /* HEXDIG chars are already included above */
1878       return true;
1879 
1880     default:
1881       return false;
1882   }
1883 }
1884 
1885 /***********************************************************************
1886  *
1887  * imap_parse_url_options()
1888  *
1889  * Parse the URL login options.
1890  */
imap_parse_url_options(struct connectdata * conn)1891 static CURLcode imap_parse_url_options(struct connectdata *conn)
1892 {
1893   CURLcode result = CURLE_OK;
1894   struct imap_conn *imapc = &conn->proto.imapc;
1895   const char *ptr = conn->options;
1896 
1897   imapc->sasl.resetprefs = TRUE;
1898 
1899   while(!result && ptr && *ptr) {
1900     const char *key = ptr;
1901     const char *value;
1902 
1903     while(*ptr && *ptr != '=')
1904         ptr++;
1905 
1906     value = ptr + 1;
1907 
1908     while(*ptr && *ptr != ';')
1909       ptr++;
1910 
1911     if(strncasecompare(key, "AUTH=", 5))
1912       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1913                                                value, ptr - value);
1914     else
1915       result = CURLE_URL_MALFORMAT;
1916 
1917     if(*ptr == ';')
1918       ptr++;
1919   }
1920 
1921   switch(imapc->sasl.prefmech) {
1922   case SASL_AUTH_NONE:
1923     imapc->preftype = IMAP_TYPE_NONE;
1924     break;
1925   case SASL_AUTH_DEFAULT:
1926     imapc->preftype = IMAP_TYPE_ANY;
1927     break;
1928   default:
1929     imapc->preftype = IMAP_TYPE_SASL;
1930     break;
1931   }
1932 
1933   return result;
1934 }
1935 
1936 /***********************************************************************
1937  *
1938  * imap_parse_url_path()
1939  *
1940  * Parse the URL path into separate path components.
1941  *
1942  */
imap_parse_url_path(struct connectdata * conn)1943 static CURLcode imap_parse_url_path(struct connectdata *conn)
1944 {
1945   /* The imap struct is already initialised in imap_connect() */
1946   CURLcode result = CURLE_OK;
1947   struct Curl_easy *data = conn->data;
1948   struct IMAP *imap = data->req.protop;
1949   const char *begin = data->state.path;
1950   const char *ptr = begin;
1951 
1952   /* See how much of the URL is a valid path and decode it */
1953   while(imap_is_bchar(*ptr))
1954     ptr++;
1955 
1956   if(ptr != begin) {
1957     /* Remove the trailing slash if present */
1958     const char *end = ptr;
1959     if(end > begin && end[-1] == '/')
1960       end--;
1961 
1962     result = Curl_urldecode(data, begin, end - begin, &imap->mailbox, NULL,
1963                             TRUE);
1964     if(result)
1965       return result;
1966   }
1967   else
1968     imap->mailbox = NULL;
1969 
1970   /* There can be any number of parameters in the form ";NAME=VALUE" */
1971   while(*ptr == ';') {
1972     char *name;
1973     char *value;
1974     size_t valuelen;
1975 
1976     /* Find the length of the name parameter */
1977     begin = ++ptr;
1978     while(*ptr && *ptr != '=')
1979       ptr++;
1980 
1981     if(!*ptr)
1982       return CURLE_URL_MALFORMAT;
1983 
1984     /* Decode the name parameter */
1985     result = Curl_urldecode(data, begin, ptr - begin, &name, NULL, TRUE);
1986     if(result)
1987       return result;
1988 
1989     /* Find the length of the value parameter */
1990     begin = ++ptr;
1991     while(imap_is_bchar(*ptr))
1992       ptr++;
1993 
1994     /* Decode the value parameter */
1995     result = Curl_urldecode(data, begin, ptr - begin, &value, &valuelen, TRUE);
1996     if(result) {
1997       free(name);
1998       return result;
1999     }
2000 
2001     DEBUGF(infof(conn->data, "IMAP URL parameter '%s' = '%s'\n", name, value));
2002 
2003     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
2004        PARTIAL) stripping of the trailing slash character if it is present.
2005 
2006        Note: Unknown parameters trigger a URL_MALFORMAT error. */
2007     if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
2008       if(valuelen > 0 && value[valuelen - 1] == '/')
2009         value[valuelen - 1] = '\0';
2010 
2011       imap->uidvalidity = value;
2012       value = NULL;
2013     }
2014     else if(strcasecompare(name, "UID") && !imap->uid) {
2015       if(valuelen > 0 && value[valuelen - 1] == '/')
2016         value[valuelen - 1] = '\0';
2017 
2018       imap->uid = value;
2019       value = NULL;
2020     }
2021     else if(strcasecompare(name, "SECTION") && !imap->section) {
2022       if(valuelen > 0 && value[valuelen - 1] == '/')
2023         value[valuelen - 1] = '\0';
2024 
2025       imap->section = value;
2026       value = NULL;
2027     }
2028     else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
2029       if(valuelen > 0 && value[valuelen - 1] == '/')
2030         value[valuelen - 1] = '\0';
2031 
2032       imap->partial = value;
2033       value = NULL;
2034     }
2035     else {
2036       free(name);
2037       free(value);
2038 
2039       return CURLE_URL_MALFORMAT;
2040     }
2041 
2042     free(name);
2043     free(value);
2044   }
2045 
2046   /* Does the URL contain a query parameter? Only valid when we have a mailbox
2047      and no UID as per RFC-5092 */
2048   if(imap->mailbox && !imap->uid && *ptr == '?') {
2049     /* Find the length of the query parameter */
2050     begin = ++ptr;
2051     while(imap_is_bchar(*ptr))
2052       ptr++;
2053 
2054     /* Decode the query parameter */
2055     result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
2056                             TRUE);
2057     if(result)
2058       return result;
2059   }
2060 
2061   /* Any extra stuff at the end of the URL is an error */
2062   if(*ptr)
2063     return CURLE_URL_MALFORMAT;
2064 
2065   return CURLE_OK;
2066 }
2067 
2068 /***********************************************************************
2069  *
2070  * imap_parse_custom_request()
2071  *
2072  * Parse the custom request.
2073  */
imap_parse_custom_request(struct connectdata * conn)2074 static CURLcode imap_parse_custom_request(struct connectdata *conn)
2075 {
2076   CURLcode result = CURLE_OK;
2077   struct Curl_easy *data = conn->data;
2078   struct IMAP *imap = data->req.protop;
2079   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2080 
2081   if(custom) {
2082     /* URL decode the custom request */
2083     result = Curl_urldecode(data, custom, 0, &imap->custom, NULL, TRUE);
2084 
2085     /* Extract the parameters if specified */
2086     if(!result) {
2087       const char *params = imap->custom;
2088 
2089       while(*params && *params != ' ')
2090         params++;
2091 
2092       if(*params) {
2093         imap->custom_params = strdup(params);
2094         imap->custom[params - imap->custom] = '\0';
2095 
2096         if(!imap->custom_params)
2097           result = CURLE_OUT_OF_MEMORY;
2098       }
2099     }
2100   }
2101 
2102   return result;
2103 }
2104 
2105 #endif /* CURL_DISABLE_IMAP */
2106