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