smtp: fix compiler warning
[platform/upstream/curl.git] / lib / smtp.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC1870 SMTP Service Extension for Message Size
22  * RFC2195 CRAM-MD5 authentication
23  * RFC2831 DIGEST-MD5 authentication
24  * RFC3207 SMTP over TLS
25  * RFC4422 Simple Authentication and Security Layer (SASL)
26  * RFC4616 PLAIN authentication
27  * RFC4954 SMTP Authentication
28  * RFC5321 SMTP protocol
29  * RFC6749 OAuth 2.0 Authorization Framework
30  * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
31  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
32  *
33  ***************************************************************************/
34
35 #include "curl_setup.h"
36
37 #ifndef CURL_DISABLE_SMTP
38
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "if2ip.h"
65 #include "hostip.h"
66 #include "progress.h"
67 #include "transfer.h"
68 #include "escape.h"
69 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "socks.h"
71 #include "smtp.h"
72
73 #include "strtoofft.h"
74 #include "strequal.h"
75 #include "sslgen.h"
76 #include "connect.h"
77 #include "strerror.h"
78 #include "select.h"
79 #include "multiif.h"
80 #include "url.h"
81 #include "rawstr.h"
82 #include "curl_gethostname.h"
83 #include "curl_sasl.h"
84 #include "warnless.h"
85
86 #define _MPRINTF_REPLACE /* use our functions only */
87 #include <curl/mprintf.h>
88
89 #include "curl_memory.h"
90 /* The last #include file should be: */
91 #include "memdebug.h"
92
93 /* Local API functions */
94 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
95 static CURLcode smtp_do(struct connectdata *conn, bool *done);
96 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
97                           bool premature);
98 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
99 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
100 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
101 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
102                         int numsocks);
103 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
104 static CURLcode smtp_setup_connection(struct connectdata *conn);
105 static CURLcode smtp_parse_url_options(struct connectdata *conn);
106 static CURLcode smtp_parse_url_path(struct connectdata *conn);
107 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   struct SessionHandle *data = conn->data;
1193
1194   (void)smtpcode;
1195   (void)instate; /* no use for this yet */
1196
1197   failf(data, "Authentication cancelled");
1198
1199   return CURLE_LOGIN_DENIED;
1200 }
1201
1202 /* For final responses in the AUTH sequence */
1203 static CURLcode smtp_state_auth_final_resp(struct connectdata *conn,
1204                                            int smtpcode,
1205                                            smtpstate instate)
1206 {
1207   CURLcode result = CURLE_OK;
1208   struct SessionHandle *data = conn->data;
1209
1210   (void)instate; /* no use for this yet */
1211
1212   if(smtpcode != 235) {
1213     failf(data, "Authentication failed: %d", smtpcode);
1214     result = CURLE_LOGIN_DENIED;
1215   }
1216   else
1217     /* End of connect phase */
1218     state(conn, SMTP_STOP);
1219
1220   return result;
1221 }
1222
1223 /* For command responses */
1224 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
1225                                         smtpstate instate)
1226 {
1227   CURLcode result = CURLE_OK;
1228   struct SessionHandle *data = conn->data;
1229   struct SMTP *smtp = data->req.protop;
1230   char *line = data->state.buffer;
1231   size_t len = strlen(line);
1232
1233   (void)instate; /* no use for this yet */
1234
1235   if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
1236      (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
1237     failf(data, "Command failed: %d", smtpcode);
1238     result = CURLE_RECV_ERROR;
1239   }
1240   else {
1241     /* Temporarily add the LF character back and send as body to the client */
1242     if(!data->set.opt_no_body) {
1243       line[len] = '\n';
1244       result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
1245       line[len] = '\0';
1246     }
1247
1248     if(smtpcode != 1) {
1249       if(smtp->rcpt) {
1250         smtp->rcpt = smtp->rcpt->next;
1251
1252         if(smtp->rcpt) {
1253           /* Send the next command */
1254           result = smtp_perform_command(conn);
1255         }
1256         else
1257           /* End of DO phase */
1258           state(conn, SMTP_STOP);
1259       }
1260       else
1261         /* End of DO phase */
1262         state(conn, SMTP_STOP);
1263     }
1264   }
1265
1266   return result;
1267 }
1268
1269 /* For MAIL responses */
1270 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
1271                                      smtpstate instate)
1272 {
1273   CURLcode result = CURLE_OK;
1274   struct SessionHandle *data = conn->data;
1275
1276   (void)instate; /* no use for this yet */
1277
1278   if(smtpcode/100 != 2) {
1279     failf(data, "MAIL failed: %d", smtpcode);
1280     result = CURLE_SEND_ERROR;
1281   }
1282   else
1283     /* Start the RCPT TO command */
1284     result = smtp_perform_rcpt_to(conn);
1285
1286   return result;
1287 }
1288
1289 /* For RCPT responses */
1290 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
1291                                      smtpstate instate)
1292 {
1293   CURLcode result = CURLE_OK;
1294   struct SessionHandle *data = conn->data;
1295   struct SMTP *smtp = data->req.protop;
1296
1297   (void)instate; /* no use for this yet */
1298
1299   if(smtpcode/100 != 2) {
1300     failf(data, "RCPT failed: %d", smtpcode);
1301     result = CURLE_SEND_ERROR;
1302   }
1303   else {
1304     smtp->rcpt = smtp->rcpt->next;
1305
1306     if(smtp->rcpt)
1307       /* Send the next RCPT TO command */
1308       result = smtp_perform_rcpt_to(conn);
1309     else {
1310       /* Send the DATA command */
1311       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
1312
1313       if(!result)
1314         state(conn, SMTP_DATA);
1315     }
1316   }
1317
1318   return result;
1319 }
1320
1321 /* For DATA response */
1322 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
1323                                      smtpstate instate)
1324 {
1325   CURLcode result = CURLE_OK;
1326   struct SessionHandle *data = conn->data;
1327
1328   (void)instate; /* no use for this yet */
1329
1330   if(smtpcode != 354) {
1331     failf(data, "DATA failed: %d", smtpcode);
1332     result = CURLE_SEND_ERROR;
1333   }
1334   else {
1335     /* Set the progress upload size */
1336     Curl_pgrsSetUploadSize(data, data->set.infilesize);
1337
1338     /* SMTP upload */
1339     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
1340
1341     /* End of DO phase */
1342     state(conn, SMTP_STOP);
1343   }
1344
1345   return result;
1346 }
1347
1348 /* For POSTDATA responses, which are received after the entire DATA
1349    part has been sent to the server */
1350 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
1351                                          int smtpcode,
1352                                          smtpstate instate)
1353 {
1354   CURLcode result = CURLE_OK;
1355
1356   (void)instate; /* no use for this yet */
1357
1358   if(smtpcode != 250)
1359     result = CURLE_RECV_ERROR;
1360
1361   /* End of DONE phase */
1362   state(conn, SMTP_STOP);
1363
1364   return result;
1365 }
1366
1367 static CURLcode smtp_statemach_act(struct connectdata *conn)
1368 {
1369   CURLcode result = CURLE_OK;
1370   curl_socket_t sock = conn->sock[FIRSTSOCKET];
1371   struct SessionHandle *data = conn->data;
1372   int smtpcode;
1373   struct smtp_conn *smtpc = &conn->proto.smtpc;
1374   struct pingpong *pp = &smtpc->pp;
1375   size_t nread = 0;
1376
1377   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
1378   if(smtpc->state == SMTP_UPGRADETLS)
1379     return smtp_perform_upgrade_tls(conn);
1380
1381   /* Flush any data that needs to be sent */
1382   if(pp->sendleft)
1383     return Curl_pp_flushsend(pp);
1384
1385   do {
1386     /* Read the response from the server */
1387     result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1388     if(result)
1389       return result;
1390
1391     /* Store the latest response for later retrieval if necessary */
1392     if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1393       data->info.httpcode = smtpcode;
1394
1395     if(!smtpcode)
1396       break;
1397
1398     /* We have now received a full SMTP server response */
1399     switch(smtpc->state) {
1400     case SMTP_SERVERGREET:
1401       result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1402       break;
1403
1404     case SMTP_EHLO:
1405       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1406       break;
1407
1408     case SMTP_HELO:
1409       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1410       break;
1411
1412     case SMTP_STARTTLS:
1413       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1414       break;
1415
1416     case SMTP_AUTH_PLAIN:
1417       result = smtp_state_auth_plain_resp(conn, smtpcode, smtpc->state);
1418       break;
1419
1420     case SMTP_AUTH_LOGIN:
1421       result = smtp_state_auth_login_resp(conn, smtpcode, smtpc->state);
1422       break;
1423
1424     case SMTP_AUTH_LOGIN_PASSWD:
1425       result = smtp_state_auth_login_password_resp(conn, smtpcode,
1426                                                    smtpc->state);
1427       break;
1428
1429 #ifndef CURL_DISABLE_CRYPTO_AUTH
1430     case SMTP_AUTH_CRAMMD5:
1431       result = smtp_state_auth_cram_resp(conn, smtpcode, smtpc->state);
1432       break;
1433
1434     case SMTP_AUTH_DIGESTMD5:
1435       result = smtp_state_auth_digest_resp(conn, smtpcode, smtpc->state);
1436       break;
1437
1438     case SMTP_AUTH_DIGESTMD5_RESP:
1439       result = smtp_state_auth_digest_resp_resp(conn, smtpcode, smtpc->state);
1440       break;
1441 #endif
1442
1443 #ifdef USE_NTLM
1444     case SMTP_AUTH_NTLM:
1445       result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
1446       break;
1447
1448     case SMTP_AUTH_NTLM_TYPE2MSG:
1449       result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode,
1450                                                   smtpc->state);
1451       break;
1452 #endif
1453
1454     case SMTP_AUTH_XOAUTH2:
1455       result = smtp_state_auth_xoauth2_resp(conn, smtpcode, smtpc->state);
1456       break;
1457
1458     case SMTP_AUTH_CANCEL:
1459       result = smtp_state_auth_cancel_resp(conn, smtpcode, smtpc->state);
1460       break;
1461
1462     case SMTP_AUTH_FINAL:
1463       result = smtp_state_auth_final_resp(conn, smtpcode, smtpc->state);
1464       break;
1465
1466     case SMTP_COMMAND:
1467       result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1468       break;
1469
1470     case SMTP_MAIL:
1471       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1472       break;
1473
1474     case SMTP_RCPT:
1475       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1476       break;
1477
1478     case SMTP_DATA:
1479       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1480       break;
1481
1482     case SMTP_POSTDATA:
1483       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1484       break;
1485
1486     case SMTP_QUIT:
1487       /* fallthrough, just stop! */
1488     default:
1489       /* internal error */
1490       state(conn, SMTP_STOP);
1491       break;
1492     }
1493   } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1494
1495   return result;
1496 }
1497
1498 /* Called repeatedly until done from multi.c */
1499 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1500 {
1501   CURLcode result = CURLE_OK;
1502   struct smtp_conn *smtpc = &conn->proto.smtpc;
1503
1504   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1505     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1506     if(result || !smtpc->ssldone)
1507       return result;
1508   }
1509
1510   result = Curl_pp_statemach(&smtpc->pp, FALSE);
1511   *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1512
1513   return result;
1514 }
1515
1516 static CURLcode smtp_block_statemach(struct connectdata *conn)
1517 {
1518   CURLcode result = CURLE_OK;
1519   struct smtp_conn *smtpc = &conn->proto.smtpc;
1520
1521   while(smtpc->state != SMTP_STOP && !result)
1522     result = Curl_pp_statemach(&smtpc->pp, TRUE);
1523
1524   return result;
1525 }
1526
1527 /* Allocate and initialize the SMTP struct for the current SessionHandle if
1528    required */
1529 static CURLcode smtp_init(struct connectdata *conn)
1530 {
1531   CURLcode result = CURLE_OK;
1532   struct SessionHandle *data = conn->data;
1533   struct SMTP *smtp;
1534
1535   smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1536   if(!smtp)
1537     result = CURLE_OUT_OF_MEMORY;
1538
1539   return result;
1540 }
1541
1542 /* For the SMTP "protocol connect" and "doing" phases only */
1543 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1544                         int numsocks)
1545 {
1546   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1547 }
1548
1549 /***********************************************************************
1550  *
1551  * smtp_connect()
1552  *
1553  * This function should do everything that is to be considered a part of
1554  * the connection phase.
1555  *
1556  * The variable pointed to by 'done' will be TRUE if the protocol-layer
1557  * connect phase is done when this function returns, or FALSE if not.
1558  */
1559 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1560 {
1561   CURLcode result = CURLE_OK;
1562   struct smtp_conn *smtpc = &conn->proto.smtpc;
1563   struct pingpong *pp = &smtpc->pp;
1564
1565   *done = FALSE; /* default to not done yet */
1566
1567   /* We always support persistent connections in SMTP */
1568   conn->bits.close = FALSE;
1569
1570   /* Set the default response time-out */
1571   pp->response_time = RESP_TIMEOUT;
1572   pp->statemach_act = smtp_statemach_act;
1573   pp->endofresp = smtp_endofresp;
1574   pp->conn = conn;
1575
1576   /* Set the default preferred authentication mechanism */
1577   smtpc->prefmech = SASL_AUTH_ANY;
1578
1579   /* Initialise the pingpong layer */
1580   Curl_pp_init(pp);
1581
1582   /* Parse the URL options */
1583   result = smtp_parse_url_options(conn);
1584   if(result)
1585     return result;
1586
1587   /* Parse the URL path */
1588   result = smtp_parse_url_path(conn);
1589   if(result)
1590     return result;
1591
1592   /* Start off waiting for the server greeting response */
1593   state(conn, SMTP_SERVERGREET);
1594
1595   result = smtp_multi_statemach(conn, done);
1596
1597   return result;
1598 }
1599
1600 /***********************************************************************
1601  *
1602  * smtp_done()
1603  *
1604  * The DONE function. This does what needs to be done after a single DO has
1605  * performed.
1606  *
1607  * Input argument is already checked for validity.
1608  */
1609 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1610                           bool premature)
1611 {
1612   CURLcode result = CURLE_OK;
1613   struct SessionHandle *data = conn->data;
1614   struct SMTP *smtp = data->req.protop;
1615   struct pingpong *pp = &conn->proto.smtpc.pp;
1616   const char *eob;
1617   ssize_t len;
1618   ssize_t bytes_written;
1619
1620   (void)premature;
1621
1622   if(!smtp)
1623     /* When the easy handle is removed from the multi interface while libcurl
1624        is still trying to resolve the host name, the SMTP struct is not yet
1625        initialized. However, the removal action calls Curl_done() which in
1626        turn calls this function, so we simply return success. */
1627     return CURLE_OK;
1628
1629   if(status) {
1630     conn->bits.close = TRUE; /* marked for closure */
1631     result = status;         /* use the already set error code */
1632   }
1633   else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
1634     /* Calculate the EOB taking into account any terminating CRLF from the
1635        previous line of the email or the CRLF of the DATA command when there
1636        is "no mail data". RFC-5321, sect. 4.1.1.4. */
1637     eob = SMTP_EOB;
1638     len = SMTP_EOB_LEN;
1639     if(smtp->trailing_crlf || !conn->data->set.infilesize) {
1640       eob += 2;
1641       len -= 2;
1642     }
1643
1644     /* Send the end of block data */
1645     result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1646     if(result)
1647       return result;
1648
1649     if(bytes_written != len) {
1650       /* The whole chunk was not sent so keep it around and adjust the
1651          pingpong structure accordingly */
1652       pp->sendthis = strdup(eob);
1653       pp->sendsize = len;
1654       pp->sendleft = len - bytes_written;
1655     }
1656     else
1657       /* Successfully sent so adjust the response timeout relative to now */
1658       pp->response = Curl_tvnow();
1659
1660     state(conn, SMTP_POSTDATA);
1661
1662     /* Run the state-machine
1663
1664        TODO: when the multi interface is used, this _really_ should be using
1665        the smtp_multi_statemach function but we have no general support for
1666        non-blocking DONE operations, not in the multi state machine and with
1667        Curl_done() invokes on several places in the code!
1668     */
1669     result = smtp_block_statemach(conn);
1670   }
1671
1672   /* Cleanup our per-request based variables */
1673   Curl_safefree(smtp->custom);
1674
1675   /* Clear the transfer mode for the next request */
1676   smtp->transfer = FTPTRANSFER_BODY;
1677
1678   return result;
1679 }
1680
1681 /***********************************************************************
1682  *
1683  * smtp_perform()
1684  *
1685  * This is the actual DO function for SMTP. Transfer a mail, send a command
1686  * or get some data according to the options previously setup.
1687  */
1688 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1689                              bool *dophase_done)
1690 {
1691   /* This is SMTP and no proxy */
1692   CURLcode result = CURLE_OK;
1693   struct SessionHandle *data = conn->data;
1694   struct SMTP *smtp = data->req.protop;
1695
1696   DEBUGF(infof(conn->data, "DO phase starts\n"));
1697
1698   if(data->set.opt_no_body) {
1699     /* Requested no body means no transfer */
1700     smtp->transfer = FTPTRANSFER_INFO;
1701   }
1702
1703   *dophase_done = FALSE; /* not done yet */
1704
1705   /* Store the first recipient (or NULL if not specified) */
1706   smtp->rcpt = data->set.mail_rcpt;
1707
1708   /* Start the first command in the DO phase */
1709   if(data->set.upload && data->set.mail_rcpt)
1710     /* MAIL transfer */
1711     result = smtp_perform_mail(conn);
1712   else
1713     /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1714     result = smtp_perform_command(conn);
1715
1716   if(result)
1717     return result;
1718
1719   /* Run the state-machine */
1720   result = smtp_multi_statemach(conn, dophase_done);
1721
1722   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1723
1724   if(*dophase_done)
1725     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1726
1727   return result;
1728 }
1729
1730 /***********************************************************************
1731  *
1732  * smtp_do()
1733  *
1734  * This function is registered as 'curl_do' function. It decodes the path
1735  * parts etc as a wrapper to the actual DO function (smtp_perform).
1736  *
1737  * The input argument is already checked for validity.
1738  */
1739 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1740 {
1741   CURLcode result = CURLE_OK;
1742
1743   *done = FALSE; /* default to false */
1744
1745   /* Parse the custom request */
1746   result = smtp_parse_custom_request(conn);
1747   if(result)
1748     return result;
1749
1750   result = smtp_regular_transfer(conn, done);
1751
1752   return result;
1753 }
1754
1755 /***********************************************************************
1756  *
1757  * smtp_disconnect()
1758  *
1759  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1760  * resources. BLOCKING.
1761  */
1762 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1763 {
1764   struct smtp_conn *smtpc = &conn->proto.smtpc;
1765
1766   /* We cannot send quit unconditionally. If this connection is stale or
1767      bad in any way, sending quit and waiting around here will make the
1768      disconnect wait in vain and cause more problems than we need to. */
1769
1770   /* The SMTP session may or may not have been allocated/setup at this
1771      point! */
1772   if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1773     if(!smtp_perform_quit(conn))
1774       (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1775
1776   /* Disconnect from the server */
1777   Curl_pp_disconnect(&smtpc->pp);
1778
1779   /* Cleanup the SASL module */
1780   Curl_sasl_cleanup(conn, smtpc->authused);
1781
1782   /* Cleanup our connection based variables */
1783   Curl_safefree(smtpc->domain);
1784
1785   return CURLE_OK;
1786 }
1787
1788 /* Call this when the DO phase has completed */
1789 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1790 {
1791   struct SMTP *smtp = conn->data->req.protop;
1792
1793   (void)connected;
1794
1795   if(smtp->transfer != FTPTRANSFER_BODY)
1796     /* no data to transfer */
1797     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1798
1799   return CURLE_OK;
1800 }
1801
1802 /* Called from multi.c while DOing */
1803 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1804 {
1805   CURLcode result = smtp_multi_statemach(conn, dophase_done);
1806
1807   if(result)
1808     DEBUGF(infof(conn->data, "DO phase failed\n"));
1809   else if(*dophase_done) {
1810     result = smtp_dophase_done(conn, FALSE /* not connected */);
1811
1812     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1813   }
1814
1815   return result;
1816 }
1817
1818 /***********************************************************************
1819  *
1820  * smtp_regular_transfer()
1821  *
1822  * The input argument is already checked for validity.
1823  *
1824  * Performs all commands done before a regular transfer between a local and a
1825  * remote host.
1826  */
1827 static CURLcode smtp_regular_transfer(struct connectdata *conn,
1828                                       bool *dophase_done)
1829 {
1830   CURLcode result = CURLE_OK;
1831   bool connected = FALSE;
1832   struct SessionHandle *data = conn->data;
1833
1834   /* Make sure size is unknown at this point */
1835   data->req.size = -1;
1836
1837   /* Set the progress data */
1838   Curl_pgrsSetUploadCounter(data, 0);
1839   Curl_pgrsSetDownloadCounter(data, 0);
1840   Curl_pgrsSetUploadSize(data, 0);
1841   Curl_pgrsSetDownloadSize(data, 0);
1842
1843   /* Carry out the perform */
1844   result = smtp_perform(conn, &connected, dophase_done);
1845
1846   /* Perform post DO phase operations if necessary */
1847   if(!result && *dophase_done)
1848     result = smtp_dophase_done(conn, connected);
1849
1850   return result;
1851 }
1852
1853 static CURLcode smtp_setup_connection(struct connectdata *conn)
1854 {
1855   struct SessionHandle *data = conn->data;
1856   CURLcode result;
1857
1858   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1859     /* Unless we have asked to tunnel SMTP operations through the proxy, we
1860        switch and use HTTP operations only */
1861 #ifndef CURL_DISABLE_HTTP
1862     if(conn->handler == &Curl_handler_smtp)
1863       conn->handler = &Curl_handler_smtp_proxy;
1864     else {
1865 #ifdef USE_SSL
1866       conn->handler = &Curl_handler_smtps_proxy;
1867 #else
1868       failf(data, "SMTPS not supported!");
1869       return CURLE_UNSUPPORTED_PROTOCOL;
1870 #endif
1871     }
1872     /* set it up as a HTTP connection instead */
1873     return conn->handler->setup_connection(conn);
1874
1875 #else
1876     failf(data, "SMTP over http proxy requires HTTP support built-in!");
1877     return CURLE_UNSUPPORTED_PROTOCOL;
1878 #endif
1879   }
1880
1881   /* Initialise the SMTP layer */
1882   result = smtp_init(conn);
1883   if(result)
1884     return result;
1885
1886   data->state.path++;   /* don't include the initial slash */
1887
1888   return CURLE_OK;
1889 }
1890
1891 /***********************************************************************
1892  *
1893  * smtp_parse_url_options()
1894  *
1895  * Parse the URL login options.
1896  */
1897 static CURLcode smtp_parse_url_options(struct connectdata *conn)
1898 {
1899   CURLcode result = CURLE_OK;
1900   struct smtp_conn *smtpc = &conn->proto.smtpc;
1901   const char *options = conn->options;
1902   const char *ptr = options;
1903
1904   if(options) {
1905     const char *key = ptr;
1906
1907     while(*ptr && *ptr != '=')
1908         ptr++;
1909
1910     if(strnequal(key, "AUTH", 4)) {
1911       const char *value = ptr + 1;
1912
1913       if(strequal(value, "*"))
1914         smtpc->prefmech = SASL_AUTH_ANY;
1915       else if(strequal(value, SASL_MECH_STRING_LOGIN))
1916         smtpc->prefmech = SASL_MECH_LOGIN;
1917       else if(strequal(value, SASL_MECH_STRING_PLAIN))
1918         smtpc->prefmech = SASL_MECH_PLAIN;
1919       else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
1920         smtpc->prefmech = SASL_MECH_CRAM_MD5;
1921       else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
1922         smtpc->prefmech = SASL_MECH_DIGEST_MD5;
1923       else if(strequal(value, SASL_MECH_STRING_GSSAPI))
1924         smtpc->prefmech = SASL_MECH_GSSAPI;
1925       else if(strequal(value, SASL_MECH_STRING_NTLM))
1926         smtpc->prefmech = SASL_MECH_NTLM;
1927       else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
1928         smtpc->prefmech = SASL_MECH_XOAUTH2;
1929       else
1930         smtpc->prefmech = SASL_AUTH_NONE;
1931     }
1932     else
1933       result = CURLE_URL_MALFORMAT;
1934   }
1935
1936   return result;
1937 }
1938
1939 /***********************************************************************
1940  *
1941  * smtp_parse_url_path()
1942  *
1943  * Parse the URL path into separate path components.
1944  */
1945 static CURLcode smtp_parse_url_path(struct connectdata *conn)
1946 {
1947   /* The SMTP struct is already initialised in smtp_connect() */
1948   struct SessionHandle *data = conn->data;
1949   struct smtp_conn *smtpc = &conn->proto.smtpc;
1950   const char *path = data->state.path;
1951   char localhost[HOSTNAME_MAX + 1];
1952
1953   /* Calculate the path if necessary */
1954   if(!*path) {
1955     if(!Curl_gethostname(localhost, sizeof(localhost)))
1956       path = localhost;
1957     else
1958       path = "localhost";
1959   }
1960
1961   /* URL decode the path and use it as the domain in our EHLO */
1962   return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1963 }
1964
1965 /***********************************************************************
1966  *
1967  * smtp_parse_custom_request()
1968  *
1969  * Parse the custom request.
1970  */
1971 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1972 {
1973   CURLcode result = CURLE_OK;
1974   struct SessionHandle *data = conn->data;
1975   struct SMTP *smtp = data->req.protop;
1976   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1977
1978   /* URL decode the custom request */
1979   if(custom)
1980     result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1981
1982   return result;
1983 }
1984
1985 /***********************************************************************
1986  *
1987  * smtp_calc_sasl_details()
1988  *
1989  * Calculate the required login details for SASL authentication.
1990  */
1991 static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
1992                                        const char **mech,
1993                                        char **initresp, size_t *len,
1994                                        smtpstate *state1, smtpstate *state2)
1995 {
1996   CURLcode result = CURLE_OK;
1997   struct SessionHandle *data = conn->data;
1998   struct smtp_conn *smtpc = &conn->proto.smtpc;
1999
2000   /* Calculate the supported authentication mechanism, by decreasing order of
2001      security, as well as the initial response where appropriate */
2002 #ifndef CURL_DISABLE_CRYPTO_AUTH
2003   if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
2004      (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
2005     *mech = SASL_MECH_STRING_DIGEST_MD5;
2006     *state1 = SMTP_AUTH_DIGESTMD5;
2007     smtpc->authused = SASL_MECH_DIGEST_MD5;
2008   }
2009   else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
2010           (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
2011     *mech = SASL_MECH_STRING_CRAM_MD5;
2012     *state1 = SMTP_AUTH_CRAMMD5;
2013     smtpc->authused = SASL_MECH_CRAM_MD5;
2014   }
2015   else
2016 #endif
2017 #ifdef USE_NTLM
2018   if((smtpc->authmechs & SASL_MECH_NTLM) &&
2019      (smtpc->prefmech & SASL_MECH_NTLM)) {
2020     *mech = SASL_MECH_STRING_NTLM;
2021     *state1 = SMTP_AUTH_NTLM;
2022     *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
2023     smtpc->authused = SASL_MECH_NTLM;
2024
2025     if(data->set.sasl_ir)
2026       result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
2027                                                    &conn->ntlm,
2028                                                    initresp, len);
2029     }
2030   else
2031 #endif
2032   if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
2033       (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
2034       (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
2035     *mech = SASL_MECH_STRING_XOAUTH2;
2036     *state1 = SMTP_AUTH_XOAUTH2;
2037     *state2 = SMTP_AUTH_FINAL;
2038     smtpc->authused = SASL_MECH_XOAUTH2;
2039
2040     if(data->set.sasl_ir)
2041       result = Curl_sasl_create_xoauth2_message(data, conn->user,
2042                                                 conn->xoauth2_bearer,
2043                                                 initresp, len);
2044   }
2045   else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
2046           (smtpc->prefmech & SASL_MECH_LOGIN)) {
2047     *mech = SASL_MECH_STRING_LOGIN;
2048     *state1 = SMTP_AUTH_LOGIN;
2049     *state2 = SMTP_AUTH_LOGIN_PASSWD;
2050     smtpc->authused = SASL_MECH_LOGIN;
2051
2052     if(data->set.sasl_ir)
2053       result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
2054   }
2055   else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
2056           (smtpc->prefmech & SASL_MECH_PLAIN)) {
2057     *mech = SASL_MECH_STRING_PLAIN;
2058     *state1 = SMTP_AUTH_PLAIN;
2059     *state2 = SMTP_AUTH_FINAL;
2060     smtpc->authused = SASL_MECH_PLAIN;
2061
2062     if(data->set.sasl_ir)
2063       result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
2064                                               initresp, len);
2065   }
2066
2067   return result;
2068 }
2069
2070 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
2071 {
2072   /* When sending a SMTP payload we must detect CRLF. sequences making sure
2073      they are sent as CRLF.. instead, as a . on the beginning of a line will
2074      be deleted by the server when not part of an EOB terminator and a
2075      genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
2076      data by the server
2077   */
2078   ssize_t i;
2079   ssize_t si;
2080   struct SessionHandle *data = conn->data;
2081   struct SMTP *smtp = data->req.protop;
2082
2083   /* Do we need to allocate the scatch buffer? */
2084   if(!data->state.scratch) {
2085     data->state.scratch = malloc(2 * BUFSIZE);
2086
2087     if(!data->state.scratch) {
2088       failf (data, "Failed to alloc scratch buffer!");
2089       return CURLE_OUT_OF_MEMORY;
2090     }
2091   }
2092
2093   /* This loop can be improved by some kind of Boyer-Moore style of
2094      approach but that is saved for later... */
2095   for(i = 0, si = 0; i < nread; i++) {
2096     if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
2097       smtp->eob++;
2098
2099       /* Is the EOB potentially the terminating CRLF? */
2100       if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
2101         smtp->trailing_crlf = TRUE;
2102       else
2103         smtp->trailing_crlf = FALSE;
2104     }
2105     else if(smtp->eob) {
2106       /* A previous substring matched so output that first */
2107       memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2108       si += smtp->eob;
2109
2110       /* Then compare the first byte */
2111       if(SMTP_EOB[0] == data->req.upload_fromhere[i])
2112         smtp->eob = 1;
2113       else
2114         smtp->eob = 0;
2115
2116       /* Reset the trailing CRLF flag as there was more data */
2117       smtp->trailing_crlf = FALSE;
2118     }
2119
2120     /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
2121     if(SMTP_EOB_FIND_LEN == smtp->eob) {
2122       /* Copy the replacement data to the target buffer */
2123       memcpy(&data->state.scratch[si], SMTP_EOB_REPL, SMTP_EOB_REPL_LEN);
2124       si += SMTP_EOB_REPL_LEN;
2125       smtp->eob = 0;
2126     }
2127     else if(!smtp->eob)
2128       data->state.scratch[si++] = data->req.upload_fromhere[i];
2129   }
2130
2131   if(smtp->eob) {
2132     /* A substring matched before processing ended so output that now */
2133     memcpy(&data->state.scratch[si], SMTP_EOB, smtp->eob);
2134     si += smtp->eob;
2135     smtp->eob = 0;
2136   }
2137
2138   if(si != nread) {
2139     /* Only use the new buffer if we replaced something */
2140     nread = si;
2141
2142     /* Upload from the new (replaced) buffer instead */
2143     data->req.upload_fromhere = data->state.scratch;
2144
2145     /* Set the new amount too */
2146     data->req.upload_present = nread;
2147   }
2148
2149   return CURLE_OK;
2150 }
2151
2152 #endif /* CURL_DISABLE_SMTP */