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