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