source: asp3_tinet_ecnl_arm/trunk/curl-7.57.0/lib/imap.c@ 352

Last change on this file since 352 was 352, checked in by coas-nagasima, 6 years ago

arm向けASP3版ECNLを追加

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