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