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