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