URL sanitize: reject URLs containing bad data
[platform/upstream/curl.git] / lib / smtp.c
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 */
100 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
101 static CURLcode smtp_do(struct connectdata *conn, bool *done);
102 static CURLcode smtp_done(struct connectdata *conn,
103                           CURLcode, bool premature);
104 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
105 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
106 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
107 static int smtp_getsock(struct connectdata *conn,
108                         curl_socket_t *socks,
109                         int numsocks);
110 static CURLcode smtp_doing(struct connectdata *conn,
111                            bool *dophase_done);
112 static CURLcode smtp_setup_connection(struct connectdata * conn);
113 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn);
114
115 /*
116  * SMTP protocol handler.
117  */
118
119 const 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 | PROTOPT_NOURLQUERY /* flags */
137 };
138
139 #ifdef USE_SSL
140 /*
141  * SMTPS protocol handler.
142  */
143
144 const 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
162   | PROTOPT_NOURLQUERY              /* flags */
163 };
164 #endif
165
166 #ifndef CURL_DISABLE_HTTP
167 /*
168  * HTTP-proxyed SMTP protocol handler.
169  */
170
171 static const struct Curl_handler Curl_handler_smtp_proxy = {
172   "SMTP",                               /* scheme */
173   ZERO_NULL,                            /* setup_connection */
174   Curl_http,                            /* do_it */
175   Curl_http_done,                       /* done */
176   ZERO_NULL,                            /* do_more */
177   ZERO_NULL,                            /* connect_it */
178   ZERO_NULL,                            /* connecting */
179   ZERO_NULL,                            /* doing */
180   ZERO_NULL,                            /* proto_getsock */
181   ZERO_NULL,                            /* doing_getsock */
182   ZERO_NULL,                            /* domore_getsock */
183   ZERO_NULL,                            /* perform_getsock */
184   ZERO_NULL,                            /* disconnect */
185   ZERO_NULL,                            /* readwrite */
186   PORT_SMTP,                            /* defport */
187   CURLPROTO_HTTP,                       /* protocol */
188   PROTOPT_NONE                          /* flags */
189 };
190
191 #ifdef USE_SSL
192 /*
193  * HTTP-proxyed SMTPS protocol handler.
194  */
195
196 static const struct Curl_handler Curl_handler_smtps_proxy = {
197   "SMTPS",                              /* scheme */
198   ZERO_NULL,                            /* setup_connection */
199   Curl_http,                            /* do_it */
200   Curl_http_done,                       /* done */
201   ZERO_NULL,                            /* do_more */
202   ZERO_NULL,                            /* connect_it */
203   ZERO_NULL,                            /* connecting */
204   ZERO_NULL,                            /* doing */
205   ZERO_NULL,                            /* proto_getsock */
206   ZERO_NULL,                            /* doing_getsock */
207   ZERO_NULL,                            /* domore_getsock */
208   ZERO_NULL,                            /* perform_getsock */
209   ZERO_NULL,                            /* disconnect */
210   ZERO_NULL,                            /* readwrite */
211   PORT_SMTPS,                           /* defport */
212   CURLPROTO_HTTP,                       /* protocol */
213   PROTOPT_NONE                          /* flags */
214 };
215 #endif
216 #endif
217
218 /* Function that checks for an ending smtp status code at the start of the
219    given string.
220    As a side effect, it also flags allowed authentication mechanisms according
221    to EHLO AUTH response. */
222 static int smtp_endofresp(struct pingpong *pp, int *resp)
223 {
224   char *line = pp->linestart_resp;
225   size_t len = pp->nread_resp;
226   struct connectdata *conn = pp->conn;
227   struct smtp_conn *smtpc = &conn->proto.smtpc;
228   int result;
229   size_t wordlen;
230
231   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
232     return FALSE;       /* Nothing for us. */
233
234   if((result = (line[3] == ' ')) != 0)
235     *resp = curlx_sltosi(strtol(line, NULL, 10));
236
237   line += 4;
238   len -= 4;
239
240   if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
241     line += 5;
242     len -= 5;
243
244     for(;;) {
245       while(len &&
246             (*line == ' ' || *line == '\t' ||
247              *line == '\r' || *line == '\n')) {
248         line++;
249         len--;
250       }
251
252       if(!len)
253         break;
254
255       for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
256             line[wordlen] != '\t' && line[wordlen] != '\r' &&
257             line[wordlen] != '\n';)
258         wordlen++;
259
260       if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
261         smtpc->authmechs |= SMTP_AUTH_LOGIN;
262       else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
263         smtpc->authmechs |= SMTP_AUTH_PLAIN;
264       else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
265         smtpc->authmechs |= SMTP_AUTH_CRAM_MD5;
266       else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
267         smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5;
268       else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
269         smtpc->authmechs |= SMTP_AUTH_GSSAPI;
270       else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
271         smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
272       else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
273         smtpc->authmechs |= SMTP_AUTH_NTLM;
274
275       line += wordlen;
276       len -= wordlen;
277     }
278   }
279
280   return result;
281 }
282
283 /* This is the ONLY way to change SMTP state! */
284 static void state(struct connectdata *conn,
285                   smtpstate newstate)
286 {
287   struct smtp_conn *smtpc = &conn->proto.smtpc;
288 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
289   /* for debug purposes */
290   static const char * const names[] = {
291     "STOP",
292     "SERVERGREET",
293     "EHLO",
294     "HELO",
295     "STARTTLS",
296     "UPGRADETLS",
297     "AUTHPLAIN",
298     "AUTHLOGIN",
299     "AUTHPASSWD",
300     "AUTHCRAM",
301     "AUTHNTLM",
302     "AUTHNTLM_TYPE2MSG",
303     "AUTH",
304     "MAIL",
305     "RCPT",
306     "DATA",
307     "POSTDATA",
308     "QUIT",
309     /* LAST */
310   };
311   if(smtpc->state != newstate)
312     infof(conn->data, "SMTP %p state change from %s to %s\n",
313           smtpc, names[smtpc->state], names[newstate]);
314 #endif
315   smtpc->state = newstate;
316 }
317
318 static CURLcode smtp_state_ehlo(struct connectdata *conn)
319 {
320   CURLcode result;
321   struct smtp_conn *smtpc = &conn->proto.smtpc;
322
323   smtpc->authmechs = 0;         /* No known authentication mechanisms yet. */
324   smtpc->authused = 0;          /* Clear the authentication mechanism used
325                                    for esmtp connections */
326
327   /* send EHLO */
328   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
329
330   if(result)
331     return result;
332
333   state(conn, SMTP_EHLO);
334   return CURLE_OK;
335 }
336
337 static CURLcode smtp_state_helo(struct connectdata *conn)
338 {
339   CURLcode result;
340   struct smtp_conn *smtpc = &conn->proto.smtpc;
341
342   smtpc->authused = 0;          /* No authentication mechanism used in smtp
343                                    connections */
344
345   /* send HELO */
346   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
347
348   if(result)
349     return result;
350
351   state(conn, SMTP_HELO);
352   return CURLE_OK;
353 }
354
355 static CURLcode smtp_auth_plain_data(struct connectdata *conn,
356                                      char **outptr, size_t *outlen)
357 {
358   char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
359   size_t ulen;
360   size_t plen;
361
362   ulen = strlen(conn->user);
363   plen = strlen(conn->passwd);
364
365   if(2 * ulen + plen + 2 > sizeof plainauth) {
366     *outlen = 0;
367     *outptr = NULL;
368     return CURLE_OUT_OF_MEMORY; /* plainauth too small */
369   }
370
371   memcpy(plainauth, conn->user, ulen);
372   plainauth[ulen] = '\0';
373   memcpy(plainauth + ulen + 1, conn->user, ulen);
374   plainauth[2 * ulen + 1] = '\0';
375   memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen);
376   return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2,
377                             outptr, outlen);
378 }
379
380 static CURLcode smtp_auth_login_user(struct connectdata *conn,
381                                      char **outptr, size_t *outlen)
382 {
383   size_t ulen = strlen(conn->user);
384
385   if(!ulen) {
386     *outptr = strdup("=");
387     if(*outptr) {
388       *outlen = (size_t) 1;
389       return CURLE_OK;
390     }
391     *outlen = 0;
392     return CURLE_OUT_OF_MEMORY;
393   }
394
395   return Curl_base64_encode(conn->data, conn->user, ulen, outptr, outlen);
396 }
397
398 #ifdef USE_NTLM
399 static CURLcode smtp_auth_ntlm_type1_message(struct connectdata *conn,
400                                              char **outptr, size_t *outlen)
401 {
402   return Curl_ntlm_create_type1_message(conn->user, conn->passwd,
403                                         &conn->ntlm, outptr, outlen);
404 }
405 #endif
406
407 static CURLcode smtp_authenticate(struct connectdata *conn)
408 {
409   CURLcode result = CURLE_OK;
410   struct smtp_conn *smtpc = &conn->proto.smtpc;
411   char *initresp = NULL;
412   const char *mech = NULL;
413   size_t len = 0;
414   smtpstate state1 = SMTP_STOP;
415   smtpstate state2 = SMTP_STOP;
416
417   /* Check we have a username and password to authenticate with and end the
418      connect phase if we don't. */
419   if(!conn->bits.user_passwd) {
420     state(conn, SMTP_STOP);
421
422     return result;
423   }
424
425   /* Check supported authentication mechanisms by decreasing order of
426      security. */
427 #ifndef CURL_DISABLE_CRYPTO_AUTH
428   if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
429     mech = "CRAM-MD5";
430     state1 = SMTP_AUTHCRAM;
431     smtpc->authused = SMTP_AUTH_CRAM_MD5;
432   }
433   else
434 #endif
435 #ifdef USE_NTLM
436   if(smtpc->authmechs & SMTP_AUTH_NTLM) {
437     mech = "NTLM";
438     state1 = SMTP_AUTHNTLM;
439     state2 = SMTP_AUTHNTLM_TYPE2MSG;
440     smtpc->authused = SMTP_AUTH_NTLM;
441     result = smtp_auth_ntlm_type1_message(conn, &initresp, &len);
442   }
443   else
444 #endif
445   if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
446     mech = "LOGIN";
447     state1 = SMTP_AUTHLOGIN;
448     state2 = SMTP_AUTHPASSWD;
449     smtpc->authused = SMTP_AUTH_LOGIN;
450     result = smtp_auth_login_user(conn, &initresp, &len);
451   }
452   else if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
453     mech = "PLAIN";
454     state1 = SMTP_AUTHPLAIN;
455     state2 = SMTP_AUTH;
456     smtpc->authused = SMTP_AUTH_PLAIN;
457     result = smtp_auth_plain_data(conn, &initresp, &len);
458   }
459   else {
460     infof(conn->data, "No known auth mechanisms supported!\n");
461     result = CURLE_LOGIN_DENIED;      /* Other mechanisms not supported. */
462   }
463
464   if(!result) {
465     if(initresp &&
466        strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
467        result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
468
469       if(!result)
470         state(conn, state2);
471     }
472     else {
473       result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
474
475       if(!result)
476         state(conn, state1);
477     }
478     Curl_safefree(initresp);
479   }
480
481   return result;
482 }
483
484 /* For the SMTP "protocol connect" and "doing" phases only */
485 static int smtp_getsock(struct connectdata *conn,
486                         curl_socket_t *socks,
487                         int numsocks)
488 {
489   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
490 }
491
492 #ifdef USE_SSL
493 static void smtp_to_smtps(struct connectdata *conn)
494 {
495   conn->handler = &Curl_handler_smtps;
496 }
497 #else
498 #define smtp_to_smtps(x) Curl_nop_stmt
499 #endif
500
501 /* for STARTTLS responses */
502 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
503                                          int smtpcode,
504                                          smtpstate instate)
505 {
506   CURLcode result = CURLE_OK;
507   struct SessionHandle *data = conn->data;
508   (void)instate; /* no use for this yet */
509
510   if(smtpcode != 220) {
511     if(data->set.use_ssl != CURLUSESSL_TRY) {
512       failf(data, "STARTTLS denied. %c", smtpcode);
513       result = CURLE_USE_SSL_FAILED;
514     }
515     else
516       result = smtp_authenticate(conn);
517   }
518   else {
519     if(data->state.used_interface == Curl_if_multi) {
520       state(conn, SMTP_UPGRADETLS);
521       return smtp_state_upgrade_tls(conn);
522     }
523     else {
524       result = Curl_ssl_connect(conn, FIRSTSOCKET);
525       if(CURLE_OK == result) {
526         smtp_to_smtps(conn);
527         result = smtp_state_ehlo(conn);
528       }
529     }
530   }
531
532   return result;
533 }
534
535 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
536 {
537   struct smtp_conn *smtpc = &conn->proto.smtpc;
538   CURLcode result;
539
540   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
541
542   if(smtpc->ssldone) {
543     smtp_to_smtps(conn);
544     result = smtp_state_ehlo(conn);
545   }
546
547   return result;
548 }
549
550 /* for EHLO responses */
551 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
552                                      int smtpcode,
553                                      smtpstate instate)
554 {
555   CURLcode result = CURLE_OK;
556   struct SessionHandle *data = conn->data;
557
558   (void)instate; /* no use for this yet */
559
560   if(smtpcode/100 != 2) {
561     if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
562      !conn->bits.user_passwd)
563       result = smtp_state_helo(conn);
564     else {
565       failf(data, "Access denied: %d", smtpcode);
566       result = CURLE_LOGIN_DENIED;
567     }
568   }
569   else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
570     /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
571        to TLS connection now */
572     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
573     state(conn, SMTP_STARTTLS);
574   }
575   else
576     result = smtp_authenticate(conn);
577
578   return result;
579 }
580
581 /* for HELO responses */
582 static CURLcode smtp_state_helo_resp(struct connectdata *conn,
583                                      int smtpcode,
584                                      smtpstate instate)
585 {
586   CURLcode result = CURLE_OK;
587   struct SessionHandle *data = conn->data;
588
589   (void)instate; /* no use for this yet */
590
591   if(smtpcode/100 != 2) {
592     failf(data, "Access denied: %d", smtpcode);
593     result = CURLE_LOGIN_DENIED;
594   }
595   else {
596     /* end the connect phase */
597     state(conn, SMTP_STOP);
598   }
599
600   return result;
601 }
602
603 /* for AUTH PLAIN (without initial response) responses */
604 static CURLcode smtp_state_authplain_resp(struct connectdata *conn,
605                                           int smtpcode,
606                                           smtpstate instate)
607 {
608   CURLcode result = CURLE_OK;
609   struct SessionHandle *data = conn->data;
610   size_t len = 0;
611   char *plainauth = NULL;
612
613   (void)instate; /* no use for this yet */
614
615   if(smtpcode != 334) {
616     failf(data, "Access denied: %d", smtpcode);
617     result = CURLE_LOGIN_DENIED;
618   }
619   else {
620     result = smtp_auth_plain_data(conn, &plainauth, &len);
621
622     if(!result) {
623       if(plainauth) {
624         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
625
626         if(!result)
627           state(conn, SMTP_AUTH);
628       }
629       Curl_safefree(plainauth);
630     }
631   }
632
633   return result;
634 }
635
636 /* for AUTH LOGIN (without initial response) responses */
637 static CURLcode smtp_state_authlogin_resp(struct connectdata *conn,
638                                           int smtpcode,
639                                           smtpstate instate)
640 {
641   CURLcode result = CURLE_OK;
642   struct SessionHandle *data = conn->data;
643   size_t len = 0;
644   char *authuser = NULL;
645
646   (void)instate; /* no use for this yet */
647
648   if(smtpcode != 334) {
649     failf(data, "Access denied: %d", smtpcode);
650     result = CURLE_LOGIN_DENIED;
651   }
652   else {
653     result = smtp_auth_login_user(conn, &authuser, &len);
654
655     if(!result) {
656       if(authuser) {
657         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
658
659         if(!result)
660           state(conn, SMTP_AUTHPASSWD);
661       }
662       Curl_safefree(authuser);
663     }
664   }
665
666   return result;
667 }
668
669 /* for responses to user entry of AUTH LOGIN. */
670 static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn,
671                                            int smtpcode,
672                                            smtpstate instate)
673 {
674   CURLcode result = CURLE_OK;
675   struct SessionHandle *data = conn->data;
676   size_t plen;
677   size_t len = 0;
678   char *authpasswd = NULL;
679
680   (void)instate; /* no use for this yet */
681
682   if(smtpcode != 334) {
683     failf(data, "Access denied: %d", smtpcode);
684     result = CURLE_LOGIN_DENIED;
685   }
686   else {
687     plen = strlen(conn->passwd);
688
689     if(!plen)
690       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "=");
691     else {
692       result = Curl_base64_encode(data, conn->passwd, plen, &authpasswd, &len);
693
694       if(!result) {
695         if(authpasswd) {
696           result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
697
698           if(!result)
699             state(conn, SMTP_AUTH);
700         }
701         Curl_safefree(authpasswd);
702       }
703     }
704   }
705
706   return result;
707 }
708
709 #ifndef CURL_DISABLE_CRYPTO_AUTH
710
711 /* for AUTH CRAM-MD5 responses. */
712 static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
713                                          int smtpcode,
714                                          smtpstate instate)
715 {
716   CURLcode result = CURLE_OK;
717   struct SessionHandle *data = conn->data;
718   char * chlg64 = data->state.buffer;
719   unsigned char * chlg;
720   size_t chlglen;
721   size_t len = 0;
722   char *rplyb64 = NULL;
723   HMAC_context *ctxt;
724   unsigned char digest[16];
725   char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1];
726
727   (void)instate; /* no use for this yet */
728
729   if(smtpcode != 334) {
730     failf(data, "Access denied: %d", smtpcode);
731     return CURLE_LOGIN_DENIED;
732   }
733
734   /* Get the challenge. */
735   for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
736     ;
737
738   chlg = (unsigned char *) NULL;
739   chlglen = 0;
740
741   if(*chlg64 != '=') {
742     for(len = strlen(chlg64); len--;)
743       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
744          chlg64[len] != '\t')
745         break;
746
747     if(++len) {
748       chlg64[len] = '\0';
749
750       result = Curl_base64_decode(chlg64, &chlg, &chlglen);
751       if(result)
752         return result;
753     }
754   }
755
756   /* Compute digest. */
757   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
758                         (const unsigned char *) conn->passwd,
759                         (unsigned int)(strlen(conn->passwd)));
760
761   if(!ctxt) {
762     Curl_safefree(chlg);
763     return CURLE_OUT_OF_MEMORY;
764   }
765
766   if(chlglen > 0)
767     Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen));
768
769   Curl_safefree(chlg);
770
771   Curl_HMAC_final(ctxt, digest);
772
773   /* Prepare the reply. */
774   snprintf(reply, sizeof reply,
775    "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
776            conn->user, digest[0], digest[1], digest[2], digest[3], digest[4],
777            digest[5],
778            digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
779            digest[12], digest[13], digest[14], digest[15]);
780
781   /* Encode it to base64 and send it. */
782   result = Curl_base64_encode(data, reply, 0, &rplyb64, &len);
783
784   if(!result) {
785     if(rplyb64) {
786       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
787
788       if(!result)
789         state(conn, SMTP_AUTH);
790     }
791     Curl_safefree(rplyb64);
792   }
793
794   return result;
795 }
796
797 #endif
798
799 #ifdef USE_NTLM
800 /* for the AUTH NTLM (without initial response) response. */
801 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
802                                           int smtpcode,
803                                           smtpstate instate)
804 {
805   CURLcode result = CURLE_OK;
806   struct SessionHandle *data = conn->data;
807   char *type1msg = NULL;
808   size_t len = 0;
809
810   (void)instate; /* no use for this yet */
811
812   if(smtpcode != 334) {
813     failf(data, "Access denied: %d", smtpcode);
814     result = CURLE_LOGIN_DENIED;
815   }
816   else {
817     result = smtp_auth_ntlm_type1_message(conn, &type1msg, &len);
818
819     if(!result) {
820       if(type1msg) {
821         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
822
823         if(!result)
824           state(conn, SMTP_AUTHNTLM_TYPE2MSG);
825       }
826       Curl_safefree(type1msg);
827     }
828   }
829
830   return result;
831 }
832
833 /* for the NTLM type-2 response (sent in reponse to our type-1 message). */
834 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
835                                                    int smtpcode,
836                                                    smtpstate instate)
837 {
838   CURLcode result = CURLE_OK;
839   struct SessionHandle *data = conn->data;
840   char *type3msg = NULL;
841   size_t len = 0;
842
843   (void)instate; /* no use for this yet */
844
845   if(smtpcode != 334) {
846     failf(data, "Access denied: %d", smtpcode);
847     result = CURLE_LOGIN_DENIED;
848   }
849   else {
850     result = Curl_ntlm_decode_type2_message(data, data->state.buffer + 4,
851                                             &conn->ntlm);
852     if(!result) {
853       result = Curl_ntlm_create_type3_message(conn->data, conn->user,
854                                               conn->passwd, &conn->ntlm,
855                                               &type3msg, &len);
856       if(!result) {
857         if(type3msg) {
858           result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
859
860           if(!result)
861             state(conn, SMTP_AUTH);
862         }
863         Curl_safefree(type3msg);
864       }
865     }
866   }
867
868   return result;
869 }
870 #endif
871
872 /* for final responses to AUTH sequences. */
873 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
874                                      int smtpcode,
875                                      smtpstate instate)
876 {
877   CURLcode result = CURLE_OK;
878   struct SessionHandle *data = conn->data;
879
880   (void)instate; /* no use for this yet */
881
882   if(smtpcode != 235) {
883     failf(data, "Authentication failed: %d", smtpcode);
884     result = CURLE_LOGIN_DENIED;
885   }
886   else
887     state(conn, SMTP_STOP);             /* End of connect phase. */
888
889   return result;
890 }
891
892 /* start the DO phase */
893 static CURLcode smtp_mail(struct connectdata *conn)
894 {
895   char *from = NULL;
896   char *size = NULL;
897   CURLcode result = CURLE_OK;
898   struct SessionHandle *data = conn->data;
899
900   /* calculate the FROM parameter */
901   if(!data->set.str[STRING_MAIL_FROM])
902     /* null reverse-path, RFC-2821, sect. 3.7 */
903     from = strdup("<>");
904   else if(data->set.str[STRING_MAIL_FROM][0] == '<')
905     from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
906   else
907     from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
908
909   if(!from)
910     return CURLE_OUT_OF_MEMORY;
911
912   /* calculate the optional SIZE parameter */
913   if(conn->data->set.infilesize > 0) {
914     size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
915
916     if(!size) {
917       Curl_safefree(from);
918
919       return CURLE_OUT_OF_MEMORY;
920     }
921   }
922
923   /* send MAIL FROM */
924   if(!size)
925     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s", from);
926   else
927     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s SIZE=%s",
928                            from, size);
929
930   Curl_safefree(size);
931   Curl_safefree(from);
932
933   if(result)
934     return result;
935
936   state(conn, SMTP_MAIL);
937
938   return result;
939 }
940
941 static CURLcode smtp_rcpt_to(struct connectdata *conn)
942 {
943   CURLcode result = CURLE_OK;
944   struct smtp_conn *smtpc = &conn->proto.smtpc;
945
946   /* send RCPT TO */
947   if(smtpc->rcpt) {
948     if(smtpc->rcpt->data[0] == '<')
949       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
950                              smtpc->rcpt->data);
951     else
952       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
953                              smtpc->rcpt->data);
954     if(!result)
955       state(conn, SMTP_RCPT);
956   }
957
958   return result;
959 }
960
961 /* for MAIL responses */
962 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
963                                      int smtpcode,
964                                      smtpstate instate)
965 {
966   CURLcode result = CURLE_OK;
967   struct SessionHandle *data = conn->data;
968   (void)instate; /* no use for this yet */
969
970   if(smtpcode/100 != 2) {
971     failf(data, "Access denied: %d", smtpcode);
972     result = CURLE_LOGIN_DENIED;
973     state(conn, SMTP_STOP);
974   }
975   else {
976     struct smtp_conn *smtpc = &conn->proto.smtpc;
977     smtpc->rcpt = data->set.mail_rcpt;
978
979     result = smtp_rcpt_to(conn);
980   }
981
982   return result;
983 }
984
985 /* for RCPT responses */
986 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
987                                      int smtpcode,
988                                      smtpstate instate)
989 {
990   CURLcode result = CURLE_OK;
991   struct SessionHandle *data = conn->data;
992   (void)instate; /* no use for this yet */
993
994   if(smtpcode/100 != 2) {
995     failf(data, "Access denied: %d", smtpcode);
996     result = CURLE_LOGIN_DENIED;
997     state(conn, SMTP_STOP);
998   }
999   else {
1000     struct smtp_conn *smtpc = &conn->proto.smtpc;
1001
1002     if(smtpc->rcpt) {
1003       smtpc->rcpt = smtpc->rcpt->next;
1004       result = smtp_rcpt_to(conn);
1005
1006       /* if we failed or still is in RCPT sending, return */
1007       if(result || smtpc->rcpt)
1008         return result;
1009     }
1010
1011     /* send DATA */
1012     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1013     if(result)
1014       return result;
1015
1016     state(conn, SMTP_DATA);
1017   }
1018
1019   return result;
1020 }
1021
1022 /* for the DATA response */
1023 static CURLcode smtp_state_data_resp(struct connectdata *conn,
1024                                      int smtpcode,
1025                                      smtpstate instate)
1026 {
1027   struct SessionHandle *data = conn->data;
1028   struct FTP *smtp = data->state.proto.smtp;
1029
1030   (void)instate; /* no use for this yet */
1031
1032   if(smtpcode != 354) {
1033     state(conn, SMTP_STOP);
1034     return CURLE_RECV_ERROR;
1035   }
1036
1037   /* SMTP upload */
1038   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
1039                       FIRSTSOCKET, smtp->bytecountp);
1040
1041   state(conn, SMTP_STOP);
1042   return CURLE_OK;
1043 }
1044
1045 /* for the POSTDATA response, which is received after the entire DATA
1046    part has been sent off to the server */
1047 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1048                                      int smtpcode,
1049                                      smtpstate instate)
1050 {
1051   CURLcode result = CURLE_OK;
1052
1053   (void)instate; /* no use for this yet */
1054
1055   if(smtpcode != 250)
1056     result = CURLE_RECV_ERROR;
1057
1058   state(conn, SMTP_STOP);
1059
1060   return result;
1061 }
1062
1063 static CURLcode smtp_statemach_act(struct connectdata *conn)
1064 {
1065   CURLcode result;
1066   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1067   struct SessionHandle *data = conn->data;
1068   int smtpcode;
1069   struct smtp_conn *smtpc = &conn->proto.smtpc;
1070   struct pingpong *pp = &smtpc->pp;
1071   size_t nread = 0;
1072
1073   if(smtpc->state == SMTP_UPGRADETLS)
1074     return smtp_state_upgrade_tls(conn);
1075
1076   if(pp->sendleft)
1077     /* we have a piece of a command still left to send */
1078     return Curl_pp_flushsend(pp);
1079
1080   /* we read a piece of response */
1081   result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1082   if(result)
1083     return result;
1084
1085   if(smtpcode) {
1086     /* we have now received a full SMTP server response */
1087     switch(smtpc->state) {
1088     case SMTP_SERVERGREET:
1089       if(smtpcode/100 != 2) {
1090         failf(data, "Got unexpected smtp-server response: %d", smtpcode);
1091         return CURLE_FTP_WEIRD_SERVER_REPLY;
1092       }
1093
1094       result = smtp_state_ehlo(conn);
1095       if(result)
1096         return result;
1097       break;
1098
1099     case SMTP_EHLO:
1100       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1101       break;
1102
1103     case SMTP_HELO:
1104       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1105       break;
1106
1107     case SMTP_STARTTLS:
1108       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1109       break;
1110
1111     case SMTP_AUTHPLAIN:
1112       result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state);
1113       break;
1114
1115     case SMTP_AUTHLOGIN:
1116       result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state);
1117       break;
1118
1119     case SMTP_AUTHPASSWD:
1120       result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state);
1121       break;
1122
1123 #ifndef CURL_DISABLE_CRYPTO_AUTH
1124     case SMTP_AUTHCRAM:
1125       result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state);
1126       break;
1127 #endif
1128
1129 #ifdef USE_NTLM
1130     case SMTP_AUTHNTLM:
1131       result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1132       break;
1133
1134     case SMTP_AUTHNTLM_TYPE2MSG:
1135       result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1136                                                   smtpc->state);
1137       break;
1138 #endif
1139
1140     case SMTP_AUTH:
1141       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1142       break;
1143
1144     case SMTP_MAIL:
1145       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1146       break;
1147
1148     case SMTP_RCPT:
1149       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1150       break;
1151
1152     case SMTP_DATA:
1153       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1154       break;
1155
1156     case SMTP_POSTDATA:
1157       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1158       break;
1159
1160     case SMTP_QUIT:
1161       /* fallthrough, just stop! */
1162     default:
1163       /* internal error */
1164       state(conn, SMTP_STOP);
1165       break;
1166     }
1167   }
1168
1169   return result;
1170 }
1171
1172 /* called repeatedly until done from multi.c */
1173 static CURLcode smtp_multi_statemach(struct connectdata *conn,
1174                                      bool *done)
1175 {
1176   struct smtp_conn *smtpc = &conn->proto.smtpc;
1177   CURLcode result;
1178
1179   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone)
1180     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1181   else
1182     result = Curl_pp_multi_statemach(&smtpc->pp);
1183
1184   *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1185
1186   return result;
1187 }
1188
1189 static CURLcode smtp_easy_statemach(struct connectdata *conn)
1190 {
1191   struct smtp_conn *smtpc = &conn->proto.smtpc;
1192   struct pingpong *pp = &smtpc->pp;
1193   CURLcode result = CURLE_OK;
1194
1195   while(smtpc->state != SMTP_STOP) {
1196     result = Curl_pp_easy_statemach(pp);
1197     if(result)
1198       break;
1199   }
1200
1201   return result;
1202 }
1203
1204 /*
1205  * Allocate and initialize the struct SMTP for the current SessionHandle.  If
1206  * need be.
1207  */
1208 static CURLcode smtp_init(struct connectdata *conn)
1209 {
1210   struct SessionHandle *data = conn->data;
1211   struct FTP *smtp = data->state.proto.smtp;
1212   if(!smtp) {
1213     smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1214     if(!smtp)
1215       return CURLE_OUT_OF_MEMORY;
1216   }
1217
1218   /* get some initial data into the smtp struct */
1219   smtp->bytecountp = &data->req.bytecount;
1220
1221   /* No need to duplicate user+password, the connectdata struct won't change
1222      during a session, but we re-init them here since on subsequent inits
1223      since the conn struct may have changed or been replaced.
1224   */
1225   smtp->user = conn->user;
1226   smtp->passwd = conn->passwd;
1227
1228   return CURLE_OK;
1229 }
1230
1231 /*
1232  * smtp_connect() should do everything that is to be considered a part of
1233  * the connection phase.
1234  *
1235  * The variable pointed to by 'done' will be TRUE if the protocol-layer
1236  * connect phase is done when this function returns, or FALSE if not. When
1237  * called as a part of the easy interface, it will always be TRUE.
1238  */
1239 static CURLcode smtp_connect(struct connectdata *conn,
1240                              bool *done) /* see description above */
1241 {
1242   CURLcode result;
1243   struct smtp_conn *smtpc = &conn->proto.smtpc;
1244   struct SessionHandle *data = conn->data;
1245   struct pingpong *pp = &smtpc->pp;
1246   const char *path = conn->data->state.path;
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   result = Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1319   if(result)
1320     return result;
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  */
1346 static 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
1409 static
1410 CURLcode 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  */
1456 static 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  */
1487 static 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  */
1508 static 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 */
1540 static 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 */
1558 static 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  */
1582 static
1583 CURLcode smtp_regular_transfer(struct connectdata *conn,
1584                                bool *dophase_done)
1585 {
1586   CURLcode result = CURLE_OK;
1587   bool connected = FALSE;
1588   struct SessionHandle *data = conn->data;
1589   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
1614 static 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
1649 CURLcode 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 */