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