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 do not */
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 do not */
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 &= ~(unsigned int)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, "APPEND %s (\\Seen) {%" FMT_OFF_T "}",
818 mailbox, data->state.infilesize);
819
820 free(mailbox);
821
822 if(!result)
823 imap_state(data, IMAP_APPEND);
824
825 return result;
826 }
827
828 /***********************************************************************
829 *
830 * imap_perform_search()
831 *
832 * Sends a SEARCH command.
833 */
imap_perform_search(struct Curl_easy * data)834 static CURLcode imap_perform_search(struct Curl_easy *data)
835 {
836 CURLcode result = CURLE_OK;
837 struct IMAP *imap = data->req.p.imap;
838
839 /* Check we have a query string */
840 if(!imap->query) {
841 failf(data, "Cannot SEARCH without a query string.");
842 return CURLE_URL_MALFORMAT;
843 }
844
845 /* Send the SEARCH command */
846 result = imap_sendf(data, "SEARCH %s", imap->query);
847
848 if(!result)
849 imap_state(data, IMAP_SEARCH);
850
851 return result;
852 }
853
854 /***********************************************************************
855 *
856 * imap_perform_logout()
857 *
858 * Performs the logout action prior to sclose() being called.
859 */
imap_perform_logout(struct Curl_easy * data)860 static CURLcode imap_perform_logout(struct Curl_easy *data)
861 {
862 /* Send the LOGOUT command */
863 CURLcode result = imap_sendf(data, "LOGOUT");
864
865 if(!result)
866 imap_state(data, IMAP_LOGOUT);
867
868 return result;
869 }
870
871 /* For the initial server greeting */
imap_state_servergreet_resp(struct Curl_easy * data,int imapcode,imapstate instate)872 static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
873 int imapcode,
874 imapstate instate)
875 {
876 struct connectdata *conn = data->conn;
877 (void)instate; /* no use for this yet */
878
879 if(imapcode == IMAP_RESP_PREAUTH) {
880 /* PREAUTH */
881 struct imap_conn *imapc = &conn->proto.imapc;
882 imapc->preauth = TRUE;
883 infof(data, "PREAUTH connection, already authenticated");
884 }
885 else if(imapcode != IMAP_RESP_OK) {
886 failf(data, "Got unexpected imap-server response");
887 return CURLE_WEIRD_SERVER_REPLY;
888 }
889
890 return imap_perform_capability(data, conn);
891 }
892
893 /* For CAPABILITY responses */
imap_state_capability_resp(struct Curl_easy * data,int imapcode,imapstate instate)894 static CURLcode imap_state_capability_resp(struct Curl_easy *data,
895 int imapcode,
896 imapstate instate)
897 {
898 CURLcode result = CURLE_OK;
899 struct connectdata *conn = data->conn;
900 struct imap_conn *imapc = &conn->proto.imapc;
901 const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf);
902
903 (void)instate; /* no use for this yet */
904
905 /* Do we have a untagged response? */
906 if(imapcode == '*') {
907 line += 2;
908
909 /* Loop through the data line */
910 for(;;) {
911 size_t wordlen;
912 while(*line &&
913 (*line == ' ' || *line == '\t' ||
914 *line == '\r' || *line == '\n')) {
915
916 line++;
917 }
918
919 if(!*line)
920 break;
921
922 /* Extract the word */
923 for(wordlen = 0; line[wordlen] && line[wordlen] != ' ' &&
924 line[wordlen] != '\t' && line[wordlen] != '\r' &&
925 line[wordlen] != '\n';)
926 wordlen++;
927
928 /* Does the server support the STARTTLS capability? */
929 if(wordlen == 8 && !memcmp(line, "STARTTLS", 8))
930 imapc->tls_supported = TRUE;
931
932 /* Has the server explicitly disabled clear text authentication? */
933 else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13))
934 imapc->login_disabled = TRUE;
935
936 /* Does the server support the SASL-IR capability? */
937 else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7))
938 imapc->ir_supported = TRUE;
939
940 /* Do we have a SASL based authentication mechanism? */
941 else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) {
942 size_t llen;
943 unsigned short mechbit;
944
945 line += 5;
946 wordlen -= 5;
947
948 /* Test the word for a matching authentication mechanism */
949 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
950 if(mechbit && llen == wordlen)
951 imapc->sasl.authmechs |= mechbit;
952 }
953
954 line += wordlen;
955 }
956 }
957 else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
958 /* PREAUTH is not compatible with STARTTLS. */
959 if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
960 /* Switch to TLS connection now */
961 result = imap_perform_starttls(data);
962 }
963 else if(data->set.use_ssl <= CURLUSESSL_TRY)
964 result = imap_perform_authentication(data, conn);
965 else {
966 failf(data, "STARTTLS not available.");
967 result = CURLE_USE_SSL_FAILED;
968 }
969 }
970 else
971 result = imap_perform_authentication(data, conn);
972
973 return result;
974 }
975
976 /* For STARTTLS responses */
imap_state_starttls_resp(struct Curl_easy * data,int imapcode,imapstate instate)977 static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
978 int imapcode,
979 imapstate instate)
980 {
981 CURLcode result = CURLE_OK;
982 struct connectdata *conn = data->conn;
983
984 (void)instate; /* no use for this yet */
985
986 /* Pipelining in response is forbidden. */
987 if(data->conn->proto.imapc.pp.overflow)
988 return CURLE_WEIRD_SERVER_REPLY;
989
990 if(imapcode != IMAP_RESP_OK) {
991 if(data->set.use_ssl != CURLUSESSL_TRY) {
992 failf(data, "STARTTLS denied");
993 result = CURLE_USE_SSL_FAILED;
994 }
995 else
996 result = imap_perform_authentication(data, conn);
997 }
998 else
999 result = imap_perform_upgrade_tls(data, conn);
1000
1001 return result;
1002 }
1003
1004 /* For SASL authentication responses */
imap_state_auth_resp(struct Curl_easy * data,struct connectdata * conn,int imapcode,imapstate instate)1005 static CURLcode imap_state_auth_resp(struct Curl_easy *data,
1006 struct connectdata *conn,
1007 int imapcode,
1008 imapstate instate)
1009 {
1010 CURLcode result = CURLE_OK;
1011 struct imap_conn *imapc = &conn->proto.imapc;
1012 saslprogress progress;
1013
1014 (void)instate; /* no use for this yet */
1015
1016 result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress);
1017 if(!result)
1018 switch(progress) {
1019 case SASL_DONE:
1020 imap_state(data, IMAP_STOP); /* Authenticated */
1021 break;
1022 case SASL_IDLE: /* No mechanism left after cancellation */
1023 if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
1024 /* Perform clear text authentication */
1025 result = imap_perform_login(data, conn);
1026 else {
1027 failf(data, "Authentication cancelled");
1028 result = CURLE_LOGIN_DENIED;
1029 }
1030 break;
1031 default:
1032 break;
1033 }
1034
1035 return result;
1036 }
1037
1038 /* For LOGIN responses */
imap_state_login_resp(struct Curl_easy * data,int imapcode,imapstate instate)1039 static CURLcode imap_state_login_resp(struct Curl_easy *data,
1040 int imapcode,
1041 imapstate instate)
1042 {
1043 CURLcode result = CURLE_OK;
1044 (void)instate; /* no use for this yet */
1045
1046 if(imapcode != IMAP_RESP_OK) {
1047 failf(data, "Access denied. %c", imapcode);
1048 result = CURLE_LOGIN_DENIED;
1049 }
1050 else
1051 /* End of connect phase */
1052 imap_state(data, IMAP_STOP);
1053
1054 return result;
1055 }
1056
1057 /* For LIST and SEARCH responses */
imap_state_listsearch_resp(struct Curl_easy * data,int imapcode,imapstate instate)1058 static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
1059 int imapcode,
1060 imapstate instate)
1061 {
1062 CURLcode result = CURLE_OK;
1063 char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
1064 size_t len = data->conn->proto.imapc.pp.nfinal;
1065
1066 (void)instate; /* No use for this yet */
1067
1068 if(imapcode == '*')
1069 result = Curl_client_write(data, CLIENTWRITE_BODY, line, len);
1070 else if(imapcode != IMAP_RESP_OK)
1071 result = CURLE_QUOTE_ERROR;
1072 else
1073 /* End of DO phase */
1074 imap_state(data, IMAP_STOP);
1075
1076 return result;
1077 }
1078
1079 /* For SELECT responses */
imap_state_select_resp(struct Curl_easy * data,int imapcode,imapstate instate)1080 static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
1081 imapstate instate)
1082 {
1083 CURLcode result = CURLE_OK;
1084 struct connectdata *conn = data->conn;
1085 struct IMAP *imap = data->req.p.imap;
1086 struct imap_conn *imapc = &conn->proto.imapc;
1087 const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
1088
1089 (void)instate; /* no use for this yet */
1090
1091 if(imapcode == '*') {
1092 /* See if this is an UIDVALIDITY response */
1093 if(checkprefix("OK [UIDVALIDITY ", line + 2)) {
1094 size_t len = 0;
1095 const char *p = &line[2] + strlen("OK [UIDVALIDITY ");
1096 while((len < 20) && p[len] && ISDIGIT(p[len]))
1097 len++;
1098 if(len && (p[len] == ']')) {
1099 struct dynbuf uid;
1100 Curl_dyn_init(&uid, 20);
1101 if(Curl_dyn_addn(&uid, p, len))
1102 return CURLE_OUT_OF_MEMORY;
1103 Curl_safefree(imapc->mailbox_uidvalidity);
1104 imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid);
1105 }
1106 }
1107 }
1108 else if(imapcode == IMAP_RESP_OK) {
1109 /* Check if the UIDVALIDITY has been specified and matches */
1110 if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
1111 !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
1112 failf(data, "Mailbox UIDVALIDITY has changed");
1113 result = CURLE_REMOTE_FILE_NOT_FOUND;
1114 }
1115 else {
1116 /* Note the currently opened mailbox on this connection */
1117 DEBUGASSERT(!imapc->mailbox);
1118 imapc->mailbox = strdup(imap->mailbox);
1119 if(!imapc->mailbox)
1120 return CURLE_OUT_OF_MEMORY;
1121
1122 if(imap->custom)
1123 result = imap_perform_list(data);
1124 else if(imap->query)
1125 result = imap_perform_search(data);
1126 else
1127 result = imap_perform_fetch(data);
1128 }
1129 }
1130 else {
1131 failf(data, "Select failed");
1132 result = CURLE_LOGIN_DENIED;
1133 }
1134
1135 return result;
1136 }
1137
1138 /* For the (first line of the) FETCH responses */
imap_state_fetch_resp(struct Curl_easy * data,struct connectdata * conn,int imapcode,imapstate instate)1139 static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
1140 struct connectdata *conn, int imapcode,
1141 imapstate instate)
1142 {
1143 CURLcode result = CURLE_OK;
1144 struct imap_conn *imapc = &conn->proto.imapc;
1145 struct pingpong *pp = &imapc->pp;
1146 const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
1147 size_t len = data->conn->proto.imapc.pp.nfinal;
1148 bool parsed = FALSE;
1149 curl_off_t size = 0;
1150
1151 (void)instate; /* no use for this yet */
1152
1153 if(imapcode != '*') {
1154 Curl_pgrsSetDownloadSize(data, -1);
1155 imap_state(data, IMAP_STOP);
1156 return CURLE_REMOTE_FILE_NOT_FOUND;
1157 }
1158
1159 /* Something like this is received "* 1 FETCH (BODY[TEXT] {2021}\r" so parse
1160 the continuation data contained within the curly brackets */
1161 ptr = memchr(ptr, '{', len);
1162 if(ptr) {
1163 char *endptr;
1164 if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) &&
1165 (endptr - ptr > 1 && *endptr == '}'))
1166 parsed = TRUE;
1167 }
1168
1169 if(parsed) {
1170 infof(data, "Found %" FMT_OFF_T " bytes to download", size);
1171 Curl_pgrsSetDownloadSize(data, size);
1172
1173 if(pp->overflow) {
1174 /* At this point there is a data in the receive buffer that is body
1175 content, send it as body and then skip it. Do note that there may
1176 even be additional "headers" after the body. */
1177 size_t chunk = pp->overflow;
1178
1179 /* keep only the overflow */
1180 Curl_dyn_tail(&pp->recvbuf, chunk);
1181 pp->nfinal = 0; /* done */
1182
1183 if(chunk > (size_t)size)
1184 /* The conversion from curl_off_t to size_t is always fine here */
1185 chunk = (size_t)size;
1186
1187 if(!chunk) {
1188 /* no size, we are done with the data */
1189 imap_state(data, IMAP_STOP);
1190 return CURLE_OK;
1191 }
1192 result = Curl_client_write(data, CLIENTWRITE_BODY,
1193 Curl_dyn_ptr(&pp->recvbuf), chunk);
1194 if(result)
1195 return result;
1196
1197 infof(data, "Written %zu bytes, %" FMT_OFF_TU
1198 " bytes are left for transfer", chunk, size - chunk);
1199
1200 /* Have we used the entire overflow or just part of it?*/
1201 if(pp->overflow > chunk) {
1202 /* remember the remaining trailing overflow data */
1203 pp->overflow -= chunk;
1204 Curl_dyn_tail(&pp->recvbuf, pp->overflow);
1205 }
1206 else {
1207 pp->overflow = 0; /* handled */
1208 /* Free the cache */
1209 Curl_dyn_reset(&pp->recvbuf);
1210 }
1211 }
1212
1213 if(data->req.bytecount == size)
1214 /* The entire data is already transferred! */
1215 Curl_xfer_setup_nop(data);
1216 else {
1217 /* IMAP download */
1218 data->req.maxdownload = size;
1219 /* force a recv/send check of this connection, as the data might've been
1220 read off the socket already */
1221 data->state.select_bits = CURL_CSELECT_IN;
1222 Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE);
1223 }
1224 }
1225 else {
1226 /* We do not know how to parse this line */
1227 failf(data, "Failed to parse FETCH response.");
1228 result = CURLE_WEIRD_SERVER_REPLY;
1229 }
1230
1231 /* End of DO phase */
1232 imap_state(data, IMAP_STOP);
1233
1234 return result;
1235 }
1236
1237 /* For final FETCH responses performed after the download */
imap_state_fetch_final_resp(struct Curl_easy * data,int imapcode,imapstate instate)1238 static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
1239 int imapcode,
1240 imapstate instate)
1241 {
1242 CURLcode result = CURLE_OK;
1243
1244 (void)instate; /* No use for this yet */
1245
1246 if(imapcode != IMAP_RESP_OK)
1247 result = CURLE_WEIRD_SERVER_REPLY;
1248 else
1249 /* End of DONE phase */
1250 imap_state(data, IMAP_STOP);
1251
1252 return result;
1253 }
1254
1255 /* For APPEND responses */
imap_state_append_resp(struct Curl_easy * data,int imapcode,imapstate instate)1256 static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
1257 imapstate instate)
1258 {
1259 CURLcode result = CURLE_OK;
1260 (void)instate; /* No use for this yet */
1261
1262 if(imapcode != '+') {
1263 result = CURLE_UPLOAD_FAILED;
1264 }
1265 else {
1266 /* Set the progress upload size */
1267 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1268
1269 /* IMAP upload */
1270 Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
1271
1272 /* End of DO phase */
1273 imap_state(data, IMAP_STOP);
1274 }
1275
1276 return result;
1277 }
1278
1279 /* For final APPEND responses performed after the upload */
imap_state_append_final_resp(struct Curl_easy * data,int imapcode,imapstate instate)1280 static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
1281 int imapcode,
1282 imapstate instate)
1283 {
1284 CURLcode result = CURLE_OK;
1285
1286 (void)instate; /* No use for this yet */
1287
1288 if(imapcode != IMAP_RESP_OK)
1289 result = CURLE_UPLOAD_FAILED;
1290 else
1291 /* End of DONE phase */
1292 imap_state(data, IMAP_STOP);
1293
1294 return result;
1295 }
1296
imap_statemachine(struct Curl_easy * data,struct connectdata * conn)1297 static CURLcode imap_statemachine(struct Curl_easy *data,
1298 struct connectdata *conn)
1299 {
1300 CURLcode result = CURLE_OK;
1301 int imapcode;
1302 struct imap_conn *imapc = &conn->proto.imapc;
1303 struct pingpong *pp = &imapc->pp;
1304 size_t nread = 0;
1305 (void)data;
1306
1307 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
1308 if(imapc->state == IMAP_UPGRADETLS)
1309 return imap_perform_upgrade_tls(data, conn);
1310
1311 /* Flush any data that needs to be sent */
1312 if(pp->sendleft)
1313 return Curl_pp_flushsend(data, pp);
1314
1315 do {
1316 /* Read the response from the server */
1317 result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &imapcode, &nread);
1318 if(result)
1319 return result;
1320
1321 /* Was there an error parsing the response line? */
1322 if(imapcode == -1)
1323 return CURLE_WEIRD_SERVER_REPLY;
1324
1325 if(!imapcode)
1326 break;
1327
1328 /* We have now received a full IMAP server response */
1329 switch(imapc->state) {
1330 case IMAP_SERVERGREET:
1331 result = imap_state_servergreet_resp(data, imapcode, imapc->state);
1332 break;
1333
1334 case IMAP_CAPABILITY:
1335 result = imap_state_capability_resp(data, imapcode, imapc->state);
1336 break;
1337
1338 case IMAP_STARTTLS:
1339 result = imap_state_starttls_resp(data, imapcode, imapc->state);
1340 break;
1341
1342 case IMAP_AUTHENTICATE:
1343 result = imap_state_auth_resp(data, conn, imapcode, imapc->state);
1344 break;
1345
1346 case IMAP_LOGIN:
1347 result = imap_state_login_resp(data, imapcode, imapc->state);
1348 break;
1349
1350 case IMAP_LIST:
1351 case IMAP_SEARCH:
1352 result = imap_state_listsearch_resp(data, imapcode, imapc->state);
1353 break;
1354
1355 case IMAP_SELECT:
1356 result = imap_state_select_resp(data, imapcode, imapc->state);
1357 break;
1358
1359 case IMAP_FETCH:
1360 result = imap_state_fetch_resp(data, conn, imapcode, imapc->state);
1361 break;
1362
1363 case IMAP_FETCH_FINAL:
1364 result = imap_state_fetch_final_resp(data, imapcode, imapc->state);
1365 break;
1366
1367 case IMAP_APPEND:
1368 result = imap_state_append_resp(data, imapcode, imapc->state);
1369 break;
1370
1371 case IMAP_APPEND_FINAL:
1372 result = imap_state_append_final_resp(data, imapcode, imapc->state);
1373 break;
1374
1375 case IMAP_LOGOUT:
1376 default:
1377 /* internal error */
1378 imap_state(data, IMAP_STOP);
1379 break;
1380 }
1381 } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
1382
1383 return result;
1384 }
1385
1386 /* Called repeatedly until done from multi.c */
imap_multi_statemach(struct Curl_easy * data,bool * done)1387 static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
1388 {
1389 CURLcode result = CURLE_OK;
1390 struct connectdata *conn = data->conn;
1391 struct imap_conn *imapc = &conn->proto.imapc;
1392
1393 if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
1394 bool ssldone = FALSE;
1395 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
1396 imapc->ssldone = ssldone;
1397 if(result || !ssldone)
1398 return result;
1399 }
1400
1401 result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
1402 *done = (imapc->state == IMAP_STOP);
1403
1404 return result;
1405 }
1406
imap_block_statemach(struct Curl_easy * data,struct connectdata * conn,bool disconnecting)1407 static CURLcode imap_block_statemach(struct Curl_easy *data,
1408 struct connectdata *conn,
1409 bool disconnecting)
1410 {
1411 CURLcode result = CURLE_OK;
1412 struct imap_conn *imapc = &conn->proto.imapc;
1413
1414 while(imapc->state != IMAP_STOP && !result)
1415 result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
1416
1417 return result;
1418 }
1419
1420 /* Allocate and initialize the struct IMAP for the current Curl_easy if
1421 required */
imap_init(struct Curl_easy * data)1422 static CURLcode imap_init(struct Curl_easy *data)
1423 {
1424 CURLcode result = CURLE_OK;
1425 struct IMAP *imap;
1426
1427 imap = data->req.p.imap = calloc(1, sizeof(struct IMAP));
1428 if(!imap)
1429 result = CURLE_OUT_OF_MEMORY;
1430
1431 return result;
1432 }
1433
1434 /* For the IMAP "protocol connect" and "doing" phases only */
imap_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)1435 static int imap_getsock(struct Curl_easy *data,
1436 struct connectdata *conn,
1437 curl_socket_t *socks)
1438 {
1439 return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks);
1440 }
1441
1442 /***********************************************************************
1443 *
1444 * imap_connect()
1445 *
1446 * This function should do everything that is to be considered a part of the
1447 * connection phase.
1448 *
1449 * The variable 'done' points to will be TRUE if the protocol-layer connect
1450 * phase is done when this function returns, or FALSE if not.
1451 */
imap_connect(struct Curl_easy * data,bool * done)1452 static CURLcode imap_connect(struct Curl_easy *data, bool *done)
1453 {
1454 CURLcode result = CURLE_OK;
1455 struct connectdata *conn = data->conn;
1456 struct imap_conn *imapc = &conn->proto.imapc;
1457 struct pingpong *pp = &imapc->pp;
1458
1459 *done = FALSE; /* default to not done yet */
1460
1461 /* We always support persistent connections in IMAP */
1462 connkeep(conn, "IMAP default");
1463
1464 PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
1465
1466 /* Set the default preferred authentication type and mechanism */
1467 imapc->preftype = IMAP_TYPE_ANY;
1468 Curl_sasl_init(&imapc->sasl, data, &saslimap);
1469
1470 Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
1471 Curl_pp_init(pp);
1472
1473 /* Parse the URL options */
1474 result = imap_parse_url_options(conn);
1475 if(result)
1476 return result;
1477
1478 /* Start off waiting for the server greeting response */
1479 imap_state(data, IMAP_SERVERGREET);
1480
1481 /* Start off with an response id of '*' */
1482 strcpy(imapc->resptag, "*");
1483
1484 result = imap_multi_statemach(data, done);
1485
1486 return result;
1487 }
1488
1489 /***********************************************************************
1490 *
1491 * imap_done()
1492 *
1493 * The DONE function. This does what needs to be done after a single DO has
1494 * performed.
1495 *
1496 * Input argument is already checked for validity.
1497 */
imap_done(struct Curl_easy * data,CURLcode status,bool premature)1498 static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
1499 bool premature)
1500 {
1501 CURLcode result = CURLE_OK;
1502 struct connectdata *conn = data->conn;
1503 struct IMAP *imap = data->req.p.imap;
1504
1505 (void)premature;
1506
1507 if(!imap)
1508 return CURLE_OK;
1509
1510 if(status) {
1511 connclose(conn, "IMAP done with bad status"); /* marked for closure */
1512 result = status; /* use the already set error code */
1513 }
1514 else if(!data->set.connect_only && !imap->custom &&
1515 (imap->uid || imap->mindex || data->state.upload ||
1516 IS_MIME_POST(data))) {
1517 /* Handle responses after FETCH or APPEND transfer has finished */
1518
1519 if(!data->state.upload && !IS_MIME_POST(data))
1520 imap_state(data, IMAP_FETCH_FINAL);
1521 else {
1522 /* End the APPEND command first by sending an empty line */
1523 result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
1524 if(!result)
1525 imap_state(data, IMAP_APPEND_FINAL);
1526 }
1527
1528 /* Run the state-machine */
1529 if(!result)
1530 result = imap_block_statemach(data, conn, FALSE);
1531 }
1532
1533 /* Cleanup our per-request based variables */
1534 Curl_safefree(imap->mailbox);
1535 Curl_safefree(imap->uidvalidity);
1536 Curl_safefree(imap->uid);
1537 Curl_safefree(imap->mindex);
1538 Curl_safefree(imap->section);
1539 Curl_safefree(imap->partial);
1540 Curl_safefree(imap->query);
1541 Curl_safefree(imap->custom);
1542 Curl_safefree(imap->custom_params);
1543
1544 /* Clear the transfer mode for the next request */
1545 imap->transfer = PPTRANSFER_BODY;
1546
1547 return result;
1548 }
1549
1550 /***********************************************************************
1551 *
1552 * imap_perform()
1553 *
1554 * This is the actual DO function for IMAP. Fetch or append a message, or do
1555 * other things according to the options previously setup.
1556 */
imap_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)1557 static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
1558 bool *dophase_done)
1559 {
1560 /* This is IMAP and no proxy */
1561 CURLcode result = CURLE_OK;
1562 struct connectdata *conn = data->conn;
1563 struct IMAP *imap = data->req.p.imap;
1564 struct imap_conn *imapc = &conn->proto.imapc;
1565 bool selected = FALSE;
1566
1567 DEBUGF(infof(data, "DO phase starts"));
1568
1569 if(data->req.no_body) {
1570 /* Requested no body means no transfer */
1571 imap->transfer = PPTRANSFER_INFO;
1572 }
1573
1574 *dophase_done = FALSE; /* not done yet */
1575
1576 /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
1577 has already been selected on this connection */
1578 if(imap->mailbox && imapc->mailbox &&
1579 strcasecompare(imap->mailbox, imapc->mailbox) &&
1580 (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
1581 strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
1582 selected = TRUE;
1583
1584 /* Start the first command in the DO phase */
1585 if(data->state.upload || IS_MIME_POST(data))
1586 /* APPEND can be executed directly */
1587 result = imap_perform_append(data);
1588 else if(imap->custom && (selected || !imap->mailbox))
1589 /* Custom command using the same mailbox or no mailbox */
1590 result = imap_perform_list(data);
1591 else if(!imap->custom && selected && (imap->uid || imap->mindex))
1592 /* FETCH from the same mailbox */
1593 result = imap_perform_fetch(data);
1594 else if(!imap->custom && selected && imap->query)
1595 /* SEARCH the current mailbox */
1596 result = imap_perform_search(data);
1597 else if(imap->mailbox && !selected &&
1598 (imap->custom || imap->uid || imap->mindex || imap->query))
1599 /* SELECT the mailbox */
1600 result = imap_perform_select(data);
1601 else
1602 /* LIST */
1603 result = imap_perform_list(data);
1604
1605 if(result)
1606 return result;
1607
1608 /* Run the state-machine */
1609 result = imap_multi_statemach(data, dophase_done);
1610
1611 *connected = Curl_conn_is_connected(conn, FIRSTSOCKET);
1612
1613 if(*dophase_done)
1614 DEBUGF(infof(data, "DO phase is complete"));
1615
1616 return result;
1617 }
1618
1619 /***********************************************************************
1620 *
1621 * imap_do()
1622 *
1623 * This function is registered as 'curl_do' function. It decodes the path
1624 * parts etc as a wrapper to the actual DO function (imap_perform).
1625 *
1626 * The input argument is already checked for validity.
1627 */
imap_do(struct Curl_easy * data,bool * done)1628 static CURLcode imap_do(struct Curl_easy *data, bool *done)
1629 {
1630 CURLcode result = CURLE_OK;
1631 *done = FALSE; /* default to false */
1632
1633 /* Parse the URL path */
1634 result = imap_parse_url_path(data);
1635 if(result)
1636 return result;
1637
1638 /* Parse the custom request */
1639 result = imap_parse_custom_request(data);
1640 if(result)
1641 return result;
1642
1643 result = imap_regular_transfer(data, done);
1644
1645 return result;
1646 }
1647
1648 /***********************************************************************
1649 *
1650 * imap_disconnect()
1651 *
1652 * Disconnect from an IMAP server. Cleanup protocol-specific per-connection
1653 * resources. BLOCKING.
1654 */
imap_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)1655 static CURLcode imap_disconnect(struct Curl_easy *data,
1656 struct connectdata *conn, bool dead_connection)
1657 {
1658 struct imap_conn *imapc = &conn->proto.imapc;
1659 (void)data;
1660
1661 /* We cannot send quit unconditionally. If this connection is stale or
1662 bad in any way, sending quit and waiting around here will make the
1663 disconnect wait in vain and cause more problems than we need to. */
1664
1665 /* The IMAP session may or may not have been allocated/setup at this
1666 point! */
1667 if(!dead_connection && conn->bits.protoconnstart) {
1668 if(!imap_perform_logout(data))
1669 (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
1670 }
1671
1672 /* Disconnect from the server */
1673 Curl_pp_disconnect(&imapc->pp);
1674 Curl_dyn_free(&imapc->dyn);
1675
1676 /* Cleanup the SASL module */
1677 Curl_sasl_cleanup(conn, imapc->sasl.authused);
1678
1679 /* Cleanup our connection based variables */
1680 Curl_safefree(imapc->mailbox);
1681 Curl_safefree(imapc->mailbox_uidvalidity);
1682
1683 return CURLE_OK;
1684 }
1685
1686 /* Call this when the DO phase has completed */
imap_dophase_done(struct Curl_easy * data,bool connected)1687 static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
1688 {
1689 struct IMAP *imap = data->req.p.imap;
1690
1691 (void)connected;
1692
1693 if(imap->transfer != PPTRANSFER_BODY)
1694 /* no data to transfer */
1695 Curl_xfer_setup_nop(data);
1696
1697 return CURLE_OK;
1698 }
1699
1700 /* Called from multi.c while DOing */
imap_doing(struct Curl_easy * data,bool * dophase_done)1701 static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
1702 {
1703 CURLcode result = imap_multi_statemach(data, dophase_done);
1704
1705 if(result)
1706 DEBUGF(infof(data, "DO phase failed"));
1707 else if(*dophase_done) {
1708 result = imap_dophase_done(data, FALSE /* not connected */);
1709
1710 DEBUGF(infof(data, "DO phase is complete"));
1711 }
1712
1713 return result;
1714 }
1715
1716 /***********************************************************************
1717 *
1718 * imap_regular_transfer()
1719 *
1720 * The input argument is already checked for validity.
1721 *
1722 * Performs all commands done before a regular transfer between a local and a
1723 * remote host.
1724 */
imap_regular_transfer(struct Curl_easy * data,bool * dophase_done)1725 static CURLcode imap_regular_transfer(struct Curl_easy *data,
1726 bool *dophase_done)
1727 {
1728 CURLcode result = CURLE_OK;
1729 bool connected = FALSE;
1730
1731 /* Make sure size is unknown at this point */
1732 data->req.size = -1;
1733
1734 /* Set the progress data */
1735 Curl_pgrsSetUploadCounter(data, 0);
1736 Curl_pgrsSetDownloadCounter(data, 0);
1737 Curl_pgrsSetUploadSize(data, -1);
1738 Curl_pgrsSetDownloadSize(data, -1);
1739
1740 /* Carry out the perform */
1741 result = imap_perform(data, &connected, dophase_done);
1742
1743 /* Perform post DO phase operations if necessary */
1744 if(!result && *dophase_done)
1745 result = imap_dophase_done(data, connected);
1746
1747 return result;
1748 }
1749
imap_setup_connection(struct Curl_easy * data,struct connectdata * conn)1750 static CURLcode imap_setup_connection(struct Curl_easy *data,
1751 struct connectdata *conn)
1752 {
1753 /* Initialise the IMAP layer */
1754 CURLcode result = imap_init(data);
1755 if(result)
1756 return result;
1757
1758 /* Clear the TLS upgraded flag */
1759 conn->bits.tls_upgraded = FALSE;
1760
1761 return CURLE_OK;
1762 }
1763
1764 /***********************************************************************
1765 *
1766 * imap_sendf()
1767 *
1768 * Sends the formatted string as an IMAP command to the server.
1769 *
1770 * Designed to never block.
1771 */
imap_sendf(struct Curl_easy * data,const char * fmt,...)1772 static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
1773 {
1774 CURLcode result = CURLE_OK;
1775 struct imap_conn *imapc = &data->conn->proto.imapc;
1776
1777 DEBUGASSERT(fmt);
1778
1779 /* Calculate the tag based on the connection ID and command ID */
1780 msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
1781 'A' + curlx_sltosi((long)(data->conn->connection_id % 26)),
1782 ++imapc->cmdid);
1783
1784 /* start with a blank buffer */
1785 Curl_dyn_reset(&imapc->dyn);
1786
1787 /* append tag + space + fmt */
1788 result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
1789 if(!result) {
1790 va_list ap;
1791 va_start(ap, fmt);
1792 #ifdef __clang__
1793 #pragma clang diagnostic push
1794 #pragma clang diagnostic ignored "-Wformat-nonliteral"
1795 #endif
1796 result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
1797 #ifdef __clang__
1798 #pragma clang diagnostic pop
1799 #endif
1800 va_end(ap);
1801 }
1802 return result;
1803 }
1804
1805 /***********************************************************************
1806 *
1807 * imap_atom()
1808 *
1809 * Checks the input string for characters that need escaping and returns an
1810 * atom ready for sending to the server.
1811 *
1812 * The returned string needs to be freed.
1813 *
1814 */
imap_atom(const char * str,bool escape_only)1815 static char *imap_atom(const char *str, bool escape_only)
1816 {
1817 struct dynbuf line;
1818 size_t nclean;
1819 size_t len;
1820
1821 if(!str)
1822 return NULL;
1823
1824 len = strlen(str);
1825 nclean = strcspn(str, "() {%*]\\\"");
1826 if(len == nclean)
1827 /* nothing to escape, return a strdup */
1828 return strdup(str);
1829
1830 Curl_dyn_init(&line, 2000);
1831
1832 if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
1833 return NULL;
1834
1835 while(*str) {
1836 if((*str == '\\' || *str == '"') &&
1837 Curl_dyn_addn(&line, "\\", 1))
1838 return NULL;
1839 if(Curl_dyn_addn(&line, str, 1))
1840 return NULL;
1841 str++;
1842 }
1843
1844 if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
1845 return NULL;
1846
1847 return Curl_dyn_ptr(&line);
1848 }
1849
1850 /***********************************************************************
1851 *
1852 * imap_is_bchar()
1853 *
1854 * Portable test of whether the specified char is a "bchar" as defined in the
1855 * grammar of RFC-5092.
1856 */
imap_is_bchar(char ch)1857 static bool imap_is_bchar(char ch)
1858 {
1859 /* Performing the alnum check with this macro is faster because of ASCII
1860 arithmetic */
1861 if(ISALNUM(ch))
1862 return TRUE;
1863
1864 switch(ch) {
1865 /* bchar */
1866 case ':': case '@': case '/':
1867 /* bchar -> achar */
1868 case '&': case '=':
1869 /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */
1870 case '-': case '.': case '_': case '~':
1871 /* bchar -> achar -> uchar -> sub-delims-sh */
1872 case '!': case '$': case '\'': case '(': case ')': case '*':
1873 case '+': case ',':
1874 /* bchar -> achar -> uchar -> pct-encoded */
1875 case '%': /* HEXDIG chars are already included above */
1876 return TRUE;
1877
1878 default:
1879 return FALSE;
1880 }
1881 }
1882
1883 /***********************************************************************
1884 *
1885 * imap_parse_url_options()
1886 *
1887 * Parse the URL login options.
1888 */
imap_parse_url_options(struct connectdata * conn)1889 static CURLcode imap_parse_url_options(struct connectdata *conn)
1890 {
1891 CURLcode result = CURLE_OK;
1892 struct imap_conn *imapc = &conn->proto.imapc;
1893 const char *ptr = conn->options;
1894 bool prefer_login = FALSE;
1895
1896 while(!result && ptr && *ptr) {
1897 const char *key = ptr;
1898 const char *value;
1899
1900 while(*ptr && *ptr != '=')
1901 ptr++;
1902
1903 value = ptr + 1;
1904
1905 while(*ptr && *ptr != ';')
1906 ptr++;
1907
1908 if(strncasecompare(key, "AUTH=+LOGIN", 11)) {
1909 /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
1910 prefer_login = TRUE;
1911 imapc->sasl.prefmech = SASL_AUTH_NONE;
1912 }
1913 else if(strncasecompare(key, "AUTH=", 5)) {
1914 prefer_login = FALSE;
1915 result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
1916 value, ptr - value);
1917 }
1918 else {
1919 prefer_login = FALSE;
1920 result = CURLE_URL_MALFORMAT;
1921 }
1922
1923 if(*ptr == ';')
1924 ptr++;
1925 }
1926
1927 if(prefer_login)
1928 imapc->preftype = IMAP_TYPE_CLEARTEXT;
1929 else {
1930 switch(imapc->sasl.prefmech) {
1931 case SASL_AUTH_NONE:
1932 imapc->preftype = IMAP_TYPE_NONE;
1933 break;
1934 case SASL_AUTH_DEFAULT:
1935 imapc->preftype = IMAP_TYPE_ANY;
1936 break;
1937 default:
1938 imapc->preftype = IMAP_TYPE_SASL;
1939 break;
1940 }
1941 }
1942
1943 return result;
1944 }
1945
1946 /***********************************************************************
1947 *
1948 * imap_parse_url_path()
1949 *
1950 * Parse the URL path into separate path components.
1951 *
1952 */
imap_parse_url_path(struct Curl_easy * data)1953 static CURLcode imap_parse_url_path(struct Curl_easy *data)
1954 {
1955 /* The imap struct is already initialised in imap_connect() */
1956 CURLcode result = CURLE_OK;
1957 struct IMAP *imap = data->req.p.imap;
1958 const char *begin = &data->state.up.path[1]; /* skip leading slash */
1959 const char *ptr = begin;
1960
1961 /* See how much of the URL is a valid path and decode it */
1962 while(imap_is_bchar(*ptr))
1963 ptr++;
1964
1965 if(ptr != begin) {
1966 /* Remove the trailing slash if present */
1967 const char *end = ptr;
1968 if(end > begin && end[-1] == '/')
1969 end--;
1970
1971 result = Curl_urldecode(begin, end - begin, &imap->mailbox, NULL,
1972 REJECT_CTRL);
1973 if(result)
1974 return result;
1975 }
1976 else
1977 imap->mailbox = NULL;
1978
1979 /* There can be any number of parameters in the form ";NAME=VALUE" */
1980 while(*ptr == ';') {
1981 char *name;
1982 char *value;
1983 size_t valuelen;
1984
1985 /* Find the length of the name parameter */
1986 begin = ++ptr;
1987 while(*ptr && *ptr != '=')
1988 ptr++;
1989
1990 if(!*ptr)
1991 return CURLE_URL_MALFORMAT;
1992
1993 /* Decode the name parameter */
1994 result = Curl_urldecode(begin, ptr - begin, &name, NULL,
1995 REJECT_CTRL);
1996 if(result)
1997 return result;
1998
1999 /* Find the length of the value parameter */
2000 begin = ++ptr;
2001 while(imap_is_bchar(*ptr))
2002 ptr++;
2003
2004 /* Decode the value parameter */
2005 result = Curl_urldecode(begin, ptr - begin, &value, &valuelen,
2006 REJECT_CTRL);
2007 if(result) {
2008 free(name);
2009 return result;
2010 }
2011
2012 DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", 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 Curl_easy * data)2085 static CURLcode imap_parse_custom_request(struct Curl_easy *data)
2086 {
2087 CURLcode result = CURLE_OK;
2088 struct IMAP *imap = data->req.p.imap;
2089 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2090
2091 if(custom) {
2092 /* URL decode the custom request */
2093 result = Curl_urldecode(custom, 0, &imap->custom, NULL, REJECT_CTRL);
2094
2095 /* Extract the parameters if specified */
2096 if(!result) {
2097 const char *params = imap->custom;
2098
2099 while(*params && *params != ' ')
2100 params++;
2101
2102 if(*params) {
2103 imap->custom_params = strdup(params);
2104 imap->custom[params - imap->custom] = '\0';
2105
2106 if(!imap->custom_params)
2107 result = CURLE_OUT_OF_MEMORY;
2108 }
2109 }
2110 }
2111
2112 return result;
2113 }
2114
2115 #endif /* CURL_DISABLE_IMAP */
2116