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