source: UsbWattMeter/trunk/curl-7.47.1/lib/imap.c@ 167

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

MIMEにSJISを設定

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