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