comparison mupdf-source/thirdparty/curl/lib/imap.c @ 2:b50eed0cc0ef upstream

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