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