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