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