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