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