Annotation of embedaddon/curl/lib/smtp.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2020, 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: * RFC1870 SMTP Service Extension for Message Size
22: * RFC2195 CRAM-MD5 authentication
23: * RFC2831 DIGEST-MD5 authentication
24: * RFC3207 SMTP over TLS
25: * RFC4422 Simple Authentication and Security Layer (SASL)
26: * RFC4616 PLAIN authentication
27: * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28: * RFC4954 SMTP Authentication
29: * RFC5321 SMTP protocol
30: * RFC5890 Internationalized Domain Names for Applications (IDNA)
31: * RFC6531 SMTP Extension for Internationalized Email
32: * RFC6532 Internationalized Email Headers
33: * RFC6749 OAuth 2.0 Authorization Framework
34: * RFC8314 Use of TLS for Email Submission and Access
35: * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
36: * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
37: *
38: ***************************************************************************/
39:
40: #include "curl_setup.h"
41:
42: #ifndef CURL_DISABLE_SMTP
43:
44: #ifdef HAVE_NETINET_IN_H
45: #include <netinet/in.h>
46: #endif
47: #ifdef HAVE_ARPA_INET_H
48: #include <arpa/inet.h>
49: #endif
50: #ifdef HAVE_UTSNAME_H
51: #include <sys/utsname.h>
52: #endif
53: #ifdef HAVE_NETDB_H
54: #include <netdb.h>
55: #endif
56: #ifdef __VMS
57: #include <in.h>
58: #include <inet.h>
59: #endif
60:
61: #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
62: #undef in_addr_t
63: #define in_addr_t unsigned long
64: #endif
65:
66: #include <curl/curl.h>
67: #include "urldata.h"
68: #include "sendf.h"
69: #include "hostip.h"
70: #include "progress.h"
71: #include "transfer.h"
72: #include "escape.h"
73: #include "http.h" /* for HTTP proxy tunnel stuff */
74: #include "mime.h"
75: #include "socks.h"
76: #include "smtp.h"
77: #include "strtoofft.h"
78: #include "strcase.h"
79: #include "vtls/vtls.h"
80: #include "connect.h"
81: #include "strerror.h"
82: #include "select.h"
83: #include "multiif.h"
84: #include "url.h"
85: #include "curl_gethostname.h"
86: #include "curl_sasl.h"
87: #include "warnless.h"
88: /* The last 3 #include files should be in this order */
89: #include "curl_printf.h"
90: #include "curl_memory.h"
91: #include "memdebug.h"
92:
93: /* Local API functions */
94: static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95: static CURLcode smtp_do(struct connectdata *conn, bool *done);
96: static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
97: bool premature);
98: static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99: static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100: static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101: static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks);
102: static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
103: static CURLcode smtp_setup_connection(struct connectdata *conn);
104: static CURLcode smtp_parse_url_options(struct connectdata *conn);
105: static CURLcode smtp_parse_url_path(struct connectdata *conn);
106: static CURLcode smtp_parse_custom_request(struct connectdata *conn);
107: static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma,
108: char **address, struct hostname *host);
109: static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
110: const char *initresp);
111: static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
112: static void smtp_get_message(char *buffer, char **outptr);
113:
114: /*
115: * SMTP protocol handler.
116: */
117:
118: const struct Curl_handler Curl_handler_smtp = {
119: "SMTP", /* scheme */
120: smtp_setup_connection, /* setup_connection */
121: smtp_do, /* do_it */
122: smtp_done, /* done */
123: ZERO_NULL, /* do_more */
124: smtp_connect, /* connect_it */
125: smtp_multi_statemach, /* connecting */
126: smtp_doing, /* doing */
127: smtp_getsock, /* proto_getsock */
128: smtp_getsock, /* doing_getsock */
129: ZERO_NULL, /* domore_getsock */
130: ZERO_NULL, /* perform_getsock */
131: smtp_disconnect, /* disconnect */
132: ZERO_NULL, /* readwrite */
133: ZERO_NULL, /* connection_check */
134: PORT_SMTP, /* defport */
135: CURLPROTO_SMTP, /* protocol */
136: PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
137: PROTOPT_URLOPTIONS
138: };
139:
140: #ifdef USE_SSL
141: /*
142: * SMTPS protocol handler.
143: */
144:
145: const struct Curl_handler Curl_handler_smtps = {
146: "SMTPS", /* scheme */
147: smtp_setup_connection, /* setup_connection */
148: smtp_do, /* do_it */
149: smtp_done, /* done */
150: ZERO_NULL, /* do_more */
151: smtp_connect, /* connect_it */
152: smtp_multi_statemach, /* connecting */
153: smtp_doing, /* doing */
154: smtp_getsock, /* proto_getsock */
155: smtp_getsock, /* doing_getsock */
156: ZERO_NULL, /* domore_getsock */
157: ZERO_NULL, /* perform_getsock */
158: smtp_disconnect, /* disconnect */
159: ZERO_NULL, /* readwrite */
160: ZERO_NULL, /* connection_check */
161: PORT_SMTPS, /* defport */
162: CURLPROTO_SMTPS, /* protocol */
163: PROTOPT_CLOSEACTION | PROTOPT_SSL
164: | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
165: };
166: #endif
167:
168: /* SASL parameters for the smtp protocol */
169: static const struct SASLproto saslsmtp = {
170: "smtp", /* The service name */
171: 334, /* Code received when continuation is expected */
172: 235, /* Code to receive upon authentication success */
173: 512 - 8, /* Maximum initial response length (no max) */
174: smtp_perform_auth, /* Send authentication command */
175: smtp_continue_auth, /* Send authentication continuation */
176: smtp_get_message /* Get SASL response message */
177: };
178:
179: #ifdef USE_SSL
180: static void smtp_to_smtps(struct connectdata *conn)
181: {
182: /* Change the connection handler */
183: conn->handler = &Curl_handler_smtps;
184:
185: /* Set the connection's upgraded to TLS flag */
186: conn->tls_upgraded = TRUE;
187: }
188: #else
189: #define smtp_to_smtps(x) Curl_nop_stmt
190: #endif
191:
192: /***********************************************************************
193: *
194: * smtp_endofresp()
195: *
196: * Checks for an ending SMTP status code at the start of the given string, but
197: * also detects various capabilities from the EHLO response including the
198: * supported authentication mechanisms.
199: */
200: static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
201: int *resp)
202: {
203: struct smtp_conn *smtpc = &conn->proto.smtpc;
204: bool result = FALSE;
205:
206: /* Nothing for us */
207: if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
208: return FALSE;
209:
210: /* Do we have a command response? This should be the response code followed
211: by a space and optionally some text as per RFC-5321 and as outlined in
212: Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
213: only send the response code instead as per Section 4.2. */
214: if(line[3] == ' ' || len == 5) {
215: char tmpline[6];
216:
217: result = TRUE;
218: memset(tmpline, '\0', sizeof(tmpline));
219: memcpy(tmpline, line, (len == 5 ? 5 : 3));
220: *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
221:
222: /* Make sure real server never sends internal value */
223: if(*resp == 1)
224: *resp = 0;
225: }
226: /* Do we have a multiline (continuation) response? */
227: else if(line[3] == '-' &&
228: (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
229: result = TRUE;
230: *resp = 1; /* Internal response code */
231: }
232:
233: return result;
234: }
235:
236: /***********************************************************************
237: *
238: * smtp_get_message()
239: *
240: * Gets the authentication message from the response buffer.
241: */
242: static void smtp_get_message(char *buffer, char **outptr)
243: {
244: size_t len = strlen(buffer);
245: char *message = NULL;
246:
247: if(len > 4) {
248: /* Find the start of the message */
249: len -= 4;
250: for(message = buffer + 4; *message == ' ' || *message == '\t';
251: message++, len--)
252: ;
253:
254: /* Find the end of the message */
255: for(; len--;)
256: if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
257: message[len] != '\t')
258: break;
259:
260: /* Terminate the message */
261: if(++len) {
262: message[len] = '\0';
263: }
264: }
265: else
266: /* junk input => zero length output */
267: message = &buffer[len];
268:
269: *outptr = message;
270: }
271:
272: /***********************************************************************
273: *
274: * state()
275: *
276: * This is the ONLY way to change SMTP state!
277: */
278: static void state(struct connectdata *conn, smtpstate newstate)
279: {
280: struct smtp_conn *smtpc = &conn->proto.smtpc;
281: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
282: /* for debug purposes */
283: static const char * const names[] = {
284: "STOP",
285: "SERVERGREET",
286: "EHLO",
287: "HELO",
288: "STARTTLS",
289: "UPGRADETLS",
290: "AUTH",
291: "COMMAND",
292: "MAIL",
293: "RCPT",
294: "DATA",
295: "POSTDATA",
296: "QUIT",
297: /* LAST */
298: };
299:
300: if(smtpc->state != newstate)
301: infof(conn->data, "SMTP %p state change from %s to %s\n",
302: (void *)smtpc, names[smtpc->state], names[newstate]);
303: #endif
304:
305: smtpc->state = newstate;
306: }
307:
308: /***********************************************************************
309: *
310: * smtp_perform_ehlo()
311: *
312: * Sends the EHLO command to not only initialise communication with the ESMTP
313: * server but to also obtain a list of server side supported capabilities.
314: */
315: static CURLcode smtp_perform_ehlo(struct connectdata *conn)
316: {
317: CURLcode result = CURLE_OK;
318: struct smtp_conn *smtpc = &conn->proto.smtpc;
319:
320: smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
321: smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
322: used for esmtp connections */
323: smtpc->tls_supported = FALSE; /* Clear the TLS capability */
324: smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
325:
326: /* Send the EHLO command */
327: result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
328:
329: if(!result)
330: state(conn, SMTP_EHLO);
331:
332: return result;
333: }
334:
335: /***********************************************************************
336: *
337: * smtp_perform_helo()
338: *
339: * Sends the HELO command to initialise communication with the SMTP server.
340: */
341: static CURLcode smtp_perform_helo(struct connectdata *conn)
342: {
343: CURLcode result = CURLE_OK;
344: struct smtp_conn *smtpc = &conn->proto.smtpc;
345:
346: smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
347: in smtp connections */
348:
349: /* Send the HELO command */
350: result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
351:
352: if(!result)
353: state(conn, SMTP_HELO);
354:
355: return result;
356: }
357:
358: /***********************************************************************
359: *
360: * smtp_perform_starttls()
361: *
362: * Sends the STLS command to start the upgrade to TLS.
363: */
364: static CURLcode smtp_perform_starttls(struct connectdata *conn)
365: {
366: /* Send the STARTTLS command */
367: CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
368:
369: if(!result)
370: state(conn, SMTP_STARTTLS);
371:
372: return result;
373: }
374:
375: /***********************************************************************
376: *
377: * smtp_perform_upgrade_tls()
378: *
379: * Performs the upgrade to TLS.
380: */
381: static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
382: {
383: /* Start the SSL connection */
384: struct smtp_conn *smtpc = &conn->proto.smtpc;
385: CURLcode result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET,
386: &smtpc->ssldone);
387:
388: if(!result) {
389: if(smtpc->state != SMTP_UPGRADETLS)
390: state(conn, SMTP_UPGRADETLS);
391:
392: if(smtpc->ssldone) {
393: smtp_to_smtps(conn);
394: result = smtp_perform_ehlo(conn);
395: }
396: }
397:
398: return result;
399: }
400:
401: /***********************************************************************
402: *
403: * smtp_perform_auth()
404: *
405: * Sends an AUTH command allowing the client to login with the given SASL
406: * authentication mechanism.
407: */
408: static CURLcode smtp_perform_auth(struct connectdata *conn,
409: const char *mech,
410: const char *initresp)
411: {
412: CURLcode result = CURLE_OK;
413: struct smtp_conn *smtpc = &conn->proto.smtpc;
414:
415: if(initresp) { /* AUTH <mech> ...<crlf> */
416: /* Send the AUTH command with the initial response */
417: result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
418: }
419: else {
420: /* Send the AUTH command */
421: result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
422: }
423:
424: return result;
425: }
426:
427: /***********************************************************************
428: *
429: * smtp_continue_auth()
430: *
431: * Sends SASL continuation data or cancellation.
432: */
433: static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
434: {
435: struct smtp_conn *smtpc = &conn->proto.smtpc;
436:
437: return Curl_pp_sendf(&smtpc->pp, "%s", resp);
438: }
439:
440: /***********************************************************************
441: *
442: * smtp_perform_authentication()
443: *
444: * Initiates the authentication sequence, with the appropriate SASL
445: * authentication mechanism.
446: */
447: static CURLcode smtp_perform_authentication(struct connectdata *conn)
448: {
449: CURLcode result = CURLE_OK;
450: struct smtp_conn *smtpc = &conn->proto.smtpc;
451: saslprogress progress;
452:
453: /* Check we have enough data to authenticate with, and the
454: server supports authentiation, and end the connect phase if not */
455: if(!smtpc->auth_supported ||
456: !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
457: state(conn, SMTP_STOP);
458: return result;
459: }
460:
461: /* Calculate the SASL login details */
462: result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
463:
464: if(!result) {
465: if(progress == SASL_INPROGRESS)
466: state(conn, SMTP_AUTH);
467: else {
468: /* Other mechanisms not supported */
469: infof(conn->data, "No known authentication mechanisms supported!\n");
470: result = CURLE_LOGIN_DENIED;
471: }
472: }
473:
474: return result;
475: }
476:
477: /***********************************************************************
478: *
479: * smtp_perform_command()
480: *
481: * Sends a SMTP based command.
482: */
483: static CURLcode smtp_perform_command(struct connectdata *conn)
484: {
485: CURLcode result = CURLE_OK;
486: struct Curl_easy *data = conn->data;
487: struct SMTP *smtp = data->req.protop;
488:
489: if(smtp->rcpt) {
490: /* We notify the server we are sending UTF-8 data if a) it supports the
491: SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
492: either the local address or host name parts. This is regardless of
493: whether the host name is encoded using IDN ACE */
494: bool utf8 = FALSE;
495:
496: if((!smtp->custom) || (!smtp->custom[0])) {
497: char *address = NULL;
498: struct hostname host = { NULL, NULL, NULL, NULL };
499:
500: /* Parse the mailbox to verify into the local address and host name
501: parts, converting the host name to an IDN A-label if necessary */
502: result = smtp_parse_address(conn, smtp->rcpt->data,
503: &address, &host);
504: if(result)
505: return result;
506:
507: /* Establish whether we should report SMTPUTF8 to the server for this
508: mailbox as per RFC-6531 sect. 3.1 point 6 */
509: utf8 = (conn->proto.smtpc.utf8_supported) &&
510: ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
511: (!Curl_is_ASCII_name(host.name)));
512:
513: /* Send the VRFY command (Note: The host name part may be absent when the
514: host is a local system) */
515: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "VRFY %s%s%s%s",
516: address,
517: host.name ? "@" : "",
518: host.name ? host.name : "",
519: utf8 ? " SMTPUTF8" : "");
520:
521: Curl_free_idnconverted_hostname(&host);
522: free(address);
523: }
524: else {
525: /* Establish whether we should report that we support SMTPUTF8 for EXPN
526: commands to the server as per RFC-6531 sect. 3.1 point 6 */
527: utf8 = (conn->proto.smtpc.utf8_supported) &&
528: (!strcmp(smtp->custom, "EXPN"));
529:
530: /* Send the custom recipient based command such as the EXPN command */
531: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s%s", smtp->custom,
532: smtp->rcpt->data,
533: utf8 ? " SMTPUTF8" : "");
534: }
535: }
536: else
537: /* Send the non-recipient based command such as HELP */
538: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
539: smtp->custom && smtp->custom[0] != '\0' ?
540: smtp->custom : "HELP");
541:
542: if(!result)
543: state(conn, SMTP_COMMAND);
544:
545: return result;
546: }
547:
548: /***********************************************************************
549: *
550: * smtp_perform_mail()
551: *
552: * Sends an MAIL command to initiate the upload of a message.
553: */
554: static CURLcode smtp_perform_mail(struct connectdata *conn)
555: {
556: char *from = NULL;
557: char *auth = NULL;
558: char *size = NULL;
559: CURLcode result = CURLE_OK;
560: struct Curl_easy *data = conn->data;
561:
562: /* We notify the server we are sending UTF-8 data if a) it supports the
563: SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
564: either the local address or host name parts. This is regardless of
565: whether the host name is encoded using IDN ACE */
566: bool utf8 = FALSE;
567:
568: /* Calculate the FROM parameter */
569: if(data->set.str[STRING_MAIL_FROM]) {
570: char *address = NULL;
571: struct hostname host = { NULL, NULL, NULL, NULL };
572:
573: /* Parse the FROM mailbox into the local address and host name parts,
574: converting the host name to an IDN A-label if necessary */
575: result = smtp_parse_address(conn, data->set.str[STRING_MAIL_FROM],
576: &address, &host);
577: if(result)
578: return result;
579:
580: /* Establish whether we should report SMTPUTF8 to the server for this
581: mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
582: utf8 = (conn->proto.smtpc.utf8_supported) &&
583: ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
584: (!Curl_is_ASCII_name(host.name)));
585:
586: if(host.name) {
587: from = aprintf("<%s@%s>", address, host.name);
588:
589: Curl_free_idnconverted_hostname(&host);
590: }
591: else
592: /* An invalid mailbox was provided but we'll simply let the server worry
593: about that and reply with a 501 error */
594: from = aprintf("<%s>", address);
595:
596: free(address);
597: }
598: else
599: /* Null reverse-path, RFC-5321, sect. 3.6.3 */
600: from = strdup("<>");
601:
602: if(!from)
603: return CURLE_OUT_OF_MEMORY;
604:
605: /* Calculate the optional AUTH parameter */
606: if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
607: if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
608: char *address = NULL;
609: struct hostname host = { NULL, NULL, NULL, NULL };
610:
611: /* Parse the AUTH mailbox into the local address and host name parts,
612: converting the host name to an IDN A-label if necessary */
613: result = smtp_parse_address(conn, data->set.str[STRING_MAIL_AUTH],
614: &address, &host);
615: if(result) {
616: free(from);
617: return result;
618: }
619:
620: /* Establish whether we should report SMTPUTF8 to the server for this
621: mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
622: if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
623: ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
624: (!Curl_is_ASCII_name(host.name))))
625: utf8 = TRUE;
626:
627: if(host.name) {
628: auth = aprintf("<%s@%s>", address, host.name);
629:
630: Curl_free_idnconverted_hostname(&host);
631: }
632: else
633: /* An invalid mailbox was provided but we'll simply let the server
634: worry about it */
635: auth = aprintf("<%s>", address);
636:
637: free(address);
638: }
639: else
640: /* Empty AUTH, RFC-2554, sect. 5 */
641: auth = strdup("<>");
642:
643: if(!auth) {
644: free(from);
645:
646: return CURLE_OUT_OF_MEMORY;
647: }
648: }
649:
650: /* Prepare the mime data if some. */
651: if(data->set.mimepost.kind != MIMEKIND_NONE) {
652: /* Use the whole structure as data. */
653: data->set.mimepost.flags &= ~MIME_BODY_ONLY;
654:
655: /* Add external headers and mime version. */
656: curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
657: result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
658: NULL, MIMESTRATEGY_MAIL);
659:
660: if(!result)
661: if(!Curl_checkheaders(conn, "Mime-Version"))
662: result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
663: "Mime-Version: 1.0");
664:
665: /* Make sure we will read the entire mime structure. */
666: if(!result)
667: result = Curl_mime_rewind(&data->set.mimepost);
668:
669: if(result) {
670: free(from);
671: free(auth);
672:
673: return result;
674: }
675:
676: data->state.infilesize = Curl_mime_size(&data->set.mimepost);
677:
678: /* Read from mime structure. */
679: data->state.fread_func = (curl_read_callback) Curl_mime_read;
680: data->state.in = (void *) &data->set.mimepost;
681: }
682:
683: /* Calculate the optional SIZE parameter */
684: if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
685: size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
686:
687: if(!size) {
688: free(from);
689: free(auth);
690:
691: return CURLE_OUT_OF_MEMORY;
692: }
693: }
694:
695: /* If the mailboxes in the FROM and AUTH parameters don't include a UTF-8
696: based address then quickly scan through the recipient list and check if
697: any there do, as we need to correctly identify our support for SMTPUTF8
698: in the envelope, as per RFC-6531 sect. 3.4 */
699: if(conn->proto.smtpc.utf8_supported && !utf8) {
700: struct SMTP *smtp = data->req.protop;
701: struct curl_slist *rcpt = smtp->rcpt;
702:
703: while(rcpt && !utf8) {
704: /* Does the host name contain non-ASCII characters? */
705: if(!Curl_is_ASCII_name(rcpt->data))
706: utf8 = TRUE;
707:
708: rcpt = rcpt->next;
709: }
710: }
711:
712: /* Send the MAIL command */
713: result = Curl_pp_sendf(&conn->proto.smtpc.pp,
714: "MAIL FROM:%s%s%s%s%s%s",
715: from, /* Mandatory */
716: auth ? " AUTH=" : "", /* Optional on AUTH support */
717: auth ? auth : "", /* */
718: size ? " SIZE=" : "", /* Optional on SIZE support */
719: size ? size : "", /* */
720: utf8 ? " SMTPUTF8" /* Internationalised mailbox */
721: : ""); /* included in our envelope */
722:
723: free(from);
724: free(auth);
725: free(size);
726:
727: if(!result)
728: state(conn, SMTP_MAIL);
729:
730: return result;
731: }
732:
733: /***********************************************************************
734: *
735: * smtp_perform_rcpt_to()
736: *
737: * Sends a RCPT TO command for a given recipient as part of the message upload
738: * process.
739: */
740: static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
741: {
742: CURLcode result = CURLE_OK;
743: struct Curl_easy *data = conn->data;
744: struct SMTP *smtp = data->req.protop;
745: char *address = NULL;
746: struct hostname host = { NULL, NULL, NULL, NULL };
747:
748: /* Parse the recipient mailbox into the local address and host name parts,
749: converting the host name to an IDN A-label if necessary */
750: result = smtp_parse_address(conn, smtp->rcpt->data,
751: &address, &host);
752: if(result)
753: return result;
754:
755: /* Send the RCPT TO command */
756: if(host.name)
757: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s@%s>", address,
758: host.name);
759: else
760: /* An invalid mailbox was provided but we'll simply let the server worry
761: about that and reply with a 501 error */
762: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>", address);
763:
764: Curl_free_idnconverted_hostname(&host);
765: free(address);
766:
767: if(!result)
768: state(conn, SMTP_RCPT);
769:
770: return result;
771: }
772:
773: /***********************************************************************
774: *
775: * smtp_perform_quit()
776: *
777: * Performs the quit action prior to sclose() being called.
778: */
779: static CURLcode smtp_perform_quit(struct connectdata *conn)
780: {
781: /* Send the QUIT command */
782: CURLcode result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
783:
784: if(!result)
785: state(conn, SMTP_QUIT);
786:
787: return result;
788: }
789:
790: /* For the initial server greeting */
791: static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
792: int smtpcode,
793: smtpstate instate)
794: {
795: CURLcode result = CURLE_OK;
796: struct Curl_easy *data = conn->data;
797:
798: (void)instate; /* no use for this yet */
799:
800: if(smtpcode/100 != 2) {
801: failf(data, "Got unexpected smtp-server response: %d", smtpcode);
802: result = CURLE_WEIRD_SERVER_REPLY;
803: }
804: else
805: result = smtp_perform_ehlo(conn);
806:
807: return result;
808: }
809:
810: /* For STARTTLS responses */
811: static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
812: int smtpcode,
813: smtpstate instate)
814: {
815: CURLcode result = CURLE_OK;
816: struct Curl_easy *data = conn->data;
817:
818: (void)instate; /* no use for this yet */
819:
820: if(smtpcode != 220) {
821: if(data->set.use_ssl != CURLUSESSL_TRY) {
822: failf(data, "STARTTLS denied, code %d", smtpcode);
823: result = CURLE_USE_SSL_FAILED;
824: }
825: else
826: result = smtp_perform_authentication(conn);
827: }
828: else
829: result = smtp_perform_upgrade_tls(conn);
830:
831: return result;
832: }
833:
834: /* For EHLO responses */
835: static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
836: smtpstate instate)
837: {
838: CURLcode result = CURLE_OK;
839: struct Curl_easy *data = conn->data;
840: struct smtp_conn *smtpc = &conn->proto.smtpc;
841: const char *line = data->state.buffer;
842: size_t len = strlen(line);
843:
844: (void)instate; /* no use for this yet */
845:
846: if(smtpcode/100 != 2 && smtpcode != 1) {
847: if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
848: result = smtp_perform_helo(conn);
849: else {
850: failf(data, "Remote access denied: %d", smtpcode);
851: result = CURLE_REMOTE_ACCESS_DENIED;
852: }
853: }
854: else if(len >= 4) {
855: line += 4;
856: len -= 4;
857:
858: /* Does the server support the STARTTLS capability? */
859: if(len >= 8 && !memcmp(line, "STARTTLS", 8))
860: smtpc->tls_supported = TRUE;
861:
862: /* Does the server support the SIZE capability? */
863: else if(len >= 4 && !memcmp(line, "SIZE", 4))
864: smtpc->size_supported = TRUE;
865:
866: /* Does the server support the UTF-8 capability? */
867: else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8))
868: smtpc->utf8_supported = TRUE;
869:
870: /* Does the server support authentication? */
871: else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
872: smtpc->auth_supported = TRUE;
873:
874: /* Advance past the AUTH keyword */
875: line += 5;
876: len -= 5;
877:
878: /* Loop through the data line */
879: for(;;) {
880: size_t llen;
881: size_t wordlen;
882: unsigned int mechbit;
883:
884: while(len &&
885: (*line == ' ' || *line == '\t' ||
886: *line == '\r' || *line == '\n')) {
887:
888: line++;
889: len--;
890: }
891:
892: if(!len)
893: break;
894:
895: /* Extract the word */
896: for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
897: line[wordlen] != '\t' && line[wordlen] != '\r' &&
898: line[wordlen] != '\n';)
899: wordlen++;
900:
901: /* Test the word for a matching authentication mechanism */
902: mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
903: if(mechbit && llen == wordlen)
904: smtpc->sasl.authmechs |= mechbit;
905:
906: line += wordlen;
907: len -= wordlen;
908: }
909: }
910:
911: if(smtpcode != 1) {
912: if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
913: /* We don't have a SSL/TLS connection yet, but SSL is requested */
914: if(smtpc->tls_supported)
915: /* Switch to TLS connection now */
916: result = smtp_perform_starttls(conn);
917: else if(data->set.use_ssl == CURLUSESSL_TRY)
918: /* Fallback and carry on with authentication */
919: result = smtp_perform_authentication(conn);
920: else {
921: failf(data, "STARTTLS not supported.");
922: result = CURLE_USE_SSL_FAILED;
923: }
924: }
925: else
926: result = smtp_perform_authentication(conn);
927: }
928: }
929: else {
930: failf(data, "Unexpectedly short EHLO response");
931: result = CURLE_WEIRD_SERVER_REPLY;
932: }
933:
934: return result;
935: }
936:
937: /* For HELO responses */
938: static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
939: smtpstate instate)
940: {
941: CURLcode result = CURLE_OK;
942: struct Curl_easy *data = conn->data;
943:
944: (void)instate; /* no use for this yet */
945:
946: if(smtpcode/100 != 2) {
947: failf(data, "Remote access denied: %d", smtpcode);
948: result = CURLE_REMOTE_ACCESS_DENIED;
949: }
950: else
951: /* End of connect phase */
952: state(conn, SMTP_STOP);
953:
954: return result;
955: }
956:
957: /* For SASL authentication responses */
958: static CURLcode smtp_state_auth_resp(struct connectdata *conn,
959: int smtpcode,
960: smtpstate instate)
961: {
962: CURLcode result = CURLE_OK;
963: struct Curl_easy *data = conn->data;
964: struct smtp_conn *smtpc = &conn->proto.smtpc;
965: saslprogress progress;
966:
967: (void)instate; /* no use for this yet */
968:
969: result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
970: if(!result)
971: switch(progress) {
972: case SASL_DONE:
973: state(conn, SMTP_STOP); /* Authenticated */
974: break;
975: case SASL_IDLE: /* No mechanism left after cancellation */
976: failf(data, "Authentication cancelled");
977: result = CURLE_LOGIN_DENIED;
978: break;
979: default:
980: break;
981: }
982:
983: return result;
984: }
985:
986: /* For command responses */
987: static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
988: smtpstate instate)
989: {
990: CURLcode result = CURLE_OK;
991: struct Curl_easy *data = conn->data;
992: struct SMTP *smtp = data->req.protop;
993: char *line = data->state.buffer;
994: size_t len = strlen(line);
995:
996: (void)instate; /* no use for this yet */
997:
998: if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
999: (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1000: failf(data, "Command failed: %d", smtpcode);
1001: result = CURLE_RECV_ERROR;
1002: }
1003: else {
1004: /* Temporarily add the LF character back and send as body to the client */
1005: if(!data->set.opt_no_body) {
1006: line[len] = '\n';
1007: result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1008: line[len] = '\0';
1009: }
1010:
1011: if(smtpcode != 1) {
1012: if(smtp->rcpt) {
1013: smtp->rcpt = smtp->rcpt->next;
1014:
1015: if(smtp->rcpt) {
1016: /* Send the next command */
1017: result = smtp_perform_command(conn);
1018: }
1019: else
1020: /* End of DO phase */
1021: state(conn, SMTP_STOP);
1022: }
1023: else
1024: /* End of DO phase */
1025: state(conn, SMTP_STOP);
1026: }
1027: }
1028:
1029: return result;
1030: }
1031:
1032: /* For MAIL responses */
1033: static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1034: smtpstate instate)
1035: {
1036: CURLcode result = CURLE_OK;
1037: struct Curl_easy *data = conn->data;
1038:
1039: (void)instate; /* no use for this yet */
1040:
1041: if(smtpcode/100 != 2) {
1042: failf(data, "MAIL failed: %d", smtpcode);
1043: result = CURLE_SEND_ERROR;
1044: }
1045: else
1046: /* Start the RCPT TO command */
1047: result = smtp_perform_rcpt_to(conn);
1048:
1049: return result;
1050: }
1051:
1052: /* For RCPT responses */
1053: static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1054: smtpstate instate)
1055: {
1056: CURLcode result = CURLE_OK;
1057: struct Curl_easy *data = conn->data;
1058: struct SMTP *smtp = data->req.protop;
1059: bool is_smtp_err = FALSE;
1060: bool is_smtp_blocking_err = FALSE;
1061:
1062: (void)instate; /* no use for this yet */
1063:
1064: is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE;
1065:
1066: /* If there's multiple RCPT TO to be issued, it's possible to ignore errors
1067: and proceed with only the valid addresses. */
1068: is_smtp_blocking_err =
1069: (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE;
1070:
1071: if(is_smtp_err) {
1072: /* Remembering the last failure which we can report if all "RCPT TO" have
1073: failed and we cannot proceed. */
1074: smtp->rcpt_last_error = smtpcode;
1075:
1076: if(is_smtp_blocking_err) {
1077: failf(data, "RCPT failed: %d", smtpcode);
1078: result = CURLE_SEND_ERROR;
1079: }
1080: }
1081: else {
1082: /* Some RCPT TO commands have succeeded. */
1083: smtp->rcpt_had_ok = TRUE;
1084: }
1085:
1086: if(!is_smtp_blocking_err) {
1087: smtp->rcpt = smtp->rcpt->next;
1088:
1089: if(smtp->rcpt)
1090: /* Send the next RCPT TO command */
1091: result = smtp_perform_rcpt_to(conn);
1092: else {
1093: /* We weren't able to issue a successful RCPT TO command while going
1094: over recipients (potentially multiple). Sending back last error. */
1095: if(!smtp->rcpt_had_ok) {
1096: failf(data, "RCPT failed: %d (last error)", smtp->rcpt_last_error);
1097: result = CURLE_SEND_ERROR;
1098: }
1099: else {
1100: /* Send the DATA command */
1101: result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1102:
1103: if(!result)
1104: state(conn, SMTP_DATA);
1105: }
1106: }
1107: }
1108:
1109: return result;
1110: }
1111:
1112: /* For DATA response */
1113: static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1114: smtpstate instate)
1115: {
1116: CURLcode result = CURLE_OK;
1117: struct Curl_easy *data = conn->data;
1118:
1119: (void)instate; /* no use for this yet */
1120:
1121: if(smtpcode != 354) {
1122: failf(data, "DATA failed: %d", smtpcode);
1123: result = CURLE_SEND_ERROR;
1124: }
1125: else {
1126: /* Set the progress upload size */
1127: Curl_pgrsSetUploadSize(data, data->state.infilesize);
1128:
1129: /* SMTP upload */
1130: Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1131:
1132: /* End of DO phase */
1133: state(conn, SMTP_STOP);
1134: }
1135:
1136: return result;
1137: }
1138:
1139: /* For POSTDATA responses, which are received after the entire DATA
1140: part has been sent to the server */
1141: static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1142: int smtpcode,
1143: smtpstate instate)
1144: {
1145: CURLcode result = CURLE_OK;
1146:
1147: (void)instate; /* no use for this yet */
1148:
1149: if(smtpcode != 250)
1150: result = CURLE_RECV_ERROR;
1151:
1152: /* End of DONE phase */
1153: state(conn, SMTP_STOP);
1154:
1155: return result;
1156: }
1157:
1158: static CURLcode smtp_statemach_act(struct connectdata *conn)
1159: {
1160: CURLcode result = CURLE_OK;
1161: curl_socket_t sock = conn->sock[FIRSTSOCKET];
1162: struct Curl_easy *data = conn->data;
1163: int smtpcode;
1164: struct smtp_conn *smtpc = &conn->proto.smtpc;
1165: struct pingpong *pp = &smtpc->pp;
1166: size_t nread = 0;
1167:
1168: /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1169: if(smtpc->state == SMTP_UPGRADETLS)
1170: return smtp_perform_upgrade_tls(conn);
1171:
1172: /* Flush any data that needs to be sent */
1173: if(pp->sendleft)
1174: return Curl_pp_flushsend(pp);
1175:
1176: do {
1177: /* Read the response from the server */
1178: result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1179: if(result)
1180: return result;
1181:
1182: /* Store the latest response for later retrieval if necessary */
1183: if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1184: data->info.httpcode = smtpcode;
1185:
1186: if(!smtpcode)
1187: break;
1188:
1189: /* We have now received a full SMTP server response */
1190: switch(smtpc->state) {
1191: case SMTP_SERVERGREET:
1192: result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1193: break;
1194:
1195: case SMTP_EHLO:
1196: result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1197: break;
1198:
1199: case SMTP_HELO:
1200: result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1201: break;
1202:
1203: case SMTP_STARTTLS:
1204: result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1205: break;
1206:
1207: case SMTP_AUTH:
1208: result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1209: break;
1210:
1211: case SMTP_COMMAND:
1212: result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1213: break;
1214:
1215: case SMTP_MAIL:
1216: result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1217: break;
1218:
1219: case SMTP_RCPT:
1220: result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1221: break;
1222:
1223: case SMTP_DATA:
1224: result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1225: break;
1226:
1227: case SMTP_POSTDATA:
1228: result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1229: break;
1230:
1231: case SMTP_QUIT:
1232: /* fallthrough, just stop! */
1233: default:
1234: /* internal error */
1235: state(conn, SMTP_STOP);
1236: break;
1237: }
1238: } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1239:
1240: return result;
1241: }
1242:
1243: /* Called repeatedly until done from multi.c */
1244: static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1245: {
1246: CURLcode result = CURLE_OK;
1247: struct smtp_conn *smtpc = &conn->proto.smtpc;
1248:
1249: if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1250: result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1251: if(result || !smtpc->ssldone)
1252: return result;
1253: }
1254:
1255: result = Curl_pp_statemach(&smtpc->pp, FALSE, FALSE);
1256: *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1257:
1258: return result;
1259: }
1260:
1261: static CURLcode smtp_block_statemach(struct connectdata *conn,
1262: bool disconnecting)
1263: {
1264: CURLcode result = CURLE_OK;
1265: struct smtp_conn *smtpc = &conn->proto.smtpc;
1266:
1267: while(smtpc->state != SMTP_STOP && !result)
1268: result = Curl_pp_statemach(&smtpc->pp, TRUE, disconnecting);
1269:
1270: return result;
1271: }
1272:
1273: /* Allocate and initialize the SMTP struct for the current Curl_easy if
1274: required */
1275: static CURLcode smtp_init(struct connectdata *conn)
1276: {
1277: CURLcode result = CURLE_OK;
1278: struct Curl_easy *data = conn->data;
1279: struct SMTP *smtp;
1280:
1281: smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1282: if(!smtp)
1283: result = CURLE_OUT_OF_MEMORY;
1284:
1285: return result;
1286: }
1287:
1288: /* For the SMTP "protocol connect" and "doing" phases only */
1289: static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks)
1290: {
1291: return Curl_pp_getsock(&conn->proto.smtpc.pp, socks);
1292: }
1293:
1294: /***********************************************************************
1295: *
1296: * smtp_connect()
1297: *
1298: * This function should do everything that is to be considered a part of
1299: * the connection phase.
1300: *
1301: * The variable pointed to by 'done' will be TRUE if the protocol-layer
1302: * connect phase is done when this function returns, or FALSE if not.
1303: */
1304: static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1305: {
1306: CURLcode result = CURLE_OK;
1307: struct smtp_conn *smtpc = &conn->proto.smtpc;
1308: struct pingpong *pp = &smtpc->pp;
1309:
1310: *done = FALSE; /* default to not done yet */
1311:
1312: /* We always support persistent connections in SMTP */
1313: connkeep(conn, "SMTP default");
1314:
1315: /* Set the default response time-out */
1316: pp->response_time = RESP_TIMEOUT;
1317: pp->statemach_act = smtp_statemach_act;
1318: pp->endofresp = smtp_endofresp;
1319: pp->conn = conn;
1320:
1321: /* Initialize the SASL storage */
1322: Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1323:
1324: /* Initialise the pingpong layer */
1325: Curl_pp_init(pp);
1326:
1327: /* Parse the URL options */
1328: result = smtp_parse_url_options(conn);
1329: if(result)
1330: return result;
1331:
1332: /* Parse the URL path */
1333: result = smtp_parse_url_path(conn);
1334: if(result)
1335: return result;
1336:
1337: /* Start off waiting for the server greeting response */
1338: state(conn, SMTP_SERVERGREET);
1339:
1340: result = smtp_multi_statemach(conn, done);
1341:
1342: return result;
1343: }
1344:
1345: /***********************************************************************
1346: *
1347: * smtp_done()
1348: *
1349: * The DONE function. This does what needs to be done after a single DO has
1350: * performed.
1351: *
1352: * Input argument is already checked for validity.
1353: */
1354: static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1355: bool premature)
1356: {
1357: CURLcode result = CURLE_OK;
1358: struct Curl_easy *data = conn->data;
1359: struct SMTP *smtp = data->req.protop;
1360: struct pingpong *pp = &conn->proto.smtpc.pp;
1361: char *eob;
1362: ssize_t len;
1363: ssize_t bytes_written;
1364:
1365: (void)premature;
1366:
1367: if(!smtp || !pp->conn)
1368: return CURLE_OK;
1369:
1370: /* Cleanup our per-request based variables */
1371: Curl_safefree(smtp->custom);
1372:
1373: if(status) {
1374: connclose(conn, "SMTP done with bad status"); /* marked for closure */
1375: result = status; /* use the already set error code */
1376: }
1377: else if(!data->set.connect_only && data->set.mail_rcpt &&
1378: (data->set.upload || data->set.mimepost.kind)) {
1379: /* Calculate the EOB taking into account any terminating CRLF from the
1380: previous line of the email or the CRLF of the DATA command when there
1381: is "no mail data". RFC-5321, sect. 4.1.1.4.
1382:
1383: Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1384: fail when using a different pointer following a previous write, that
1385: returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1386: bytes written doesn't equal len. */
1387: if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1388: eob = strdup(&SMTP_EOB[2]);
1389: len = SMTP_EOB_LEN - 2;
1390: }
1391: else {
1392: eob = strdup(SMTP_EOB);
1393: len = SMTP_EOB_LEN;
1394: }
1395:
1396: if(!eob)
1397: return CURLE_OUT_OF_MEMORY;
1398:
1399: /* Send the end of block data */
1400: result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1401: if(result) {
1402: free(eob);
1403: return result;
1404: }
1405:
1406: if(bytes_written != len) {
1407: /* The whole chunk was not sent so keep it around and adjust the
1408: pingpong structure accordingly */
1409: pp->sendthis = eob;
1410: pp->sendsize = len;
1411: pp->sendleft = len - bytes_written;
1412: }
1413: else {
1414: /* Successfully sent so adjust the response timeout relative to now */
1415: pp->response = Curl_now();
1416:
1417: free(eob);
1418: }
1419:
1420: state(conn, SMTP_POSTDATA);
1421:
1422: /* Run the state-machine */
1423: result = smtp_block_statemach(conn, FALSE);
1424: }
1425:
1426: /* Clear the transfer mode for the next request */
1427: smtp->transfer = FTPTRANSFER_BODY;
1428:
1429: return result;
1430: }
1431:
1432: /***********************************************************************
1433: *
1434: * smtp_perform()
1435: *
1436: * This is the actual DO function for SMTP. Transfer a mail, send a command
1437: * or get some data according to the options previously setup.
1438: */
1439: static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1440: bool *dophase_done)
1441: {
1442: /* This is SMTP and no proxy */
1443: CURLcode result = CURLE_OK;
1444: struct Curl_easy *data = conn->data;
1445: struct SMTP *smtp = data->req.protop;
1446:
1447: DEBUGF(infof(conn->data, "DO phase starts\n"));
1448:
1449: if(data->set.opt_no_body) {
1450: /* Requested no body means no transfer */
1451: smtp->transfer = FTPTRANSFER_INFO;
1452: }
1453:
1454: *dophase_done = FALSE; /* not done yet */
1455:
1456: /* Store the first recipient (or NULL if not specified) */
1457: smtp->rcpt = data->set.mail_rcpt;
1458:
1459: /* Track of whether we've successfully sent at least one RCPT TO command */
1460: smtp->rcpt_had_ok = FALSE;
1461:
1462: /* Track of the last error we've received by sending RCPT TO command */
1463: smtp->rcpt_last_error = 0;
1464:
1465: /* Initial data character is the first character in line: it is implicitly
1466: preceded by a virtual CRLF. */
1467: smtp->trailing_crlf = TRUE;
1468: smtp->eob = 2;
1469:
1470: /* Start the first command in the DO phase */
1471: if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1472: /* MAIL transfer */
1473: result = smtp_perform_mail(conn);
1474: else
1475: /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1476: result = smtp_perform_command(conn);
1477:
1478: if(result)
1479: return result;
1480:
1481: /* Run the state-machine */
1482: result = smtp_multi_statemach(conn, dophase_done);
1483:
1484: *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1485:
1486: if(*dophase_done)
1487: DEBUGF(infof(conn->data, "DO phase is complete\n"));
1488:
1489: return result;
1490: }
1491:
1492: /***********************************************************************
1493: *
1494: * smtp_do()
1495: *
1496: * This function is registered as 'curl_do' function. It decodes the path
1497: * parts etc as a wrapper to the actual DO function (smtp_perform).
1498: *
1499: * The input argument is already checked for validity.
1500: */
1501: static CURLcode smtp_do(struct connectdata *conn, bool *done)
1502: {
1503: CURLcode result = CURLE_OK;
1504:
1505: *done = FALSE; /* default to false */
1506:
1507: /* Parse the custom request */
1508: result = smtp_parse_custom_request(conn);
1509: if(result)
1510: return result;
1511:
1512: result = smtp_regular_transfer(conn, done);
1513:
1514: return result;
1515: }
1516:
1517: /***********************************************************************
1518: *
1519: * smtp_disconnect()
1520: *
1521: * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1522: * resources. BLOCKING.
1523: */
1524: static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1525: {
1526: struct smtp_conn *smtpc = &conn->proto.smtpc;
1527:
1528: /* We cannot send quit unconditionally. If this connection is stale or
1529: bad in any way, sending quit and waiting around here will make the
1530: disconnect wait in vain and cause more problems than we need to. */
1531:
1532: /* The SMTP session may or may not have been allocated/setup at this
1533: point! */
1534: if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1535: if(!smtp_perform_quit(conn))
1536: (void)smtp_block_statemach(conn, TRUE); /* ignore errors on QUIT */
1537:
1538: /* Disconnect from the server */
1539: Curl_pp_disconnect(&smtpc->pp);
1540:
1541: /* Cleanup the SASL module */
1542: Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1543:
1544: /* Cleanup our connection based variables */
1545: Curl_safefree(smtpc->domain);
1546:
1547: return CURLE_OK;
1548: }
1549:
1550: /* Call this when the DO phase has completed */
1551: static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1552: {
1553: struct SMTP *smtp = conn->data->req.protop;
1554:
1555: (void)connected;
1556:
1557: if(smtp->transfer != FTPTRANSFER_BODY)
1558: /* no data to transfer */
1559: Curl_setup_transfer(conn->data, -1, -1, FALSE, -1);
1560:
1561: return CURLE_OK;
1562: }
1563:
1564: /* Called from multi.c while DOing */
1565: static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1566: {
1567: CURLcode result = smtp_multi_statemach(conn, dophase_done);
1568:
1569: if(result)
1570: DEBUGF(infof(conn->data, "DO phase failed\n"));
1571: else if(*dophase_done) {
1572: result = smtp_dophase_done(conn, FALSE /* not connected */);
1573:
1574: DEBUGF(infof(conn->data, "DO phase is complete\n"));
1575: }
1576:
1577: return result;
1578: }
1579:
1580: /***********************************************************************
1581: *
1582: * smtp_regular_transfer()
1583: *
1584: * The input argument is already checked for validity.
1585: *
1586: * Performs all commands done before a regular transfer between a local and a
1587: * remote host.
1588: */
1589: static CURLcode smtp_regular_transfer(struct connectdata *conn,
1590: bool *dophase_done)
1591: {
1592: CURLcode result = CURLE_OK;
1593: bool connected = FALSE;
1594: struct Curl_easy *data = conn->data;
1595:
1596: /* Make sure size is unknown at this point */
1597: data->req.size = -1;
1598:
1599: /* Set the progress data */
1600: Curl_pgrsSetUploadCounter(data, 0);
1601: Curl_pgrsSetDownloadCounter(data, 0);
1602: Curl_pgrsSetUploadSize(data, -1);
1603: Curl_pgrsSetDownloadSize(data, -1);
1604:
1605: /* Carry out the perform */
1606: result = smtp_perform(conn, &connected, dophase_done);
1607:
1608: /* Perform post DO phase operations if necessary */
1609: if(!result && *dophase_done)
1610: result = smtp_dophase_done(conn, connected);
1611:
1612: return result;
1613: }
1614:
1615: static CURLcode smtp_setup_connection(struct connectdata *conn)
1616: {
1617: CURLcode result;
1618:
1619: /* Clear the TLS upgraded flag */
1620: conn->tls_upgraded = FALSE;
1621:
1622: /* Initialise the SMTP layer */
1623: result = smtp_init(conn);
1624: if(result)
1625: return result;
1626:
1627: return CURLE_OK;
1628: }
1629:
1630: /***********************************************************************
1631: *
1632: * smtp_parse_url_options()
1633: *
1634: * Parse the URL login options.
1635: */
1636: static CURLcode smtp_parse_url_options(struct connectdata *conn)
1637: {
1638: CURLcode result = CURLE_OK;
1639: struct smtp_conn *smtpc = &conn->proto.smtpc;
1640: const char *ptr = conn->options;
1641:
1642: smtpc->sasl.resetprefs = TRUE;
1643:
1644: while(!result && ptr && *ptr) {
1645: const char *key = ptr;
1646: const char *value;
1647:
1648: while(*ptr && *ptr != '=')
1649: ptr++;
1650:
1651: value = ptr + 1;
1652:
1653: while(*ptr && *ptr != ';')
1654: ptr++;
1655:
1656: if(strncasecompare(key, "AUTH=", 5))
1657: result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1658: value, ptr - value);
1659: else
1660: result = CURLE_URL_MALFORMAT;
1661:
1662: if(*ptr == ';')
1663: ptr++;
1664: }
1665:
1666: return result;
1667: }
1668:
1669: /***********************************************************************
1670: *
1671: * smtp_parse_url_path()
1672: *
1673: * Parse the URL path into separate path components.
1674: */
1675: static CURLcode smtp_parse_url_path(struct connectdata *conn)
1676: {
1677: /* The SMTP struct is already initialised in smtp_connect() */
1678: struct Curl_easy *data = conn->data;
1679: struct smtp_conn *smtpc = &conn->proto.smtpc;
1680: const char *path = &data->state.up.path[1]; /* skip leading path */
1681: char localhost[HOSTNAME_MAX + 1];
1682:
1683: /* Calculate the path if necessary */
1684: if(!*path) {
1685: if(!Curl_gethostname(localhost, sizeof(localhost)))
1686: path = localhost;
1687: else
1688: path = "localhost";
1689: }
1690:
1691: /* URL decode the path and use it as the domain in our EHLO */
1692: return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1693: }
1694:
1695: /***********************************************************************
1696: *
1697: * smtp_parse_custom_request()
1698: *
1699: * Parse the custom request.
1700: */
1701: static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1702: {
1703: CURLcode result = CURLE_OK;
1704: struct Curl_easy *data = conn->data;
1705: struct SMTP *smtp = data->req.protop;
1706: const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1707:
1708: /* URL decode the custom request */
1709: if(custom)
1710: result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1711:
1712: return result;
1713: }
1714:
1715: /***********************************************************************
1716: *
1717: * smtp_parse_address()
1718: *
1719: * Parse the fully qualified mailbox address into a local address part and the
1720: * host name, converting the host name to an IDN A-label, as per RFC-5890, if
1721: * necessary.
1722: *
1723: * Parameters:
1724: *
1725: * conn [in] - The connection handle.
1726: * fqma [in] - The fully qualified mailbox address (which may or
1727: * may not contain UTF-8 characters).
1728: * address [in/out] - A new allocated buffer which holds the local
1729: * address part of the mailbox. This buffer must be
1730: * free'ed by the caller.
1731: * host [in/out] - The host name structure that holds the original,
1732: * and optionally encoded, host name.
1733: * Curl_free_idnconverted_hostname() must be called
1734: * once the caller has finished with the structure.
1735: *
1736: * Returns CURLE_OK on success.
1737: *
1738: * Notes:
1739: *
1740: * Should a UTF-8 host name require conversion to IDN ACE and we cannot honor
1741: * that conversion then we shall return success. This allow the caller to send
1742: * the data to the server as a U-label (as per RFC-6531 sect. 3.2).
1743: *
1744: * If an mailbox '@' separator cannot be located then the mailbox is considered
1745: * to be either a local mailbox or an invalid mailbox (depending on what the
1746: * calling function deems it to be) then the input will simply be returned in
1747: * the address part with the host name being NULL.
1748: */
1749: static CURLcode smtp_parse_address(struct connectdata *conn, const char *fqma,
1750: char **address, struct hostname *host)
1751: {
1752: CURLcode result = CURLE_OK;
1753: size_t length;
1754:
1755: /* Duplicate the fully qualified email address so we can manipulate it,
1756: ensuring it doesn't contain the delimiters if specified */
1757: char *dup = strdup(fqma[0] == '<' ? fqma + 1 : fqma);
1758: if(!dup)
1759: return CURLE_OUT_OF_MEMORY;
1760:
1761: length = strlen(dup);
1762: if(dup[length - 1] == '>')
1763: dup[length - 1] = '\0';
1764:
1765: /* Extract the host name from the address (if we can) */
1766: host->name = strpbrk(dup, "@");
1767: if(host->name) {
1768: *host->name = '\0';
1769: host->name = host->name + 1;
1770:
1771: /* Attempt to convert the host name to IDN ACE */
1772: (void) Curl_idnconvert_hostname(conn, host);
1773:
1774: /* If Curl_idnconvert_hostname() fails then we shall attempt to continue
1775: and send the host name using UTF-8 rather than as 7-bit ACE (which is
1776: our preference) */
1777: }
1778:
1779: /* Extract the local address from the mailbox */
1780: *address = dup;
1781:
1782: return result;
1783: }
1784:
1785: CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1786: {
1787: /* When sending a SMTP payload we must detect CRLF. sequences making sure
1788: they are sent as CRLF.. instead, as a . on the beginning of a line will
1789: be deleted by the server when not part of an EOB terminator and a
1790: genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1791: data by the server
1792: */
1793: ssize_t i;
1794: ssize_t si;
1795: struct Curl_easy *data = conn->data;
1796: struct SMTP *smtp = data->req.protop;
1797: char *scratch = data->state.scratch;
1798: char *newscratch = NULL;
1799: char *oldscratch = NULL;
1800: size_t eob_sent;
1801:
1802: /* Do we need to allocate a scratch buffer? */
1803: if(!scratch || data->set.crlf) {
1804: oldscratch = scratch;
1805:
1806: scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
1807: if(!newscratch) {
1808: failf(data, "Failed to alloc scratch buffer!");
1809:
1810: return CURLE_OUT_OF_MEMORY;
1811: }
1812: }
1813: DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread);
1814:
1815: /* Have we already sent part of the EOB? */
1816: eob_sent = smtp->eob;
1817:
1818: /* This loop can be improved by some kind of Boyer-Moore style of
1819: approach but that is saved for later... */
1820: for(i = 0, si = 0; i < nread; i++) {
1821: if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1822: smtp->eob++;
1823:
1824: /* Is the EOB potentially the terminating CRLF? */
1825: if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1826: smtp->trailing_crlf = TRUE;
1827: else
1828: smtp->trailing_crlf = FALSE;
1829: }
1830: else if(smtp->eob) {
1831: /* A previous substring matched so output that first */
1832: memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1833: si += smtp->eob - eob_sent;
1834:
1835: /* Then compare the first byte */
1836: if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1837: smtp->eob = 1;
1838: else
1839: smtp->eob = 0;
1840:
1841: eob_sent = 0;
1842:
1843: /* Reset the trailing CRLF flag as there was more data */
1844: smtp->trailing_crlf = FALSE;
1845: }
1846:
1847: /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1848: if(SMTP_EOB_FIND_LEN == smtp->eob) {
1849: /* Copy the replacement data to the target buffer */
1850: memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1851: SMTP_EOB_REPL_LEN - eob_sent);
1852: si += SMTP_EOB_REPL_LEN - eob_sent;
1853: smtp->eob = 0;
1854: eob_sent = 0;
1855: }
1856: else if(!smtp->eob)
1857: scratch[si++] = data->req.upload_fromhere[i];
1858: }
1859:
1860: if(smtp->eob - eob_sent) {
1861: /* A substring matched before processing ended so output that now */
1862: memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1863: si += smtp->eob - eob_sent;
1864: }
1865:
1866: /* Only use the new buffer if we replaced something */
1867: if(si != nread) {
1868: /* Upload from the new (replaced) buffer instead */
1869: data->req.upload_fromhere = scratch;
1870:
1871: /* Save the buffer so it can be freed later */
1872: data->state.scratch = scratch;
1873:
1874: /* Free the old scratch buffer */
1875: free(oldscratch);
1876:
1877: /* Set the new amount too */
1878: data->req.upload_present = si;
1879: }
1880: else
1881: free(newscratch);
1882:
1883: return CURLE_OK;
1884: }
1885:
1886: #endif /* CURL_DISABLE_SMTP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>