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