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