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