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