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