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