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