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