VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/imap.c@ 97138

最後變更 在這個檔案從97138是 95312,由 vboxsync 提交於 3 年 前

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette