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