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