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