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