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