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