1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2014, 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 http://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 * RFC4954 SMTP Authentication
28 * RFC5321 SMTP protocol
29 * RFC6749 OAuth 2.0 Authorization Framework
30 * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
31 * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32 *
33 ***************************************************************************/
34
35#include "curl_setup.h"
36
37#ifndef CURL_DISABLE_SMTP
38
39#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_UTSNAME_H
46#include <sys/utsname.h>
47#endif
48#ifdef HAVE_NETDB_H
49#include <netdb.h>
50#endif
51#ifdef __VMS
52#include <in.h>
53#include <inet.h>
54#endif
55
56#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57#undef in_addr_t
58#define in_addr_t unsigned long
59#endif
60
61#include <curl/curl.h>
62#include "urldata.h"
63#include "sendf.h"
64#include "if2ip.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
86#define _MPRINTF_REPLACE /* use our functions only */
87#include <curl/mprintf.h>
88
89#include "curl_memory.h"
90/* The last #include file should be: */
91#include "memdebug.h"
92
93/* Local API functions */
94static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95static CURLcode smtp_do(struct connectdata *conn, bool *done);
96static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
97                          bool premature);
98static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
102                        int numsocks);
103static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
104static CURLcode smtp_setup_connection(struct connectdata *conn);
105static CURLcode smtp_parse_url_options(struct connectdata *conn);
106static CURLcode smtp_parse_url_path(struct connectdata *conn);
107static CURLcode smtp_parse_custom_request(struct connectdata *conn);
108static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
109                                       const char **mech,
110                                       char **initresp, size_t *len,
111                                       smtpstate *state1, smtpstate *state2);
112
113/*
114 * SMTP protocol handler.
115 */
116
117const struct Curl_handler Curl_handler_smtp = {
118  "SMTP",                           /* scheme */
119  smtp_setup_connection,            /* setup_connection */
120  smtp_do,                          /* do_it */
121  smtp_done,                        /* done */
122  ZERO_NULL,                        /* do_more */
123  smtp_connect,                     /* connect_it */
124  smtp_multi_statemach,             /* connecting */
125  smtp_doing,                       /* doing */
126  smtp_getsock,                     /* proto_getsock */
127  smtp_getsock,                     /* doing_getsock */
128  ZERO_NULL,                        /* domore_getsock */
129  ZERO_NULL,                        /* perform_getsock */
130  smtp_disconnect,                  /* disconnect */
131  ZERO_NULL,                        /* readwrite */
132  PORT_SMTP,                        /* defport */
133  CURLPROTO_SMTP,                   /* protocol */
134  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
135};
136
137#ifdef USE_SSL
138/*
139 * SMTPS protocol handler.
140 */
141
142const struct Curl_handler Curl_handler_smtps = {
143  "SMTPS",                          /* scheme */
144  smtp_setup_connection,            /* setup_connection */
145  smtp_do,                          /* do_it */
146  smtp_done,                        /* done */
147  ZERO_NULL,                        /* do_more */
148  smtp_connect,                     /* connect_it */
149  smtp_multi_statemach,             /* connecting */
150  smtp_doing,                       /* doing */
151  smtp_getsock,                     /* proto_getsock */
152  smtp_getsock,                     /* doing_getsock */
153  ZERO_NULL,                        /* domore_getsock */
154  ZERO_NULL,                        /* perform_getsock */
155  smtp_disconnect,                  /* disconnect */
156  ZERO_NULL,                        /* readwrite */
157  PORT_SMTPS,                       /* defport */
158  CURLPROTO_SMTPS,                  /* protocol */
159  PROTOPT_CLOSEACTION | PROTOPT_SSL
160  | PROTOPT_NOURLQUERY              /* flags */
161};
162#endif
163
164#ifndef CURL_DISABLE_HTTP
165/*
166 * HTTP-proxyed SMTP protocol handler.
167 */
168
169static const struct Curl_handler Curl_handler_smtp_proxy = {
170  "SMTP",                               /* scheme */
171  Curl_http_setup_conn,                 /* setup_connection */
172  Curl_http,                            /* do_it */
173  Curl_http_done,                       /* done */
174  ZERO_NULL,                            /* do_more */
175  ZERO_NULL,                            /* connect_it */
176  ZERO_NULL,                            /* connecting */
177  ZERO_NULL,                            /* doing */
178  ZERO_NULL,                            /* proto_getsock */
179  ZERO_NULL,                            /* doing_getsock */
180  ZERO_NULL,                            /* domore_getsock */
181  ZERO_NULL,                            /* perform_getsock */
182  ZERO_NULL,                            /* disconnect */
183  ZERO_NULL,                            /* readwrite */
184  PORT_SMTP,                            /* defport */
185  CURLPROTO_HTTP,                       /* protocol */
186  PROTOPT_NONE                          /* flags */
187};
188
189#ifdef USE_SSL
190/*
191 * HTTP-proxyed SMTPS protocol handler.
192 */
193
194static const struct Curl_handler Curl_handler_smtps_proxy = {
195  "SMTPS",                              /* scheme */
196  Curl_http_setup_conn,                 /* setup_connection */
197  Curl_http,                            /* do_it */
198  Curl_http_done,                       /* done */
199  ZERO_NULL,                            /* do_more */
200  ZERO_NULL,                            /* connect_it */
201  ZERO_NULL,                            /* connecting */
202  ZERO_NULL,                            /* doing */
203  ZERO_NULL,                            /* proto_getsock */
204  ZERO_NULL,                            /* doing_getsock */
205  ZERO_NULL,                            /* domore_getsock */
206  ZERO_NULL,                            /* perform_getsock */
207  ZERO_NULL,                            /* disconnect */
208  ZERO_NULL,                            /* readwrite */
209  PORT_SMTPS,                           /* defport */
210  CURLPROTO_HTTP,                       /* protocol */
211  PROTOPT_NONE                          /* flags */
212};
213#endif
214#endif
215
216#ifdef USE_SSL
217static void smtp_to_smtps(struct connectdata *conn)
218{
219  conn->handler = &Curl_handler_smtps;
220}
221#else
222#define smtp_to_smtps(x) Curl_nop_stmt
223#endif
224
225/***********************************************************************
226 *
227 * smtp_endofresp()
228 *
229 * Checks for an ending SMTP status code at the start of the given string, but
230 * also detects various capabilities from the EHLO response including the
231 * supported authentication mechanisms.
232 */
233static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
234                           int *resp)
235{
236  struct smtp_conn *smtpc = &conn->proto.smtpc;
237  bool result = FALSE;
238
239  /* Nothing for us */
240  if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
241    return FALSE;
242
243  /* Do we have a command response? This should be the response code followed
244     by a space and optionally some text as per RFC-5321 and as outlined in
245     Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
246     only send the response code instead as per Section 4.2. */
247  if(line[3] == ' ' || len == 5) {
248    result = TRUE;
249    *resp = curlx_sltosi(strtol(line, NULL, 10));
250
251    /* Make sure real server never sends internal value */
252    if(*resp == 1)
253      *resp = 0;
254  }
255  /* Do we have a multiline (continuation) response? */
256  else if(line[3] == '-' &&
257          (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
258    result = TRUE;
259    *resp = 1;  /* Internal response code */
260  }
261
262  return result;
263}
264
265/***********************************************************************
266 *
267 * smtp_get_message()
268 *
269 * Gets the authentication message from the response buffer.
270 */
271static void smtp_get_message(char *buffer, char** outptr)
272{
273  size_t len = 0;
274  char* message = NULL;
275
276  /* Find the start of the message */
277  for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
278    ;
279
280  /* Find the end of the message */
281  for(len = strlen(message); len--;)
282    if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
283        message[len] != '\t')
284      break;
285
286  /* Terminate the message */
287  if(++len) {
288    message[len] = '\0';
289  }
290
291  *outptr = message;
292}
293
294/***********************************************************************
295 *
296 * state()
297 *
298 * This is the ONLY way to change SMTP state!
299 */
300static void state(struct connectdata *conn, smtpstate newstate)
301{
302  struct smtp_conn *smtpc = &conn->proto.smtpc;
303#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
304  /* for debug purposes */
305  static const char * const names[] = {
306    "STOP",
307    "SERVERGREET",
308    "EHLO",
309    "HELO",
310    "STARTTLS",
311    "UPGRADETLS",
312    "AUTH_PLAIN",
313    "AUTH_LOGIN",
314    "AUTH_LOGIN_PASSWD",
315    "AUTH_CRAMMD5",
316    "AUTH_DIGESTMD5",
317    "AUTH_DIGESTMD5_RESP",
318    "AUTH_NTLM",
319    "AUTH_NTLM_TYPE2MSG",
320    "AUTH_XOAUTH2",
321    "AUTH_CANCEL",
322    "AUTH_FINAL",
323    "COMMAND",
324    "MAIL",
325    "RCPT",
326    "DATA",
327    "POSTDATA",
328    "QUIT",
329    /* LAST */
330  };
331
332  if(smtpc->state != newstate)
333    infof(conn->data, "SMTP %p state change from %s to %s\n",
334          (void *)smtpc, names[smtpc->state], names[newstate]);
335#endif
336
337  smtpc->state = newstate;
338}
339
340/***********************************************************************
341 *
342 * smtp_perform_ehlo()
343 *
344 * Sends the EHLO command to not only initialise communication with the ESMTP
345 * server but to also obtain a list of server side supported capabilities.
346 */
347static CURLcode smtp_perform_ehlo(struct connectdata *conn)
348{
349  CURLcode result = CURLE_OK;
350  struct smtp_conn *smtpc = &conn->proto.smtpc;
351
352  smtpc->authmechs = 0;           /* No known authentication mechanisms yet */
353  smtpc->authused = 0;            /* Clear the authentication mechanism used
354                                     for esmtp connections */
355  smtpc->tls_supported = FALSE;   /* Clear the TLS capability */
356  smtpc->auth_supported = FALSE;  /* Clear the AUTH capability */
357
358  /* Send the EHLO command */
359  result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
360
361  if(!result)
362    state(conn, SMTP_EHLO);
363
364  return result;
365}
366
367/***********************************************************************
368 *
369 * smtp_perform_helo()
370 *
371 * Sends the HELO command to initialise communication with the SMTP server.
372 */
373static CURLcode smtp_perform_helo(struct connectdata *conn)
374{
375  CURLcode result = CURLE_OK;
376  struct smtp_conn *smtpc = &conn->proto.smtpc;
377
378  smtpc->authused = 0;          /* No authentication mechanism used in smtp
379                                   connections */
380
381  /* Send the HELO command */
382  result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
383
384  if(!result)
385    state(conn, SMTP_HELO);
386
387  return result;
388}
389
390/***********************************************************************
391 *
392 * smtp_perform_starttls()
393 *
394 * Sends the STLS command to start the upgrade to TLS.
395 */
396static CURLcode smtp_perform_starttls(struct connectdata *conn)
397{
398  CURLcode result = CURLE_OK;
399
400  /* Send the STARTTLS command */
401  result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
402
403  if(!result)
404    state(conn, SMTP_STARTTLS);
405
406  return result;
407}
408
409/***********************************************************************
410 *
411 * smtp_perform_upgrade_tls()
412 *
413 * Performs the upgrade to TLS.
414 */
415static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
416{
417  CURLcode result = CURLE_OK;
418  struct smtp_conn *smtpc = &conn->proto.smtpc;
419
420  /* Start the SSL connection */
421  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
422
423  if(!result) {
424    if(smtpc->state != SMTP_UPGRADETLS)
425      state(conn, SMTP_UPGRADETLS);
426
427    if(smtpc->ssldone) {
428      smtp_to_smtps(conn);
429      result = smtp_perform_ehlo(conn);
430    }
431  }
432
433  return result;
434}
435
436/***********************************************************************
437 *
438 * smtp_perform_auth()
439 *
440 * Sends an AUTH command allowing the client to login with the given SASL
441 * authentication mechanism.
442 */
443static CURLcode smtp_perform_auth(struct connectdata *conn,
444                                  const char *mech,
445                                  const char *initresp, size_t len,
446                                  smtpstate state1, smtpstate state2)
447{
448  CURLcode result = CURLE_OK;
449  struct smtp_conn *smtpc = &conn->proto.smtpc;
450
451  if(initresp && 8 + strlen(mech) + len <= 512) { /* 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    if(!result)
456      state(conn, state2);
457  }
458  else {
459    /* Send the AUTH command */
460    result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
461
462    if(!result)
463      state(conn, state1);
464  }
465
466  return result;
467}
468
469/***********************************************************************
470 *
471 * smtp_perform_authentication()
472 *
473 * Initiates the authentication sequence, with the appropriate SASL
474 * authentication mechanism.
475 */
476static CURLcode smtp_perform_authentication(struct connectdata *conn)
477{
478  CURLcode result = CURLE_OK;
479  struct smtp_conn *smtpc = &conn->proto.smtpc;
480  const char *mech = NULL;
481  char *initresp = NULL;
482  size_t len = 0;
483  smtpstate state1 = SMTP_STOP;
484  smtpstate state2 = SMTP_STOP;
485
486  /* Check we have a username and password to authenticate with, and the
487     server supports authentiation, and end the connect phase if not */
488  if(!conn->bits.user_passwd || !smtpc->auth_supported) {
489    state(conn, SMTP_STOP);
490
491    return result;
492  }
493
494  /* Calculate the SASL login details */
495  result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
496                                  &state2);
497
498  if(!result) {
499    if(mech) {
500      /* Perform SASL based authentication */
501      result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
502
503      Curl_safefree(initresp);
504    }
505    else {
506      /* Other mechanisms not supported */
507      infof(conn->data, "No known authentication mechanisms supported!\n");
508      result = CURLE_LOGIN_DENIED;
509    }
510  }
511
512  return result;
513}
514
515/***********************************************************************
516 *
517 * smtp_perform_command()
518 *
519 * Sends a SMTP based command.
520 */
521static CURLcode smtp_perform_command(struct connectdata *conn)
522{
523  CURLcode result = CURLE_OK;
524  struct SessionHandle *data = conn->data;
525  struct SMTP *smtp = data->req.protop;
526
527  /* Send the command */
528  if(smtp->rcpt)
529    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
530                            smtp->custom && smtp->custom[0] != '\0' ?
531                            smtp->custom : "VRFY",
532                            smtp->rcpt->data);
533  else
534    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
535                           smtp->custom && smtp->custom[0] != '\0' ?
536                           smtp->custom : "HELP");
537
538  if(!result)
539    state(conn, SMTP_COMMAND);
540
541  return result;
542}
543
544/***********************************************************************
545 *
546 * smtp_perform_mail()
547 *
548 * Sends an MAIL command to initiate the upload of a message.
549 */
550static CURLcode smtp_perform_mail(struct connectdata *conn)
551{
552  char *from = NULL;
553  char *auth = NULL;
554  char *size = NULL;
555  CURLcode result = CURLE_OK;
556  struct SessionHandle *data = conn->data;
557
558  /* Calculate the FROM parameter */
559  if(!data->set.str[STRING_MAIL_FROM])
560    /* Null reverse-path, RFC-5321, sect. 3.6.3 */
561    from = strdup("<>");
562  else if(data->set.str[STRING_MAIL_FROM][0] == '<')
563    from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
564  else
565    from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
566
567  if(!from)
568    return CURLE_OUT_OF_MEMORY;
569
570  /* Calculate the optional AUTH parameter */
571  if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
572    if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
573      auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
574    else
575      /* Empty AUTH, RFC-2554, sect. 5 */
576      auth = strdup("<>");
577
578    if(!auth) {
579      Curl_safefree(from);
580
581      return CURLE_OUT_OF_MEMORY;
582    }
583  }
584
585  /* Calculate the optional SIZE parameter */
586  if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
587    size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
588
589    if(!size) {
590      Curl_safefree(from);
591      Curl_safefree(auth);
592
593      return CURLE_OUT_OF_MEMORY;
594    }
595  }
596
597  /* Send the MAIL command */
598  if(!auth && !size)
599    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
600                           "MAIL FROM:%s", from);
601  else if(auth && !size)
602    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
603                           "MAIL FROM:%s AUTH=%s", from, auth);
604  else if(auth && size)
605    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
606                           "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
607  else
608    result = Curl_pp_sendf(&conn->proto.smtpc.pp,
609                           "MAIL FROM:%s SIZE=%s", from, size);
610
611  Curl_safefree(from);
612  Curl_safefree(auth);
613  Curl_safefree(size);
614
615  if(!result)
616    state(conn, SMTP_MAIL);
617
618  return result;
619}
620
621/***********************************************************************
622 *
623 * smtp_perform_rcpt_to()
624 *
625 * Sends a RCPT TO command for a given recipient as part of the message upload
626 * process.
627 */
628static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
629{
630  CURLcode result = CURLE_OK;
631  struct SessionHandle *data = conn->data;
632  struct SMTP *smtp = data->req.protop;
633
634  /* Send the RCPT TO command */
635  if(smtp->rcpt->data[0] == '<')
636    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
637                            smtp->rcpt->data);
638  else
639    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
640                            smtp->rcpt->data);
641  if(!result)
642    state(conn, SMTP_RCPT);
643
644  return result;
645}
646
647/***********************************************************************
648 *
649 * smtp_perform_quit()
650 *
651 * Performs the quit action prior to sclose() being called.
652 */
653static CURLcode smtp_perform_quit(struct connectdata *conn)
654{
655  CURLcode result = CURLE_OK;
656
657  /* Send the QUIT command */
658  result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
659
660  if(!result)
661    state(conn, SMTP_QUIT);
662
663  return result;
664}
665
666/* For the initial server greeting */
667static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
668                                            int smtpcode,
669                                            smtpstate instate)
670{
671  CURLcode result = CURLE_OK;
672  struct SessionHandle *data = conn->data;
673
674  (void)instate; /* no use for this yet */
675
676  if(smtpcode/100 != 2) {
677    failf(data, "Got unexpected smtp-server response: %d", smtpcode);
678    result = CURLE_FTP_WEIRD_SERVER_REPLY;
679  }
680  else
681    result = smtp_perform_ehlo(conn);
682
683  return result;
684}
685
686/* For STARTTLS responses */
687static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
688                                         int smtpcode,
689                                         smtpstate instate)
690{
691  CURLcode result = CURLE_OK;
692  struct SessionHandle *data = conn->data;
693
694  (void)instate; /* no use for this yet */
695
696  if(smtpcode != 220) {
697    if(data->set.use_ssl != CURLUSESSL_TRY) {
698      failf(data, "STARTTLS denied. %c", smtpcode);
699      result = CURLE_USE_SSL_FAILED;
700    }
701    else
702      result = smtp_perform_authentication(conn);
703  }
704  else
705    result = smtp_perform_upgrade_tls(conn);
706
707  return result;
708}
709
710/* For EHLO responses */
711static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
712                                     smtpstate instate)
713{
714  CURLcode result = CURLE_OK;
715  struct SessionHandle *data = conn->data;
716  struct smtp_conn *smtpc = &conn->proto.smtpc;
717  const char *line = data->state.buffer;
718  size_t len = strlen(line);
719  size_t wordlen;
720
721  (void)instate; /* no use for this yet */
722
723  if(smtpcode/100 != 2 && smtpcode != 1) {
724    if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
725      result = smtp_perform_helo(conn);
726    else {
727      failf(data, "Remote access denied: %d", smtpcode);
728      result = CURLE_REMOTE_ACCESS_DENIED;
729    }
730  }
731  else {
732    line += 4;
733    len -= 4;
734
735    /* Does the server support the STARTTLS capability? */
736    if(len >= 8 && !memcmp(line, "STARTTLS", 8))
737      smtpc->tls_supported = TRUE;
738
739    /* Does the server support the SIZE capability? */
740    else if(len >= 4 && !memcmp(line, "SIZE", 4))
741      smtpc->size_supported = TRUE;
742
743    /* Does the server support authentication? */
744    else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
745      smtpc->auth_supported = TRUE;
746
747      /* Advance past the AUTH keyword */
748      line += 5;
749      len -= 5;
750
751      /* Loop through the data line */
752      for(;;) {
753        while(len &&
754              (*line == ' ' || *line == '\t' ||
755               *line == '\r' || *line == '\n')) {
756
757          line++;
758          len--;
759        }
760
761        if(!len)
762          break;
763
764        /* Extract the word */
765        for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
766              line[wordlen] != '\t' && line[wordlen] != '\r' &&
767              line[wordlen] != '\n';)
768          wordlen++;
769
770        /* Test the word for a matching authentication mechanism */
771        if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_LOGIN))
772          smtpc->authmechs |= SASL_MECH_LOGIN;
773        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_PLAIN))
774          smtpc->authmechs |= SASL_MECH_PLAIN;
775        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_CRAM_MD5))
776          smtpc->authmechs |= SASL_MECH_CRAM_MD5;
777        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_DIGEST_MD5))
778          smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
779        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_GSSAPI))
780          smtpc->authmechs |= SASL_MECH_GSSAPI;
781        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_EXTERNAL))
782          smtpc->authmechs |= SASL_MECH_EXTERNAL;
783        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_NTLM))
784          smtpc->authmechs |= SASL_MECH_NTLM;
785        else if(sasl_mech_equal(line, wordlen, SASL_MECH_STRING_XOAUTH2))
786          smtpc->authmechs |= SASL_MECH_XOAUTH2;
787
788        line += wordlen;
789        len -= wordlen;
790      }
791    }
792
793    if(smtpcode != 1) {
794      if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
795        /* We don't have a SSL/TLS connection yet, but SSL is requested */
796        if(smtpc->tls_supported)
797          /* Switch to TLS connection now */
798          result = smtp_perform_starttls(conn);
799        else if(data->set.use_ssl == CURLUSESSL_TRY)
800          /* Fallback and carry on with authentication */
801          result = smtp_perform_authentication(conn);
802        else {
803          failf(data, "STARTTLS not supported.");
804          result = CURLE_USE_SSL_FAILED;
805        }
806      }
807      else
808        result = smtp_perform_authentication(conn);
809    }
810  }
811
812  return result;
813}
814
815/* For HELO responses */
816static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
817                                     smtpstate instate)
818{
819  CURLcode result = CURLE_OK;
820  struct SessionHandle *data = conn->data;
821
822  (void)instate; /* no use for this yet */
823
824  if(smtpcode/100 != 2) {
825    failf(data, "Remote access denied: %d", smtpcode);
826    result = CURLE_REMOTE_ACCESS_DENIED;
827  }
828  else
829    /* End of connect phase */
830    state(conn, SMTP_STOP);
831
832  return result;
833}
834
835/* For AUTH PLAIN (without initial response) responses */
836static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
837                                           int smtpcode,
838                                           smtpstate instate)
839{
840  CURLcode result = CURLE_OK;
841  struct SessionHandle *data = conn->data;
842  size_t len = 0;
843  char *plainauth = NULL;
844
845  (void)instate; /* no use for this yet */
846
847  if(smtpcode != 334) {
848    failf(data, "Access denied: %d", smtpcode);
849    result = CURLE_LOGIN_DENIED;
850  }
851  else {
852    /* Create the authorisation message */
853    result = Curl_sasl_create_plain_message(conn->data, conn->user,
854                                            conn->passwd, &plainauth, &len);
855    if(!result && plainauth) {
856      /* Send the message */
857      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
858
859      if(!result)
860        state(conn, SMTP_AUTH_FINAL);
861    }
862  }
863
864  Curl_safefree(plainauth);
865
866  return result;
867}
868
869/* For AUTH LOGIN (without initial response) responses */
870static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
871                                           int smtpcode,
872                                           smtpstate instate)
873{
874  CURLcode result = CURLE_OK;
875  struct SessionHandle *data = conn->data;
876  size_t len = 0;
877  char *authuser = NULL;
878
879  (void)instate; /* no use for this yet */
880
881  if(smtpcode != 334) {
882    failf(data, "Access denied: %d", smtpcode);
883    result = CURLE_LOGIN_DENIED;
884  }
885  else {
886    /* Create the user message */
887    result = Curl_sasl_create_login_message(conn->data, conn->user,
888                                            &authuser, &len);
889    if(!result && authuser) {
890      /* Send the user */
891      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
892
893      if(!result)
894        state(conn, SMTP_AUTH_LOGIN_PASSWD);
895    }
896  }
897
898  Curl_safefree(authuser);
899
900  return result;
901}
902
903/* For AUTH LOGIN user entry responses */
904static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
905                                                    int smtpcode,
906                                                    smtpstate instate)
907{
908  CURLcode result = CURLE_OK;
909  struct SessionHandle *data = conn->data;
910  size_t len = 0;
911  char *authpasswd = NULL;
912
913  (void)instate; /* no use for this yet */
914
915  if(smtpcode != 334) {
916    failf(data, "Access denied: %d", smtpcode);
917    result = CURLE_LOGIN_DENIED;
918  }
919  else {
920    /* Create the password message */
921    result = Curl_sasl_create_login_message(conn->data, conn->passwd,
922                                            &authpasswd, &len);
923    if(!result && authpasswd) {
924      /* Send the password */
925      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
926
927      if(!result)
928        state(conn, SMTP_AUTH_FINAL);
929    }
930  }
931
932  Curl_safefree(authpasswd);
933
934  return result;
935}
936
937#ifndef CURL_DISABLE_CRYPTO_AUTH
938/* For AUTH CRAM-MD5 responses */
939static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
940                                          int smtpcode,
941                                          smtpstate instate)
942{
943  CURLcode result = CURLE_OK;
944  struct SessionHandle *data = conn->data;
945  char *chlg = NULL;
946  char *chlg64 = NULL;
947  char *rplyb64 = NULL;
948  size_t len = 0;
949
950  (void)instate; /* no use for this yet */
951
952  if(smtpcode != 334) {
953    failf(data, "Access denied: %d", smtpcode);
954    return CURLE_LOGIN_DENIED;
955  }
956
957  /* Get the challenge message */
958  smtp_get_message(data->state.buffer, &chlg64);
959
960  /* Decode the challenge message */
961  result = Curl_sasl_decode_cram_md5_message(chlg64, &chlg, &len);
962  if(result) {
963    /* Send the cancellation */
964    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
965
966    if(!result)
967      state(conn, SMTP_AUTH_CANCEL);
968  }
969  else {
970    /* Create the response message */
971    result = Curl_sasl_create_cram_md5_message(data, chlg, conn->user,
972                                               conn->passwd, &rplyb64, &len);
973    if(!result && rplyb64) {
974      /* Send the response */
975      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
976
977      if(!result)
978        state(conn, SMTP_AUTH_FINAL);
979    }
980  }
981
982  Curl_safefree(chlg);
983  Curl_safefree(rplyb64);
984
985  return result;
986}
987
988/* For AUTH DIGEST-MD5 challenge responses */
989static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
990                                            int smtpcode,
991                                            smtpstate instate)
992{
993  CURLcode result = CURLE_OK;
994  struct SessionHandle *data = conn->data;
995  char *chlg64 = NULL;
996  char *rplyb64 = NULL;
997  size_t len = 0;
998
999  (void)instate; /* no use for this yet */
1000
1001  if(smtpcode != 334) {
1002    failf(data, "Access denied: %d", smtpcode);
1003    return CURLE_LOGIN_DENIED;
1004  }
1005
1006  /* Get the challenge message */
1007  smtp_get_message(data->state.buffer, &chlg64);
1008
1009  /* Create the response message */
1010  result = Curl_sasl_create_digest_md5_message(data, chlg64,
1011                                               conn->user, conn->passwd,
1012                                               "smtp", &rplyb64, &len);
1013  if(result) {
1014    if(result == CURLE_BAD_CONTENT_ENCODING) {
1015      /* Send the cancellation */
1016      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1017
1018      if(!result)
1019        state(conn, SMTP_AUTH_CANCEL);
1020    }
1021  }
1022  else {
1023    /* Send the response */
1024    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
1025
1026    if(!result)
1027      state(conn, SMTP_AUTH_DIGESTMD5_RESP);
1028  }
1029
1030  Curl_safefree(rplyb64);
1031
1032  return result;
1033}
1034
1035/* For AUTH DIGEST-MD5 challenge-response responses */
1036static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
1037                                                 int smtpcode,
1038                                                 smtpstate instate)
1039{
1040  CURLcode result = CURLE_OK;
1041  struct SessionHandle *data = conn->data;
1042
1043  (void)instate; /* no use for this yet */
1044
1045  if(smtpcode != 334) {
1046    failf(data, "Authentication failed: %d", smtpcode);
1047    result = CURLE_LOGIN_DENIED;
1048  }
1049  else {
1050    /* Send an empty response */
1051    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "");
1052
1053    if(!result)
1054      state(conn, SMTP_AUTH_FINAL);
1055  }
1056
1057  return result;
1058}
1059
1060#endif
1061
1062#ifdef USE_NTLM
1063/* For AUTH NTLM (without initial response) responses */
1064static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
1065                                          int smtpcode,
1066                                          smtpstate instate)
1067{
1068  CURLcode result = CURLE_OK;
1069  struct SessionHandle *data = conn->data;
1070  char *type1msg = NULL;
1071  size_t len = 0;
1072
1073  (void)instate; /* no use for this yet */
1074
1075  if(smtpcode != 334) {
1076    failf(data, "Access denied: %d", smtpcode);
1077    result = CURLE_LOGIN_DENIED;
1078  }
1079  else {
1080    /* Create the type-1 message */
1081    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
1082                                                 &conn->ntlm,
1083                                                 &type1msg, &len);
1084    if(!result && type1msg) {
1085      /* Send the message */
1086      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
1087
1088      if(!result)
1089        state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
1090    }
1091  }
1092
1093  Curl_safefree(type1msg);
1094
1095  return result;
1096}
1097
1098/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
1099static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
1100                                                   int smtpcode,
1101                                                   smtpstate instate)
1102{
1103  CURLcode result = CURLE_OK;
1104  struct SessionHandle *data = conn->data;
1105  char *type2msg = NULL;
1106  char *type3msg = NULL;
1107  size_t len = 0;
1108
1109  (void)instate; /* no use for this yet */
1110
1111  if(smtpcode != 334) {
1112    failf(data, "Access denied: %d", smtpcode);
1113    result = CURLE_LOGIN_DENIED;
1114  }
1115  else {
1116    /* Get the type-2 message */
1117    smtp_get_message(data->state.buffer, &type2msg);
1118
1119    /* Decode the type-2 message */
1120    result = Curl_sasl_decode_ntlm_type2_message(data, type2msg, &conn->ntlm);
1121    if(result) {
1122      /* Send the cancellation */
1123      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
1124
1125      if(!result)
1126        state(conn, SMTP_AUTH_CANCEL);
1127    }
1128    else {
1129      /* Create the type-3 message */
1130      result = Curl_sasl_create_ntlm_type3_message(data, conn->user,
1131                                                   conn->passwd, &conn->ntlm,
1132                                                   &type3msg, &len);
1133      if(!result && type3msg) {
1134        /* Send the message */
1135        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
1136
1137        if(!result)
1138          state(conn, SMTP_AUTH_FINAL);
1139      }
1140    }
1141  }
1142
1143  Curl_safefree(type3msg);
1144
1145  return result;
1146}
1147#endif
1148
1149/* For AUTH XOAUTH2 (without initial response) responses */
1150static CURLcode smtp_state_auth_xoauth2_resp(struct connectdata *conn,
1151                                             int smtpcode, smtpstate instate)
1152{
1153  CURLcode result = CURLE_OK;
1154  struct SessionHandle *data = conn->data;
1155  size_t len = 0;
1156  char *xoauth = NULL;
1157
1158  (void)instate; /* no use for this yet */
1159
1160  if(smtpcode != 334) {
1161    failf(data, "Access denied: %d", smtpcode);
1162    result = CURLE_LOGIN_DENIED;
1163  }
1164  else {
1165    /* Create the authorisation message */
1166    result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
1167                                              conn->xoauth2_bearer,
1168                                              &xoauth, &len);
1169    if(!result && xoauth) {
1170      /* Send the message */
1171      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", xoauth);
1172
1173      if(!result)
1174        state(conn, SMTP_AUTH_FINAL);
1175    }
1176  }
1177
1178  Curl_safefree(xoauth);
1179
1180  return result;
1181}
1182
1183/* For AUTH cancellation responses */
1184static CURLcode smtp_state_auth_cancel_resp(struct connectdata *conn,
1185                                            int smtpcode,
1186                                            smtpstate instate)
1187{
1188  CURLcode result = CURLE_OK;
1189  struct SessionHandle *data = conn->data;
1190  struct smtp_conn *smtpc = &conn->proto.smtpc;
1191  const char *mech = NULL;
1192  char *initresp = NULL;
1193  size_t len = 0;
1194  smtpstate state1 = SMTP_STOP;
1195  smtpstate state2 = SMTP_STOP;
1196
1197  (void)smtpcode;
1198  (void)instate; /* no use for this yet */
1199
1200  /* Remove the offending mechanism from the supported list */
1201  smtpc->authmechs ^= smtpc->authused;
1202
1203  /* Calculate alternative SASL login details */
1204  result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
1205                                  &state2);
1206
1207  if(!result) {
1208    /* Do we have any mechanisms left? */
1209    if(mech) {
1210      /* Retry SASL based authentication */
1211      result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
1212
1213      Curl_safefree(initresp);
1214    }
1215    else {
1216      failf(data, "Authentication cancelled");
1217
1218      result = CURLE_LOGIN_DENIED;
1219    }
1220  }
1221
1222  return result;
1223}
1224
1225/* For final responses in the AUTH sequence */
1226static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1227                                           int smtpcode,
1228                                           smtpstate instate)
1229{
1230  CURLcode result = CURLE_OK;
1231  struct SessionHandle *data = conn->data;
1232
1233  (void)instate; /* no use for this yet */
1234
1235  if(smtpcode != 235) {
1236    failf(data, "Authentication failed: %d", smtpcode);
1237    result = CURLE_LOGIN_DENIED;
1238  }
1239  else
1240    /* End of connect phase */
1241    state(conn, SMTP_STOP);
1242
1243  return result;
1244}
1245
1246/* For command responses */
1247static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1248                                        smtpstate instate)
1249{
1250  CURLcode result = CURLE_OK;
1251  struct SessionHandle *data = conn->data;
1252  struct SMTP *smtp = data->req.protop;
1253  char *line = data->state.buffer;
1254  size_t len = strlen(line);
1255
1256  (void)instate; /* no use for this yet */
1257
1258  if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1259     (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1260    failf(data, "Command failed: %d", smtpcode);
1261    result = CURLE_RECV_ERROR;
1262  }
1263  else {
1264    /* Temporarily add the LF character back and send as body to the client */
1265    if(!data->set.opt_no_body) {
1266      line[len] = '\n';
1267      result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1268      line[len] = '\0';
1269    }
1270
1271    if(smtpcode != 1) {
1272      if(smtp->rcpt) {
1273        smtp->rcpt = smtp->rcpt->next;
1274
1275        if(smtp->rcpt) {
1276          /* Send the next command */
1277          result = smtp_perform_command(conn);
1278        }
1279        else
1280          /* End of DO phase */
1281          state(conn, SMTP_STOP);
1282      }
1283      else
1284        /* End of DO phase */
1285        state(conn, SMTP_STOP);
1286    }
1287  }
1288
1289  return result;
1290}
1291
1292/* For MAIL responses */
1293static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1294                                     smtpstate instate)
1295{
1296  CURLcode result = CURLE_OK;
1297  struct SessionHandle *data = conn->data;
1298
1299  (void)instate; /* no use for this yet */
1300
1301  if(smtpcode/100 != 2) {
1302    failf(data, "MAIL failed: %d", smtpcode);
1303    result = CURLE_SEND_ERROR;
1304  }
1305  else
1306    /* Start the RCPT TO command */
1307    result = smtp_perform_rcpt_to(conn);
1308
1309  return result;
1310}
1311
1312/* For RCPT responses */
1313static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1314                                     smtpstate instate)
1315{
1316  CURLcode result = CURLE_OK;
1317  struct SessionHandle *data = conn->data;
1318  struct SMTP *smtp = data->req.protop;
1319
1320  (void)instate; /* no use for this yet */
1321
1322  if(smtpcode/100 != 2) {
1323    failf(data, "RCPT failed: %d", smtpcode);
1324    result = CURLE_SEND_ERROR;
1325  }
1326  else {
1327    smtp->rcpt = smtp->rcpt->next;
1328
1329    if(smtp->rcpt)
1330      /* Send the next RCPT TO command */
1331      result = smtp_perform_rcpt_to(conn);
1332    else {
1333      /* Send the DATA command */
1334      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1335
1336      if(!result)
1337        state(conn, SMTP_DATA);
1338    }
1339  }
1340
1341  return result;
1342}
1343
1344/* For DATA response */
1345static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1346                                     smtpstate instate)
1347{
1348  CURLcode result = CURLE_OK;
1349  struct SessionHandle *data = conn->data;
1350
1351  (void)instate; /* no use for this yet */
1352
1353  if(smtpcode != 354) {
1354    failf(data, "DATA failed: %d", smtpcode);
1355    result = CURLE_SEND_ERROR;
1356  }
1357  else {
1358    /* Set the progress upload size */
1359    Curl_pgrsSetUploadSize(data, data->state.infilesize);
1360
1361    /* SMTP upload */
1362    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1363
1364    /* End of DO phase */
1365    state(conn, SMTP_STOP);
1366  }
1367
1368  return result;
1369}
1370
1371/* For POSTDATA responses, which are received after the entire DATA
1372   part has been sent to the server */
1373static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1374                                         int smtpcode,
1375                                         smtpstate instate)
1376{
1377  CURLcode result = CURLE_OK;
1378
1379  (void)instate; /* no use for this yet */
1380
1381  if(smtpcode != 250)
1382    result = CURLE_RECV_ERROR;
1383
1384  /* End of DONE phase */
1385  state(conn, SMTP_STOP);
1386
1387  return result;
1388}
1389
1390static CURLcode smtp_statemach_act(struct connectdata *conn)
1391{
1392  CURLcode result = CURLE_OK;
1393  curl_socket_t sock = conn->sock[FIRSTSOCKET];
1394  struct SessionHandle *data = conn->data;
1395  int smtpcode;
1396  struct smtp_conn *smtpc = &conn->proto.smtpc;
1397  struct pingpong *pp = &smtpc->pp;
1398  size_t nread = 0;
1399
1400  /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1401  if(smtpc->state == SMTP_UPGRADETLS)
1402    return smtp_perform_upgrade_tls(conn);
1403
1404  /* Flush any data that needs to be sent */
1405  if(pp->sendleft)
1406    return Curl_pp_flushsend(pp);
1407
1408  do {
1409    /* Read the response from the server */
1410    result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1411    if(result)
1412      return result;
1413
1414    /* Store the latest response for later retrieval if necessary */
1415    if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1416      data->info.httpcode = smtpcode;
1417
1418    if(!smtpcode)
1419      break;
1420
1421    /* We have now received a full SMTP server response */
1422    switch(smtpc->state) {
1423    case SMTP_SERVERGREET:
1424      result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1425      break;
1426
1427    case SMTP_EHLO:
1428      result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1429      break;
1430
1431    case SMTP_HELO:
1432      result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1433      break;
1434
1435    case SMTP_STARTTLS:
1436      result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1437      break;
1438
1439    case SMTP_AUTH_PLAIN:
1440      result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1441      break;
1442
1443    case SMTP_AUTH_LOGIN:
1444      result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1445      break;
1446
1447    case SMTP_AUTH_LOGIN_PASSWD:
1448      result = smtp_state_auth_login_password_resp(conn, smtpcode,
1449                                                   smtpc->state);
1450      break;
1451
1452#ifndef CURL_DISABLE_CRYPTO_AUTH
1453    case SMTP_AUTH_CRAMMD5:
1454      result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1455      break;
1456
1457    case SMTP_AUTH_DIGESTMD5:
1458      result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1459      break;
1460
1461    case SMTP_AUTH_DIGESTMD5_RESP:
1462      result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1463      break;
1464#endif
1465
1466#ifdef USE_NTLM
1467    case SMTP_AUTH_NTLM:
1468      result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1469      break;
1470
1471    case SMTP_AUTH_NTLM_TYPE2MSG:
1472      result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1473                                                  smtpc->state);
1474      break;
1475#endif
1476
1477    case SMTP_AUTH_XOAUTH2:
1478      result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1479      break;
1480
1481    case SMTP_AUTH_CANCEL:
1482      result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1483      break;
1484
1485    case SMTP_AUTH_FINAL:
1486      result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1487      break;
1488
1489    case SMTP_COMMAND:
1490      result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1491      break;
1492
1493    case SMTP_MAIL:
1494      result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1495      break;
1496
1497    case SMTP_RCPT:
1498      result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1499      break;
1500
1501    case SMTP_DATA:
1502      result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1503      break;
1504
1505    case SMTP_POSTDATA:
1506      result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1507      break;
1508
1509    case SMTP_QUIT:
1510      /* fallthrough, just stop! */
1511    default:
1512      /* internal error */
1513      state(conn, SMTP_STOP);
1514      break;
1515    }
1516  } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1517
1518  return result;
1519}
1520
1521/* Called repeatedly until done from multi.c */
1522static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1523{
1524  CURLcode result = CURLE_OK;
1525  struct smtp_conn *smtpc = &conn->proto.smtpc;
1526
1527  if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1528    result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1529    if(result || !smtpc->ssldone)
1530      return result;
1531  }
1532
1533  result = Curl_pp_statemach(&smtpc->pp, FALSE);
1534  *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1535
1536  return result;
1537}
1538
1539static CURLcode smtp_block_statemach(struct connectdata *conn)
1540{
1541  CURLcode result = CURLE_OK;
1542  struct smtp_conn *smtpc = &conn->proto.smtpc;
1543
1544  while(smtpc->state != SMTP_STOP && !result)
1545    result = Curl_pp_statemach(&smtpc->pp, TRUE);
1546
1547  return result;
1548}
1549
1550/* Allocate and initialize the SMTP struct for the current SessionHandle if
1551   required */
1552static CURLcode smtp_init(struct connectdata *conn)
1553{
1554  CURLcode result = CURLE_OK;
1555  struct SessionHandle *data = conn->data;
1556  struct SMTP *smtp;
1557
1558  smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1559  if(!smtp)
1560    result = CURLE_OUT_OF_MEMORY;
1561
1562  return result;
1563}
1564
1565/* For the SMTP "protocol connect" and "doing" phases only */
1566static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1567                        int numsocks)
1568{
1569  return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1570}
1571
1572/***********************************************************************
1573 *
1574 * smtp_connect()
1575 *
1576 * This function should do everything that is to be considered a part of
1577 * the connection phase.
1578 *
1579 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1580 * connect phase is done when this function returns, or FALSE if not.
1581 */
1582static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1583{
1584  CURLcode result = CURLE_OK;
1585  struct smtp_conn *smtpc = &conn->proto.smtpc;
1586  struct pingpong *pp = &smtpc->pp;
1587
1588  *done = FALSE; /* default to not done yet */
1589
1590  /* We always support persistent connections in SMTP */
1591  connkeep(conn, "SMTP default");
1592
1593  /* Set the default response time-out */
1594  pp->response_time = RESP_TIMEOUT;
1595  pp->statemach_act = smtp_statemach_act;
1596  pp->endofresp = smtp_endofresp;
1597  pp->conn = conn;
1598
1599  /* Set the default preferred authentication mechanism */
1600  smtpc->prefmech = SASL_AUTH_ANY;
1601
1602  /* Initialise the pingpong layer */
1603  Curl_pp_init(pp);
1604
1605  /* Parse the URL options */
1606  result = smtp_parse_url_options(conn);
1607  if(result)
1608    return result;
1609
1610  /* Parse the URL path */
1611  result = smtp_parse_url_path(conn);
1612  if(result)
1613    return result;
1614
1615  /* Start off waiting for the server greeting response */
1616  state(conn, SMTP_SERVERGREET);
1617
1618  result = smtp_multi_statemach(conn, done);
1619
1620  return result;
1621}
1622
1623/***********************************************************************
1624 *
1625 * smtp_done()
1626 *
1627 * The DONE function. This does what needs to be done after a single DO has
1628 * performed.
1629 *
1630 * Input argument is already checked for validity.
1631 */
1632static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1633                          bool premature)
1634{
1635  CURLcode result = CURLE_OK;
1636  struct SessionHandle *data = conn->data;
1637  struct SMTP *smtp = data->req.protop;
1638  struct pingpong *pp = &conn->proto.smtpc.pp;
1639  const char *eob;
1640  ssize_t len;
1641  ssize_t bytes_written;
1642
1643  (void)premature;
1644
1645  if(!smtp)
1646    /* When the easy handle is removed from the multi interface while libcurl
1647       is still trying to resolve the host name, the SMTP struct is not yet
1648       initialized. However, the removal action calls Curl_done() which in
1649       turn calls this function, so we simply return success. */
1650    return CURLE_OK;
1651
1652  if(status) {
1653    connclose(conn, "SMTP done with bad status"); /* marked for closure */
1654    result = status;         /* use the already set error code */
1655  }
1656  else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1657    /* Calculate the EOB taking into account any terminating CRLF from the
1658       previous line of the email or the CRLF of the DATA command when there
1659       is "no mail data". RFC-5321, sect. 4.1.1.4. */
1660    eob = SMTP_EOB;
1661    len = SMTP_EOB_LEN;
1662    if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1663      eob += 2;
1664      len -= 2;
1665    }
1666
1667    /* Send the end of block data */
1668    result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1669    if(result)
1670      return result;
1671
1672    if(bytes_written != len) {
1673      /* The whole chunk was not sent so keep it around and adjust the
1674         pingpong structure accordingly */
1675      pp->sendthis = strdup(eob);
1676      pp->sendsize = len;
1677      pp->sendleft = len - bytes_written;
1678    }
1679    else
1680      /* Successfully sent so adjust the response timeout relative to now */
1681      pp->response = Curl_tvnow();
1682
1683    state(conn, SMTP_POSTDATA);
1684
1685    /* Run the state-machine
1686
1687       TODO: when the multi interface is used, this _really_ should be using
1688       the smtp_multi_statemach function but we have no general support for
1689       non-blocking DONE operations, not in the multi state machine and with
1690       Curl_done() invokes on several places in the code!
1691    */
1692    result = smtp_block_statemach(conn);
1693  }
1694
1695  /* Cleanup our per-request based variables */
1696  Curl_safefree(smtp->custom);
1697
1698  /* Clear the transfer mode for the next request */
1699  smtp->transfer = FTPTRANSFER_BODY;
1700
1701  return result;
1702}
1703
1704/***********************************************************************
1705 *
1706 * smtp_perform()
1707 *
1708 * This is the actual DO function for SMTP. Transfer a mail, send a command
1709 * or get some data according to the options previously setup.
1710 */
1711static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1712                             bool *dophase_done)
1713{
1714  /* This is SMTP and no proxy */
1715  CURLcode result = CURLE_OK;
1716  struct SessionHandle *data = conn->data;
1717  struct SMTP *smtp = data->req.protop;
1718
1719  DEBUGF(infof(conn->data, "DO phase starts\n"));
1720
1721  if(data->set.opt_no_body) {
1722    /* Requested no body means no transfer */
1723    smtp->transfer = FTPTRANSFER_INFO;
1724  }
1725
1726  *dophase_done = FALSE; /* not done yet */
1727
1728  /* Store the first recipient (or NULL if not specified) */
1729  smtp->rcpt = data->set.mail_rcpt;
1730
1731  /* Start the first command in the DO phase */
1732  if(data->set.upload && data->set.mail_rcpt)
1733    /* MAIL transfer */
1734    result = smtp_perform_mail(conn);
1735  else
1736    /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1737    result = smtp_perform_command(conn);
1738
1739  if(result)
1740    return result;
1741
1742  /* Run the state-machine */
1743  result = smtp_multi_statemach(conn, dophase_done);
1744
1745  *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1746
1747  if(*dophase_done)
1748    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1749
1750  return result;
1751}
1752
1753/***********************************************************************
1754 *
1755 * smtp_do()
1756 *
1757 * This function is registered as 'curl_do' function. It decodes the path
1758 * parts etc as a wrapper to the actual DO function (smtp_perform).
1759 *
1760 * The input argument is already checked for validity.
1761 */
1762static CURLcode smtp_do(struct connectdata *conn, bool *done)
1763{
1764  CURLcode result = CURLE_OK;
1765
1766  *done = FALSE; /* default to false */
1767
1768  /* Parse the custom request */
1769  result = smtp_parse_custom_request(conn);
1770  if(result)
1771    return result;
1772
1773  result = smtp_regular_transfer(conn, done);
1774
1775  return result;
1776}
1777
1778/***********************************************************************
1779 *
1780 * smtp_disconnect()
1781 *
1782 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1783 * resources. BLOCKING.
1784 */
1785static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1786{
1787  struct smtp_conn *smtpc = &conn->proto.smtpc;
1788
1789  /* We cannot send quit unconditionally. If this connection is stale or
1790     bad in any way, sending quit and waiting around here will make the
1791     disconnect wait in vain and cause more problems than we need to. */
1792
1793  /* The SMTP session may or may not have been allocated/setup at this
1794     point! */
1795  if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1796    if(!smtp_perform_quit(conn))
1797      (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1798
1799  /* Disconnect from the server */
1800  Curl_pp_disconnect(&smtpc->pp);
1801
1802  /* Cleanup the SASL module */
1803  Curl_sasl_cleanup(conn, smtpc->authused);
1804
1805  /* Cleanup our connection based variables */
1806  Curl_safefree(smtpc->domain);
1807
1808  return CURLE_OK;
1809}
1810
1811/* Call this when the DO phase has completed */
1812static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1813{
1814  struct SMTP *smtp = conn->data->req.protop;
1815
1816  (void)connected;
1817
1818  if(smtp->transfer != FTPTRANSFER_BODY)
1819    /* no data to transfer */
1820    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1821
1822  return CURLE_OK;
1823}
1824
1825/* Called from multi.c while DOing */
1826static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1827{
1828  CURLcode result = smtp_multi_statemach(conn, dophase_done);
1829
1830  if(result)
1831    DEBUGF(infof(conn->data, "DO phase failed\n"));
1832  else if(*dophase_done) {
1833    result = smtp_dophase_done(conn, FALSE /* not connected */);
1834
1835    DEBUGF(infof(conn->data, "DO phase is complete\n"));
1836  }
1837
1838  return result;
1839}
1840
1841/***********************************************************************
1842 *
1843 * smtp_regular_transfer()
1844 *
1845 * The input argument is already checked for validity.
1846 *
1847 * Performs all commands done before a regular transfer between a local and a
1848 * remote host.
1849 */
1850static CURLcode smtp_regular_transfer(struct connectdata *conn,
1851                                      bool *dophase_done)
1852{
1853  CURLcode result = CURLE_OK;
1854  bool connected = FALSE;
1855  struct SessionHandle *data = conn->data;
1856
1857  /* Make sure size is unknown at this point */
1858  data->req.size = -1;
1859
1860  /* Set the progress data */
1861  Curl_pgrsSetUploadCounter(data, 0);
1862  Curl_pgrsSetDownloadCounter(data, 0);
1863  Curl_pgrsSetUploadSize(data, 0);
1864  Curl_pgrsSetDownloadSize(data, 0);
1865
1866  /* Carry out the perform */
1867  result = smtp_perform(conn, &connected, dophase_done);
1868
1869  /* Perform post DO phase operations if necessary */
1870  if(!result && *dophase_done)
1871    result = smtp_dophase_done(conn, connected);
1872
1873  return result;
1874}
1875
1876static CURLcode smtp_setup_connection(struct connectdata *conn)
1877{
1878  struct SessionHandle *data = conn->data;
1879  CURLcode result;
1880
1881  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1882    /* Unless we have asked to tunnel SMTP operations through the proxy, we
1883       switch and use HTTP operations only */
1884#ifndef CURL_DISABLE_HTTP
1885    if(conn->handler == &Curl_handler_smtp)
1886      conn->handler = &Curl_handler_smtp_proxy;
1887    else {
1888#ifdef USE_SSL
1889      conn->handler = &Curl_handler_smtps_proxy;
1890#else
1891      failf(data, "SMTPS not supported!");
1892      return CURLE_UNSUPPORTED_PROTOCOL;
1893#endif
1894    }
1895    /* set it up as a HTTP connection instead */
1896    return conn->handler->setup_connection(conn);
1897
1898#else
1899    failf(data, "SMTP over http proxy requires HTTP support built-in!");
1900    return CURLE_UNSUPPORTED_PROTOCOL;
1901#endif
1902  }
1903
1904  /* Initialise the SMTP layer */
1905  result = smtp_init(conn);
1906  if(result)
1907    return result;
1908
1909  data->state.path++;   /* don't include the initial slash */
1910
1911  return CURLE_OK;
1912}
1913
1914/***********************************************************************
1915 *
1916 * smtp_parse_url_options()
1917 *
1918 * Parse the URL login options.
1919 */
1920static CURLcode smtp_parse_url_options(struct connectdata *conn)
1921{
1922  CURLcode result = CURLE_OK;
1923  struct smtp_conn *smtpc = &conn->proto.smtpc;
1924  const char *options = conn->options;
1925  const char *ptr = options;
1926  bool reset = TRUE;
1927
1928  while(ptr && *ptr) {
1929    const char *key = ptr;
1930
1931    while(*ptr && *ptr != '=')
1932        ptr++;
1933
1934    if(strnequal(key, "AUTH", 4)) {
1935      size_t len = 0;
1936      const char *value = ++ptr;
1937
1938      if(reset) {
1939        reset = FALSE;
1940        smtpc->prefmech = SASL_AUTH_NONE;
1941      }
1942
1943      while(*ptr && *ptr != ';') {
1944        ptr++;
1945        len++;
1946      }
1947
1948      if(strnequal(value, "*", len))
1949        smtpc->prefmech = SASL_AUTH_ANY;
1950      else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
1951        smtpc->prefmech |= SASL_MECH_LOGIN;
1952      else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
1953        smtpc->prefmech |= SASL_MECH_PLAIN;
1954      else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
1955        smtpc->prefmech |= SASL_MECH_CRAM_MD5;
1956      else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
1957        smtpc->prefmech |= SASL_MECH_DIGEST_MD5;
1958      else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
1959        smtpc->prefmech |= SASL_MECH_GSSAPI;
1960      else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
1961        smtpc->prefmech |= SASL_MECH_NTLM;
1962      else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
1963        smtpc->prefmech |= SASL_MECH_XOAUTH2;
1964
1965      if(*ptr == ';')
1966        ptr++;
1967    }
1968    else
1969      result = CURLE_URL_MALFORMAT;
1970  }
1971
1972  return result;
1973}
1974
1975/***********************************************************************
1976 *
1977 * smtp_parse_url_path()
1978 *
1979 * Parse the URL path into separate path components.
1980 */
1981static CURLcode smtp_parse_url_path(struct connectdata *conn)
1982{
1983  /* The SMTP struct is already initialised in smtp_connect() */
1984  struct SessionHandle *data = conn->data;
1985  struct smtp_conn *smtpc = &conn->proto.smtpc;
1986  const char *path = data->state.path;
1987  char localhost[HOSTNAME_MAX + 1];
1988
1989  /* Calculate the path if necessary */
1990  if(!*path) {
1991    if(!Curl_gethostname(localhost, sizeof(localhost)))
1992      path = localhost;
1993    else
1994      path = "localhost";
1995  }
1996
1997  /* URL decode the path and use it as the domain in our EHLO */
1998  return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1999}
2000
2001/***********************************************************************
2002 *
2003 * smtp_parse_custom_request()
2004 *
2005 * Parse the custom request.
2006 */
2007static CURLcode smtp_parse_custom_request(struct connectdata *conn)
2008{
2009  CURLcode result = CURLE_OK;
2010  struct SessionHandle *data = conn->data;
2011  struct SMTP *smtp = data->req.protop;
2012  const char *custom = data->set.str[STRING_CUSTOMREQUEST];
2013
2014  /* URL decode the custom request */
2015  if(custom)
2016    result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
2017
2018  return result;
2019}
2020
2021/***********************************************************************
2022 *
2023 * smtp_calc_sasl_details()
2024 *
2025 * Calculate the required login details for SASL authentication.
2026 */
2027static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
2028                                       const char **mech,
2029                                       char **initresp, size_t *len,
2030                                       smtpstate *state1, smtpstate *state2)
2031{
2032  CURLcode result = CURLE_OK;
2033  struct SessionHandle *data = conn->data;
2034  struct smtp_conn *smtpc = &conn->proto.smtpc;
2035
2036  /* Calculate the supported authentication mechanism, by decreasing order of
2037     security, as well as the initial response where appropriate */
2038#ifndef CURL_DISABLE_CRYPTO_AUTH
2039  if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2040     (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2041    *mech = SASL_MECH_STRING_DIGEST_MD5;
2042    *state1 = SMTP_AUTH_DIGESTMD5;
2043    smtpc->authused = SASL_MECH_DIGEST_MD5;
2044  }
2045  else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2046          (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2047    *mech = SASL_MECH_STRING_CRAM_MD5;
2048    *state1 = SMTP_AUTH_CRAMMD5;
2049    smtpc->authused = SASL_MECH_CRAM_MD5;
2050  }
2051  else
2052#endif
2053#ifdef USE_NTLM
2054  if((smtpc->authmechs & SASL_MECH_NTLM) &&
2055     (smtpc->prefmech & SASL_MECH_NTLM)) {
2056    *mech = SASL_MECH_STRING_NTLM;
2057    *state1 = SMTP_AUTH_NTLM;
2058    *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2059    smtpc->authused = SASL_MECH_NTLM;
2060
2061    if(data->set.sasl_ir)
2062      result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2063                                                   &conn->ntlm,
2064                                                   initresp, len);
2065    }
2066  else
2067#endif
2068  if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2069      (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2070      (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2071    *mech = SASL_MECH_STRING_XOAUTH2;
2072    *state1 = SMTP_AUTH_XOAUTH2;
2073    *state2 = SMTP_AUTH_FINAL;
2074    smtpc->authused = SASL_MECH_XOAUTH2;
2075
2076    if(data->set.sasl_ir)
2077      result = Curl_sasl_create_xoauth2_message(data, conn->user,
2078                                                conn->xoauth2_bearer,
2079                                                initresp, len);
2080  }
2081  else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2082          (smtpc->prefmech & SASL_MECH_LOGIN)) {
2083    *mech = SASL_MECH_STRING_LOGIN;
2084    *state1 = SMTP_AUTH_LOGIN;
2085    *state2 = SMTP_AUTH_LOGIN_PASSWD;
2086    smtpc->authused = SASL_MECH_LOGIN;
2087
2088    if(data->set.sasl_ir)
2089      result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2090  }
2091  else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2092          (smtpc->prefmech & SASL_MECH_PLAIN)) {
2093    *mech = SASL_MECH_STRING_PLAIN;
2094    *state1 = SMTP_AUTH_PLAIN;
2095    *state2 = SMTP_AUTH_FINAL;
2096    smtpc->authused = SASL_MECH_PLAIN;
2097
2098    if(data->set.sasl_ir)
2099      result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2100                                              initresp, len);
2101  }
2102
2103  return result;
2104}
2105
2106CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
2107{
2108  /* When sending a SMTP payload we must detect CRLF. sequences making sure
2109     they are sent as CRLF.. instead, as a . on the beginning of a line will
2110     be deleted by the server when not part of an EOB terminator and a
2111     genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2112     data by the server
2113  */
2114  ssize_t i;
2115  ssize_t si;
2116  struct SessionHandle *data = conn->data;
2117  struct SMTP *smtp = data->req.protop;
2118
2119  /* Do we need to allocate the scatch buffer? */
2120  if(!data->state.scratch) {
2121    data->state.scratch = malloc(2 * BUFSIZE);
2122
2123    if(!data->state.scratch) {
2124      failf (data, "Failed to alloc scratch buffer!");
2125      return CURLE_OUT_OF_MEMORY;
2126    }
2127  }
2128
2129  /* This loop can be improved by some kind of Boyer-Moore style of
2130     approach but that is saved for later... */
2131  for(i = 0, si = 0; i < nread; i++) {
2132    if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2133      smtp->eob++;
2134
2135      /* Is the EOB potentially the terminating CRLF? */
2136      if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2137        smtp->trailing_crlf = TRUE;
2138      else
2139        smtp->trailing_crlf = FALSE;
2140    }
2141    else if(smtp->eob) {
2142      /* A previous substring matched so output that first */
2143      memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2144      si += smtp->eob;
2145
2146      /* Then compare the first byte */
2147      if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2148        smtp->eob = 1;
2149      else
2150        smtp->eob = 0;
2151
2152      /* Reset the trailing CRLF flag as there was more data */
2153      smtp->trailing_crlf = FALSE;
2154    }
2155
2156    /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2157    if(SMTP_EOB_FIND_LEN == smtp->eob) {
2158      /* Copy the replacement data to the target buffer */
2159      memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
2160      si += SMTP_EOB_REPL_LEN;
2161      smtp->eob = 0;
2162    }
2163    else if(!smtp->eob)
2164      data->state.scratch[si++] = data->req.upload_fromhere[i];
2165  }
2166
2167  if(smtp->eob) {
2168    /* A substring matched before processing ended so output that now */
2169    memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2170    si += smtp->eob;
2171    smtp->eob = 0;
2172  }
2173
2174  if(si != nread) {
2175    /* Only use the new buffer if we replaced something */
2176    nread = si;
2177
2178    /* Upload from the new (replaced) buffer instead */
2179    data->req.upload_fromhere = data->state.scratch;
2180
2181    /* Set the new amount too */
2182    data->req.upload_present = nread;
2183  }
2184
2185  return CURLE_OK;
2186}
2187
2188#endif /* CURL_DISABLE_SMTP */
2189