smtp: Moved smtp_rcpt_to() to be with the other perform functions
[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 /* Function that checks for an ending SMTP status code at the start of the
219    given string, but also detects various capabilities from the EHLO response
220    including the supported authentication mechanisms. */
221 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
222                            int *resp)
223 {
224   struct smtp_conn *smtpc = &conn->proto.smtpc;
225   bool result = FALSE;
226   size_t wordlen;
227
228   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
229     return FALSE;       /* Nothing for us */
230
231   /* Do we have a command response? */
232   result = (line[3] == ' ') ? TRUE : FALSE;
233   if(result)
234     *resp = curlx_sltosi(strtol(line, NULL, 10));
235
236   /* Are we processing EHLO command data? */
237   if(smtpc->state == SMTP_EHLO && (!result || (result && *resp/100 == 2))) {
238     line += 4;
239     len -= 4;
240
241     /* Does the server support the STARTTLS capability? */
242     if(len >= 8 && !memcmp(line, "STARTTLS", 8))
243       smtpc->tls_supported = TRUE;
244
245     /* Does the server support the SIZE capability? */
246     else if(len >= 4 && !memcmp(line, "SIZE", 4))
247       smtpc->size_supported = TRUE;
248
249     /* Do we have the authentication mechanism list? */
250     else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
251       line += 5;
252       len -= 5;
253
254       /* Loop through the data line */
255       for(;;) {
256         while(len &&
257               (*line == ' ' || *line == '\t' ||
258                *line == '\r' || *line == '\n')) {
259
260           line++;
261           len--;
262         }
263
264         if(!len)
265           break;
266
267         /* Extract the word */
268         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
269               line[wordlen] != '\t' && line[wordlen] != '\r' &&
270               line[wordlen] != '\n';)
271           wordlen++;
272
273         /* Test the word for a matching authentication mechanism */
274         if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
275           smtpc->authmechs |= SASL_MECH_LOGIN;
276         else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
277           smtpc->authmechs |= SASL_MECH_PLAIN;
278         else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
279           smtpc->authmechs |= SASL_MECH_CRAM_MD5;
280         else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
281           smtpc->authmechs |= SASL_MECH_DIGEST_MD5;
282         else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
283           smtpc->authmechs |= SASL_MECH_GSSAPI;
284         else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
285           smtpc->authmechs |= SASL_MECH_EXTERNAL;
286         else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
287           smtpc->authmechs |= SASL_MECH_NTLM;
288
289         line += wordlen;
290         len -= wordlen;
291       }
292     }
293   }
294
295   return result;
296 }
297
298 /* This is the ONLY way to change SMTP state! */
299 static void state(struct connectdata *conn, smtpstate newstate)
300 {
301   struct smtp_conn *smtpc = &conn->proto.smtpc;
302 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
303   /* for debug purposes */
304   static const char * const names[] = {
305     "STOP",
306     "SERVERGREET",
307     "EHLO",
308     "HELO",
309     "STARTTLS",
310     "UPGRADETLS",
311     "AUTH_PLAIN",
312     "AUTH_LOGIN",
313     "AUTH_LOGIN_PASSWD",
314     "AUTH_CRAMMD5",
315     "AUTH_DIGESTMD5",
316     "AUTH_DIGESTMD5_RESP",
317     "AUTH_NTLM",
318     "AUTH_NTLM_TYPE2MSG",
319     "AUTH_FINAL",
320     "MAIL",
321     "RCPT",
322     "DATA",
323     "POSTDATA",
324     "QUIT",
325     /* LAST */
326   };
327
328   if(smtpc->state != newstate)
329     infof(conn->data, "SMTP %p state change from %s to %s\n",
330           smtpc, names[smtpc->state], names[newstate]);
331 #endif
332
333   smtpc->state = newstate;
334 }
335
336 static CURLcode smtp_state_ehlo(struct connectdata *conn)
337 {
338   CURLcode result = CURLE_OK;
339   struct smtp_conn *smtpc = &conn->proto.smtpc;
340
341   smtpc->authmechs = 0;         /* No known authentication mechanisms yet */
342   smtpc->authused = 0;          /* Clear the authentication mechanism used
343                                    for esmtp connections */
344   smtpc->tls_supported = FALSE; /* Clear the TLS capability */
345
346   /* Send the EHLO command */
347   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
348
349   if(!result)
350     state(conn, SMTP_EHLO);
351
352   return result;
353 }
354
355 static CURLcode smtp_state_helo(struct connectdata *conn)
356 {
357   CURLcode result = CURLE_OK;
358   struct smtp_conn *smtpc = &conn->proto.smtpc;
359
360   smtpc->authused = 0;          /* No authentication mechanism used in smtp
361                                    connections */
362
363   /* Send the HELO command */
364   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
365
366   if(!result)
367     state(conn, SMTP_HELO);
368
369   return result;
370 }
371
372 static CURLcode smtp_state_starttls(struct connectdata *conn)
373 {
374   CURLcode result = CURLE_OK;
375
376   /* Send the STARTTLS command */
377   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
378
379   if(!result)
380     state(conn, SMTP_STARTTLS);
381
382   return result;
383 }
384
385 static CURLcode smtp_state_upgrade_tls(struct connectdata *conn)
386 {
387   CURLcode result = CURLE_OK;
388   struct smtp_conn *smtpc = &conn->proto.smtpc;
389
390   /* Start the SSL connection */
391   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
392
393   if(!result) {
394     if(smtpc->state != SMTP_UPGRADETLS)
395       state(conn, SMTP_UPGRADETLS);
396
397     if(smtpc->ssldone) {
398       smtp_to_smtps(conn);
399       result = smtp_state_ehlo(conn);
400     }
401   }
402
403   return result;
404 }
405
406 static CURLcode smtp_authenticate(struct connectdata *conn)
407 {
408   CURLcode result = CURLE_OK;
409   struct smtp_conn *smtpc = &conn->proto.smtpc;
410   const char *mech = NULL;
411   char *initresp = NULL;
412   size_t len = 0;
413   smtpstate state1 = SMTP_STOP;
414   smtpstate state2 = SMTP_STOP;
415
416   /* Check we have a username and password to authenticate with and end the
417      connect phase if we don't */
418   if(!conn->bits.user_passwd) {
419     state(conn, SMTP_STOP);
420
421     return result;
422   }
423
424   /* Calculate the supported authentication mechanism, by decreasing order of
425      security, as well as the initial response where appropriate */
426 #ifndef CURL_DISABLE_CRYPTO_AUTH
427   if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
428      (smtpc->prefmech & 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           (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
435     mech = "CRAM-MD5";
436     state1 = SMTP_AUTH_CRAMMD5;
437     smtpc->authused = SASL_MECH_CRAM_MD5;
438   }
439   else
440 #endif
441 #ifdef USE_NTLM
442   if((smtpc->authmechs & SASL_MECH_NTLM) &&
443      (smtpc->prefmech & SASL_MECH_NTLM)) {
444     mech = "NTLM";
445     state1 = SMTP_AUTH_NTLM;
446     state2 = SMTP_AUTH_NTLM_TYPE2MSG;
447     smtpc->authused = SASL_MECH_NTLM;
448     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
449                                                  &conn->ntlm,
450                                                  &initresp, &len);
451   }
452   else
453 #endif
454   if((smtpc->authmechs & SASL_MECH_LOGIN) &&
455      (smtpc->prefmech & SASL_MECH_LOGIN)) {
456     mech = "LOGIN";
457     state1 = SMTP_AUTH_LOGIN;
458     state2 = SMTP_AUTH_LOGIN_PASSWD;
459     smtpc->authused = SASL_MECH_LOGIN;
460     result = Curl_sasl_create_login_message(conn->data, conn->user,
461                                             &initresp, &len);
462   }
463   else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
464           (smtpc->prefmech & SASL_MECH_PLAIN)) {
465     mech = "PLAIN";
466     state1 = SMTP_AUTH_PLAIN;
467     state2 = SMTP_AUTH_FINAL;
468     smtpc->authused = SASL_MECH_PLAIN;
469     result = Curl_sasl_create_plain_message(conn->data, conn->user,
470                                             conn->passwd, &initresp, &len);
471   }
472   else {
473     /* Other mechanisms not supported */
474     infof(conn->data, "No known authentication mechanisms supported!\n");
475     result = CURLE_LOGIN_DENIED;
476   }
477
478   if(!result) {
479     /* Perform SASL based authentication */
480     if(initresp &&
481        strlen(mech) + len <= 512 - 8) { /* AUTH <mech> ...<crlf> */
482        result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
483
484       if(!result)
485         state(conn, state2);
486     }
487     else {
488       result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
489
490       if(!result)
491         state(conn, state1);
492     }
493
494     Curl_safefree(initresp);
495   }
496
497   return result;
498 }
499
500 /* Start the DO phase */
501 static CURLcode smtp_mail(struct connectdata *conn)
502 {
503   char *from = NULL;
504   char *auth = NULL;
505   char *size = NULL;
506   CURLcode result = CURLE_OK;
507   struct SessionHandle *data = conn->data;
508
509   /* Calculate the FROM parameter */
510   if(!data->set.str[STRING_MAIL_FROM])
511     /* Null reverse-path, RFC-2821, sect. 3.7 */
512     from = strdup("<>");
513   else if(data->set.str[STRING_MAIL_FROM][0] == '<')
514     from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
515   else
516     from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
517
518   if(!from)
519     return CURLE_OUT_OF_MEMORY;
520
521   /* Calculate the optional AUTH parameter */
522   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.authused) {
523     if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
524       auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
525     else
526       /* Empty AUTH, RFC-2554, sect. 5 */
527       auth = strdup("<>");
528
529     if(!auth) {
530       Curl_safefree(from);
531
532       return CURLE_OUT_OF_MEMORY;
533     }
534   }
535
536   /* calculate the optional SIZE parameter */
537   if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
538     size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
539
540     if(!size) {
541       Curl_safefree(from);
542       Curl_safefree(auth);
543
544       return CURLE_OUT_OF_MEMORY;
545     }
546   }
547
548   /* Send the MAIL command */
549   if(!auth && !size)
550     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
551                            "MAIL FROM:%s", from);
552   else if(auth && !size)
553     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
554                            "MAIL FROM:%s AUTH=%s", from, auth);
555   else if(auth && size)
556     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
557                            "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
558   else
559     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
560                            "MAIL FROM:%s SIZE=%s", from, size);
561
562   Curl_safefree(from);
563   Curl_safefree(auth);
564   Curl_safefree(size);
565
566   if(!result)
567     state(conn, SMTP_MAIL);
568
569   return result;
570 }
571
572 static CURLcode smtp_rcpt_to(struct connectdata *conn)
573 {
574   CURLcode result = CURLE_OK;
575   struct SessionHandle *data = conn->data;
576   struct SMTP *smtp = data->state.proto.smtp;
577
578   /* Send the RCPT TO command */
579   if(smtp->rcpt) {
580     if(smtp->rcpt->data[0] == '<')
581       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
582                              smtp->rcpt->data);
583     else
584       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
585                              smtp->rcpt->data);
586     if(!result)
587       state(conn, SMTP_RCPT);
588   }
589
590   return result;
591 }
592
593 /* For the initial server greeting */
594 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
595                                             int smtpcode,
596                                             smtpstate instate)
597 {
598   CURLcode result = CURLE_OK;
599   struct SessionHandle *data = conn->data;
600
601   (void)instate; /* no use for this yet */
602
603   if(smtpcode/100 != 2) {
604     failf(data, "Got unexpected smtp-server response: %d", smtpcode);
605     result = CURLE_FTP_WEIRD_SERVER_REPLY;
606   }
607   else
608     result = smtp_state_ehlo(conn);
609
610   return result;
611 }
612
613 /* For STARTTLS responses */
614 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
615                                          int smtpcode,
616                                          smtpstate instate)
617 {
618   CURLcode result = CURLE_OK;
619   struct SessionHandle *data = conn->data;
620
621   (void)instate; /* no use for this yet */
622
623   if(smtpcode != 220) {
624     if(data->set.use_ssl != CURLUSESSL_TRY) {
625       failf(data, "STARTTLS denied. %c", smtpcode);
626       result = CURLE_USE_SSL_FAILED;
627     }
628     else
629       result = smtp_authenticate(conn);
630   }
631   else
632     result = smtp_state_upgrade_tls(conn);
633
634   return result;
635 }
636
637 /* For EHLO responses */
638 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
639                                      smtpstate instate)
640 {
641   CURLcode result = CURLE_OK;
642   struct SessionHandle *data = conn->data;
643   struct smtp_conn *smtpc = &conn->proto.smtpc;
644
645   (void)instate; /* no use for this yet */
646
647   if(smtpcode/100 != 2) {
648     if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
649      !conn->bits.user_passwd)
650       result = smtp_state_helo(conn);
651     else {
652       failf(data, "Remote access denied: %d", smtpcode);
653       result = CURLE_REMOTE_ACCESS_DENIED;
654     }
655   }
656   else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
657     /* We don't have a SSL/TLS connection yet, but SSL is requested */
658     if(smtpc->tls_supported)
659       /* Switch to TLS connection now */
660       result = smtp_state_starttls(conn);
661     else if(data->set.use_ssl == CURLUSESSL_TRY)
662       /* Fallback and carry on with authentication */
663       result = smtp_authenticate(conn);
664     else {
665       failf(data, "STARTTLS not supported.");
666       result = CURLE_USE_SSL_FAILED;
667     }
668   }
669   else
670     result = smtp_authenticate(conn);
671
672   return result;
673 }
674
675 /* For HELO responses */
676 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
677                                      smtpstate instate)
678 {
679   CURLcode result = CURLE_OK;
680   struct SessionHandle *data = conn->data;
681
682   (void)instate; /* no use for this yet */
683
684   if(smtpcode/100 != 2) {
685     failf(data, "Remote access denied: %d", smtpcode);
686     result = CURLE_REMOTE_ACCESS_DENIED;
687   }
688   else
689     /* End of connect phase */
690     state(conn, SMTP_STOP);
691
692   return result;
693 }
694
695 /* For AUTH PLAIN (without initial response) responses */
696 static CURLcode smtp_state_auth_plain_resp(struct connectdata *conn,
697                                            int smtpcode,
698                                            smtpstate instate)
699 {
700   CURLcode result = CURLE_OK;
701   struct SessionHandle *data = conn->data;
702   size_t len = 0;
703   char *plainauth = NULL;
704
705   (void)instate; /* no use for this yet */
706
707   if(smtpcode != 334) {
708     failf(data, "Access denied: %d", smtpcode);
709     result = CURLE_LOGIN_DENIED;
710   }
711   else {
712     /* Create the authorisation message */
713     result = Curl_sasl_create_plain_message(conn->data, conn->user,
714                                             conn->passwd, &plainauth, &len);
715
716     /* Send the message */
717     if(!result) {
718       if(plainauth) {
719         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
720
721         if(!result)
722           state(conn, SMTP_AUTH_FINAL);
723       }
724
725       Curl_safefree(plainauth);
726     }
727   }
728
729   return result;
730 }
731
732 /* For AUTH LOGIN (without initial response) responses */
733 static CURLcode smtp_state_auth_login_resp(struct connectdata *conn,
734                                            int smtpcode,
735                                            smtpstate instate)
736 {
737   CURLcode result = CURLE_OK;
738   struct SessionHandle *data = conn->data;
739   size_t len = 0;
740   char *authuser = NULL;
741
742   (void)instate; /* no use for this yet */
743
744   if(smtpcode != 334) {
745     failf(data, "Access denied: %d", smtpcode);
746     result = CURLE_LOGIN_DENIED;
747   }
748   else {
749     /* Create the user message */
750     result = Curl_sasl_create_login_message(conn->data, conn->user,
751                                             &authuser, &len);
752
753     /* Send the user */
754     if(!result) {
755       if(authuser) {
756         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
757
758         if(!result)
759           state(conn, SMTP_AUTH_LOGIN_PASSWD);
760       }
761
762       Curl_safefree(authuser);
763     }
764   }
765
766   return result;
767 }
768
769 /* For AUTH LOGIN user entry responses */
770 static CURLcode smtp_state_auth_login_password_resp(struct connectdata *conn,
771                                                     int smtpcode,
772                                                     smtpstate instate)
773 {
774   CURLcode result = CURLE_OK;
775   struct SessionHandle *data = conn->data;
776   size_t len = 0;
777   char *authpasswd = NULL;
778
779   (void)instate; /* no use for this yet */
780
781   if(smtpcode != 334) {
782     failf(data, "Access denied: %d", smtpcode);
783     result = CURLE_LOGIN_DENIED;
784   }
785   else {
786     /* Create the password message */
787     result = Curl_sasl_create_login_message(conn->data, conn->passwd,
788                                             &authpasswd, &len);
789
790     /* Send the password */
791     if(!result) {
792       if(authpasswd) {
793         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
794
795         if(!result)
796           state(conn, SMTP_AUTH_FINAL);
797       }
798
799       Curl_safefree(authpasswd);
800     }
801   }
802
803   return result;
804 }
805
806 #ifndef CURL_DISABLE_CRYPTO_AUTH
807 /* For AUTH CRAM-MD5 responses */
808 static CURLcode smtp_state_auth_cram_resp(struct connectdata *conn,
809                                           int smtpcode,
810                                           smtpstate instate)
811 {
812   CURLcode result = CURLE_OK;
813   struct SessionHandle *data = conn->data;
814   char *chlg64 = data->state.buffer;
815   size_t len = 0;
816   char *rplyb64 = NULL;
817
818   (void)instate; /* no use for this yet */
819
820   if(smtpcode != 334) {
821     failf(data, "Access denied: %d", smtpcode);
822     return CURLE_LOGIN_DENIED;
823   }
824
825   /* Get the challenge */
826   for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
827     ;
828
829   /* Terminate the challenge */
830   if(*chlg64 != '=') {
831     for(len = strlen(chlg64); len--;)
832       if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
833          chlg64[len] != '\t')
834         break;
835
836     if(++len) {
837       chlg64[len] = '\0';
838     }
839   }
840
841   /* Create the response message */
842   result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
843                                              conn->passwd, &rplyb64, &len);
844
845   /* Send the response */
846   if(!result) {
847     if(rplyb64) {
848       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
849
850       if(!result)
851         state(conn, SMTP_AUTH_FINAL);
852     }
853
854     Curl_safefree(rplyb64);
855   }
856
857   return result;
858 }
859
860 /* For AUTH DIGEST-MD5 challenge responses */
861 static CURLcode smtp_state_auth_digest_resp(struct connectdata *conn,
862                                             int smtpcode,
863                                             smtpstate instate)
864 {
865   CURLcode result = CURLE_OK;
866   struct SessionHandle *data = conn->data;
867   char *chlg64 = data->state.buffer;
868   size_t len = 0;
869   char *rplyb64 = NULL;
870
871   (void)instate; /* no use for this yet */
872
873   if(smtpcode != 334) {
874     failf(data, "Access denied: %d", smtpcode);
875     return CURLE_LOGIN_DENIED;
876   }
877
878   /* Get the challenge */
879   for(chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
880     ;
881
882   /* Create the response message */
883   result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
884                                                conn->passwd, "smtp",
885                                                &rplyb64, &len);
886
887   /* Send the response */
888   if(!result) {
889     if(rplyb64) {
890       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
891
892       if(!result)
893         state(conn, SMTP_AUTH_DIGESTMD5_RESP);
894     }
895
896     Curl_safefree(rplyb64);
897   }
898
899   return result;
900 }
901
902 /* For AUTH DIGEST-MD5 challenge-response responses */
903 static CURLcode smtp_state_auth_digest_resp_resp(struct connectdata *conn,
904                                                  int smtpcode,
905                                                  smtpstate instate)
906 {
907   CURLcode result = CURLE_OK;
908   struct SessionHandle *data = conn->data;
909
910   (void)instate; /* no use for this yet */
911
912   if(smtpcode != 334) {
913     failf(data, "Authentication failed: %d", smtpcode);
914     result = CURLE_LOGIN_DENIED;
915   }
916   else {
917     /* Send an empty response */
918     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "");
919
920     if(!result)
921       state(conn, SMTP_AUTH_FINAL);
922   }
923
924   return result;
925 }
926
927 #endif
928
929 #ifdef USE_NTLM
930 /* For AUTH NTLM (without initial response) responses */
931 static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
932                                           int smtpcode,
933                                           smtpstate instate)
934 {
935   CURLcode result = CURLE_OK;
936   struct SessionHandle *data = conn->data;
937   char *type1msg = NULL;
938   size_t len = 0;
939
940   (void)instate; /* no use for this yet */
941
942   if(smtpcode != 334) {
943     failf(data, "Access denied: %d", smtpcode);
944     result = CURLE_LOGIN_DENIED;
945   }
946   else {
947     /* Create the type-1 message */
948     result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
949                                                  &conn->ntlm,
950                                                  &type1msg, &len);
951
952     /* Send the message */
953     if(!result) {
954       if(type1msg) {
955         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
956
957         if(!result)
958           state(conn, SMTP_AUTH_NTLM_TYPE2MSG);
959       }
960
961       Curl_safefree(type1msg);
962     }
963   }
964
965   return result;
966 }
967
968 /* For NTLM type-2 responses (sent in reponse to our type-1 message) */
969 static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
970                                                    int smtpcode,
971                                                    smtpstate instate)
972 {
973   CURLcode result = CURLE_OK;
974   struct SessionHandle *data = conn->data;
975   char *type3msg = NULL;
976   size_t len = 0;
977
978   (void)instate; /* no use for this yet */
979
980   if(smtpcode != 334) {
981     failf(data, "Access denied: %d", smtpcode);
982     result = CURLE_LOGIN_DENIED;
983   }
984   else {
985     /* Create the type-3 message */
986     result = Curl_sasl_create_ntlm_type3_message(data,
987                                                  data->state.buffer + 4,
988                                                  conn->user, conn->passwd,
989                                                  &conn->ntlm,
990                                                  &type3msg, &len);
991
992     /* Send the message */
993     if(!result) {
994       if(type3msg) {
995         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
996
997         if(!result)
998           state(conn, SMTP_AUTH_FINAL);
999       }
1000
1001       Curl_safefree(type3msg);
1002     }
1003   }
1004
1005   return result;
1006 }
1007 #endif
1008
1009 /* For the final responses to the AUTH sequence */
1010 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1011                                            int smtpcode,
1012                                            smtpstate instate)
1013 {
1014   CURLcode result = CURLE_OK;
1015   struct SessionHandle *data = conn->data;
1016
1017   (void)instate; /* no use for this yet */
1018
1019   if(smtpcode != 235) {
1020     failf(data, "Authentication failed: %d", smtpcode);
1021     result = CURLE_LOGIN_DENIED;
1022   }
1023   else
1024     /* End of connect phase */
1025     state(conn, SMTP_STOP);
1026
1027   return result;
1028 }
1029
1030 /* For MAIL responses */
1031 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1032                                      smtpstate instate)
1033 {
1034   CURLcode result = CURLE_OK;
1035   struct SessionHandle *data = conn->data;
1036   struct SMTP *smtp = data->state.proto.smtp;
1037
1038   (void)instate; /* no use for this yet */
1039
1040   if(smtpcode/100 != 2) {
1041     failf(data, "MAIL failed: %d", smtpcode);
1042     result = CURLE_SEND_ERROR;
1043     state(conn, SMTP_STOP);
1044   }
1045   else {
1046     smtp->rcpt = data->set.mail_rcpt;
1047
1048     result = smtp_rcpt_to(conn);
1049   }
1050
1051   return result;
1052 }
1053
1054 /* For RCPT responses */
1055 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1056                                      smtpstate instate)
1057 {
1058   CURLcode result = CURLE_OK;
1059   struct SessionHandle *data = conn->data;
1060   struct SMTP *smtp = data->state.proto.smtp;
1061
1062   (void)instate; /* no use for this yet */
1063
1064   if(smtpcode/100 != 2) {
1065     failf(data, "RCPT failed: %d", smtpcode);
1066     result = CURLE_SEND_ERROR;
1067     state(conn, SMTP_STOP);
1068   }
1069   else {
1070     if(smtp->rcpt) {
1071       smtp->rcpt = smtp->rcpt->next;
1072       result = smtp_rcpt_to(conn);
1073
1074       /* If we failed or still are sending RCPT data then return */
1075       if(result || smtp->rcpt)
1076         return result;
1077     }
1078
1079     /* Send the DATA command */
1080     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
1081
1082     if(!result)
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_FINAL:
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 && !result)
1266     result = Curl_pp_statemach(&smtpc->pp, TRUE);
1267
1268   return result;
1269 }
1270
1271 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1272    required */
1273 static CURLcode smtp_init(struct connectdata *conn)
1274 {
1275   CURLcode result = CURLE_OK;
1276   struct SessionHandle *data = conn->data;
1277   struct SMTP *smtp = data->state.proto.smtp;
1278
1279   if(!smtp) {
1280     smtp = data->state.proto.smtp = calloc(sizeof(struct SMTP), 1);
1281     if(!smtp)
1282       result = CURLE_OUT_OF_MEMORY;
1283   }
1284
1285   return result;
1286 }
1287
1288 /* For the SMTP "protocol connect" and "doing" phases only */
1289 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1290                         int numsocks)
1291 {
1292   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1293 }
1294
1295 /***********************************************************************
1296  *
1297  * smtp_connect()
1298  *
1299  * This function should do everything that is to be considered a part of
1300  * the connection phase.
1301  *
1302  * The variable pointed to by 'done' will be TRUE if the protocol-layer
1303  * connect phase is done when this function returns, or FALSE if not. When
1304  * called as a part of the easy interface, it will always be TRUE.
1305  */
1306 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1307 {
1308   CURLcode result = CURLE_OK;
1309   struct smtp_conn *smtpc = &conn->proto.smtpc;
1310   struct pingpong *pp = &smtpc->pp;
1311
1312   *done = FALSE; /* default to not done yet */
1313
1314   /* If there already is a protocol-specific struct allocated for this
1315      sessionhandle, deal with it */
1316   Curl_reset_reqproto(conn);
1317
1318   /* Initialise the SMTP layer */
1319   result = smtp_init(conn);
1320   if(result)
1321     return result;
1322
1323   /* We always support persistent connections in SMTP */
1324   conn->bits.close = FALSE;
1325
1326   /* Set the default response time-out */
1327   pp->response_time = RESP_TIMEOUT;
1328   pp->statemach_act = smtp_statemach_act;
1329   pp->endofresp = smtp_endofresp;
1330   pp->conn = conn;
1331
1332   /* Set the default preferred authentication mechanism */
1333   smtpc->prefmech = SASL_AUTH_ANY;
1334
1335   /* Initialise the pingpong layer */
1336   Curl_pp_init(pp);
1337
1338   /* Parse the URL options */
1339   result = smtp_parse_url_options(conn);
1340   if(result)
1341     return result;
1342
1343   /* Parse the URL path */
1344   result = smtp_parse_url_path(conn);
1345   if(result)
1346     return result;
1347
1348   /* Start off waiting for the server greeting response */
1349   state(conn, SMTP_SERVERGREET);
1350
1351   result = smtp_multi_statemach(conn, done);
1352
1353   return result;
1354 }
1355
1356 /***********************************************************************
1357  *
1358  * smtp_done()
1359  *
1360  * The DONE function. This does what needs to be done after a single DO has
1361  * performed.
1362  *
1363  * Input argument is already checked for validity.
1364  */
1365 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1366                           bool premature)
1367 {
1368   CURLcode result = CURLE_OK;
1369   struct SessionHandle *data = conn->data;
1370   struct SMTP *smtp = data->state.proto.smtp;
1371   ssize_t bytes_written;
1372
1373   (void)premature;
1374
1375   if(!smtp)
1376     /* When the easy handle is removed from the multi interface while libcurl
1377        is still trying to resolve the host name, the SMTP struct is not yet
1378        initialized. However, the removal action calls Curl_done() which in
1379        turn calls this function, so we simply return success. */
1380     return CURLE_OK;
1381
1382   if(status) {
1383     conn->bits.close = TRUE; /* marked for closure */
1384     result = status;         /* use the already set error code */
1385   }
1386   else if(!data->set.connect_only) {
1387     struct smtp_conn *smtpc = &conn->proto.smtpc;
1388     struct pingpong *pp = &smtpc->pp;
1389
1390     /* Send the end of block data */
1391     result = Curl_write(conn,
1392                         conn->writesockfd,  /* socket to send to */
1393                         SMTP_EOB,           /* buffer pointer */
1394                         SMTP_EOB_LEN,       /* buffer size */
1395                         &bytes_written);    /* actually sent away */
1396
1397     if(result)
1398       return result;
1399
1400     if(bytes_written != SMTP_EOB_LEN) {
1401       /* The whole chunk was not sent so keep it around and adjust the
1402          pingpong structure accordingly */
1403       pp->sendthis = strdup(SMTP_EOB);
1404       pp->sendsize = SMTP_EOB_LEN;
1405       pp->sendleft = SMTP_EOB_LEN - bytes_written;
1406     }
1407     else
1408       /* Successfully sent so adjust the response timeout relative to now */
1409       pp->response = Curl_tvnow();
1410
1411     state(conn, SMTP_POSTDATA);
1412
1413     /* Run the state-machine
1414
1415        TODO: when the multi interface is used, this _really_ should be using
1416        the smtp_multi_statemach function but we have no general support for
1417        non-blocking DONE operations, not in the multi state machine and with
1418        Curl_done() invokes on several places in the code!
1419     */
1420     result = smtp_block_statemach(conn);
1421   }
1422
1423   /* Clear the transfer mode for the next request */
1424   smtp->transfer = FTPTRANSFER_BODY;
1425
1426   return result;
1427 }
1428
1429 /***********************************************************************
1430  *
1431  * smtp_perform()
1432  *
1433  * This is the actual DO function for SMTP. Send a mail according to the
1434  * options previously setup.
1435  */
1436 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1437                              bool *dophase_done)
1438 {
1439   /* This is SMTP and no proxy */
1440   CURLcode result = CURLE_OK;
1441
1442   DEBUGF(infof(conn->data, "DO phase starts\n"));
1443
1444   if(conn->data->set.opt_no_body) {
1445     /* Requested no body means no transfer */
1446     struct SMTP *smtp = conn->data->state.proto.smtp;
1447     smtp->transfer = FTPTRANSFER_INFO;
1448   }
1449
1450   *dophase_done = FALSE; /* not done yet */
1451
1452   /* Start the first command in the DO phase */
1453   result = smtp_mail(conn);
1454   if(result)
1455     return result;
1456
1457   /* run the state-machine */
1458   result = smtp_multi_statemach(conn, dophase_done);
1459
1460   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1461
1462   if(*dophase_done)
1463     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1464
1465   return result;
1466 }
1467
1468 /***********************************************************************
1469  *
1470  * smtp_do()
1471  *
1472  * This function is registered as 'curl_do' function. It decodes the path
1473  * parts etc as a wrapper to the actual DO function (smtp_perform).
1474  *
1475  * The input argument is already checked for validity.
1476  */
1477 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1478 {
1479   CURLcode result = CURLE_OK;
1480
1481   *done = FALSE; /* default to false */
1482
1483   /* Since connections can be re-used between SessionHandles, there might be a
1484      connection already existing but on a fresh SessionHandle struct. As such
1485      we make sure we have a good SMTP struct to play with. For new connections
1486      the SMTP struct is allocated and setup in the smtp_connect() function. */
1487   Curl_reset_reqproto(conn);
1488   result = smtp_init(conn);
1489   if(result)
1490     return result;
1491
1492   result = smtp_regular_transfer(conn, done);
1493
1494   return result;
1495 }
1496
1497 /***********************************************************************
1498  *
1499  * smtp_quit()
1500  *
1501  * Performs the quit action prior to sclose() being called.
1502  */
1503 static CURLcode smtp_quit(struct connectdata *conn)
1504 {
1505   CURLcode result = CURLE_OK;
1506
1507   /* Send the QUIT command */
1508   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1509
1510   if(!result)
1511     state(conn, SMTP_QUIT);
1512
1513   return result;
1514 }
1515
1516 /***********************************************************************
1517  *
1518  * smtp_disconnect()
1519  *
1520  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1521  * resources. BLOCKING.
1522  */
1523 static CURLcode smtp_disconnect(struct connectdata *conn,
1524                                 bool dead_connection)
1525 {
1526   struct smtp_conn *smtpc = &conn->proto.smtpc;
1527
1528   /* We cannot send quit unconditionally. If this connection is stale or
1529      bad in any way, sending quit and waiting around here will make the
1530      disconnect wait in vain and cause more problems than we need to. */
1531
1532   /* The SMTP session may or may not have been allocated/setup at this
1533      point! */
1534   if(!dead_connection && smtpc->pp.conn)
1535     if(!smtp_quit(conn))
1536       (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1537
1538   /* Disconnect from the server */
1539   Curl_pp_disconnect(&smtpc->pp);
1540
1541   /* Cleanup the SASL module */
1542   Curl_sasl_cleanup(conn, smtpc->authused);
1543
1544   /* Cleanup our connection based variables */
1545   Curl_safefree(smtpc->domain);
1546
1547   return CURLE_OK;
1548 }
1549
1550 /* Call this when the DO phase has completed */
1551 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1552 {
1553   struct SMTP *smtp = conn->data->state.proto.smtp;
1554
1555   (void)connected;
1556
1557   if(smtp->transfer != FTPTRANSFER_BODY)
1558     /* no data to transfer */
1559     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1560
1561   return CURLE_OK;
1562 }
1563
1564 /* Called from multi.c while DOing */
1565 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1566 {
1567   CURLcode result = smtp_multi_statemach(conn, dophase_done);
1568
1569   if(result)
1570     DEBUGF(infof(conn->data, "DO phase failed\n"));
1571   else if(*dophase_done) {
1572     result = smtp_dophase_done(conn, FALSE /* not connected */);
1573
1574     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1575   }
1576
1577   return result;
1578 }
1579
1580 /***********************************************************************
1581  *
1582  * smtp_regular_transfer()
1583  *
1584  * The input argument is already checked for validity.
1585  *
1586  * Performs all commands done before a regular transfer between a local and a
1587  * remote host.
1588  */
1589 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1590                                       bool *dophase_done)
1591 {
1592   CURLcode result = CURLE_OK;
1593   bool connected = FALSE;
1594   struct SessionHandle *data = conn->data;
1595
1596   /* Make sure size is unknown at this point */
1597   data->req.size = -1;
1598
1599   /* Set the progress data */
1600   Curl_pgrsSetUploadCounter(data, 0);
1601   Curl_pgrsSetDownloadCounter(data, 0);
1602   Curl_pgrsSetUploadSize(data, 0);
1603   Curl_pgrsSetDownloadSize(data, 0);
1604
1605   /* Carry out the perform */
1606   result = smtp_perform(conn, &connected, dophase_done);
1607
1608   /* Perform post DO phase operations if necessary */
1609   if(!result && *dophase_done)
1610     result = smtp_dophase_done(conn, connected);
1611
1612   return result;
1613 }
1614
1615 static CURLcode smtp_setup_connection(struct connectdata *conn)
1616 {
1617   struct SessionHandle *data = conn->data;
1618
1619   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1620     /* Unless we have asked to tunnel SMTP operations through the proxy, we
1621        switch and use HTTP operations only */
1622 #ifndef CURL_DISABLE_HTTP
1623     if(conn->handler == &Curl_handler_smtp)
1624       conn->handler = &Curl_handler_smtp_proxy;
1625     else {
1626 #ifdef USE_SSL
1627       conn->handler = &Curl_handler_smtps_proxy;
1628 #else
1629       failf(data, "SMTPS not supported!");
1630       return CURLE_UNSUPPORTED_PROTOCOL;
1631 #endif
1632     }
1633
1634     /* We explicitly mark this connection as persistent here as we're doing
1635        SMTP over HTTP and thus we accidentally avoid setting this value
1636        otherwise */
1637     conn->bits.close = FALSE;
1638 #else
1639     failf(data, "SMTP over http proxy requires HTTP support built-in!");
1640     return CURLE_UNSUPPORTED_PROTOCOL;
1641 #endif
1642   }
1643
1644   data->state.path++;   /* don't include the initial slash */
1645
1646   return CURLE_OK;
1647 }
1648
1649 /***********************************************************************
1650  *
1651  * smtp_parse_url_options()
1652  *
1653  * Parse the URL login options.
1654  */
1655 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1656 {
1657   CURLcode result = CURLE_OK;
1658   struct smtp_conn *smtpc = &conn->proto.smtpc;
1659   const char *options = conn->options;
1660   const char *ptr = options;
1661
1662   if(options) {
1663     const char *key = ptr;
1664
1665     while(*ptr && *ptr != '=')
1666         ptr++;
1667
1668     if(strnequal(key, "AUTH", 4)) {
1669       const char *value = ptr + 1;
1670
1671       if(strequal(value, "*"))
1672         smtpc->prefmech = SASL_AUTH_ANY;
1673       else if(strequal(value, "LOGIN"))
1674         smtpc->prefmech = SASL_MECH_LOGIN;
1675       else if(strequal(value, "PLAIN"))
1676         smtpc->prefmech = SASL_MECH_PLAIN;
1677       else if(strequal(value, "CRAM-MD5"))
1678         smtpc->prefmech = SASL_MECH_CRAM_MD5;
1679       else if(strequal(value, "DIGEST-MD5"))
1680         smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1681       else if(strequal(value, "GSSAPI"))
1682         smtpc->prefmech = SASL_MECH_GSSAPI;
1683       else if(strequal(value, "NTLM"))
1684         smtpc->prefmech = SASL_MECH_NTLM;
1685       else
1686         smtpc->prefmech = SASL_AUTH_NONE;
1687     }
1688     else
1689       result = CURLE_URL_MALFORMAT;
1690   }
1691
1692   return result;
1693 }
1694
1695 /***********************************************************************
1696  *
1697  * smtp_parse_url_path()
1698  *
1699  * Parse the URL path into separate path components.
1700  */
1701 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1702 {
1703   /* The SMTP struct is already initialised in smtp_connect() */
1704   struct SessionHandle *data = conn->data;
1705   struct smtp_conn *smtpc = &conn->proto.smtpc;
1706   const char *path = data->state.path;
1707   char localhost[HOSTNAME_MAX + 1];
1708
1709   /* Calculate the path if necessary */
1710   if(!*path) {
1711     if(!Curl_gethostname(localhost, sizeof(localhost)))
1712       path = localhost;
1713     else
1714       path = "localhost";
1715   }
1716
1717   /* URL decode the path and use it as the domain in our EHLO */
1718   return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1719 }
1720
1721 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1722 {
1723   /* When sending a SMTP payload we must detect CRLF. sequences making sure
1724      they are sent as CRLF.. instead, as a . on the beginning of a line will
1725      be deleted by the server when not part of an EOB terminator and a
1726      genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1727      data by the server.
1728   */
1729   ssize_t i;
1730   ssize_t si;
1731   struct smtp_conn *smtpc = &conn->proto.smtpc;
1732   struct SessionHandle *data = conn->data;
1733
1734   /* Do we need to allocate the scatch buffer? */
1735   if(!data->state.scratch) {
1736     data->state.scratch = malloc(2 * BUFSIZE);
1737
1738     if(!data->state.scratch) {
1739       failf (data, "Failed to alloc scratch buffer!");
1740       return CURLE_OUT_OF_MEMORY;
1741     }
1742   }
1743
1744   /* This loop can be improved by some kind of Boyer-Moore style of
1745      approach but that is saved for later... */
1746   for(i = 0, si = 0; i < nread; i++) {
1747     if(SMTP_EOB[smtpc->eob] == data->req.upload_fromhere[i])
1748       smtpc->eob++;
1749     else if(smtpc->eob) {
1750       /* A previous substring matched so output that first */
1751       memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1752       si += smtpc->eob;
1753
1754       /* Then compare the first byte */
1755       if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1756         smtpc->eob = 1;
1757       else
1758         smtpc->eob = 0;
1759     }
1760
1761     /* Do we have a match for CRLF. as per RFC-2821, sect. 4.5.2 */
1762     if(SMTP_EOB_FIND_LEN == smtpc->eob) {
1763       /* Copy the replacement data to the target buffer */
1764       memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
1765       si += SMTP_EOB_REPL_LEN;
1766       smtpc->eob = 0;
1767     }
1768     else if(!smtpc->eob)
1769       data->state.scratch[si++] = data->req.upload_fromhere[i];
1770   }
1771
1772   if(smtpc->eob) {
1773     /* A substring matched before processing ended so output that now */
1774     memcpy(&data->state.scratch[si], SMTP_EOB, smtpc->eob);
1775     si += smtpc->eob;
1776     smtpc->eob = 0;
1777   }
1778
1779   if(si != nread) {
1780     /* Only use the new buffer if we replaced something */
1781     nread = si;
1782
1783     /* Upload from the new (replaced) buffer instead */
1784     data->req.upload_fromhere = data->state.scratch;
1785
1786     /* Set the new amount too */
1787     data->req.upload_present = nread;
1788   }
1789
1790   return CURLE_OK;
1791 }
1792
1793 #endif /* CURL_DISABLE_SMTP */