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