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