82621a42b7c86c952bfa893a007ee905d461f490
[external/curl.git] / lib / smtp.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, 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  * RFC2821 SMTP protocol
22  * RFC3207 SMTP over TLS
23  * RFC4954 SMTP Authentication
24  * RFC2195 CRAM-MD5 authentication
25  * RFC4616 PLAIN authentication
26  *
27  ***************************************************************************/
28
29 #include "setup.h"
30
31 #ifndef CURL_DISABLE_SMTP
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <ctype.h>
37
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51 #ifdef HAVE_UTSNAME_H
52 #include <sys/utsname.h>
53 #endif
54 #ifdef HAVE_NETDB_H
55 #include <netdb.h>
56 #endif
57 #ifdef __VMS
58 #include <in.h>
59 #include <inet.h>
60 #endif
61
62 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
63 #undef in_addr_t
64 #define in_addr_t unsigned long
65 #endif
66
67 #include <curl/curl.h>
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "easyif.h" /* for Curl_convert_... prototypes */
71
72 #include "if2ip.h"
73 #include "hostip.h"
74 #include "progress.h"
75 #include "transfer.h"
76 #include "escape.h"
77 #include "http.h" /* for HTTP proxy tunnel stuff */
78 #include "socks.h"
79 #include "smtp.h"
80
81 #include "strtoofft.h"
82 #include "strequal.h"
83 #include "sslgen.h"
84 #include "connect.h"
85 #include "strerror.h"
86 #include "select.h"
87 #include "multiif.h"
88 #include "url.h"
89 #include "rawstr.h"
90 #include "strtoofft.h"
91 #include "curl_base64.h"
92 #include "curl_md5.h"
93 #include "curl_hmac.h"
94 #include "curl_gethostname.h"
95 #include "warnless.h"
96
97 #define _MPRINTF_REPLACE /* use our functions only */
98 #include <curl/mprintf.h>
99
100 #include "curl_memory.h"
101 /* The last #include file should be: */
102 #include "memdebug.h"
103
104 /* Local API functions */
105 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
106 static CURLcode smtp_do(struct connectdata *conn, bool *done);
107 static CURLcode smtp_done(struct connectdata *conn,
108                           CURLcode, bool premature);
109 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
110 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection);
111 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
112 static int smtp_getsock(struct connectdata *conn,
113                         curl_socket_t *socks,
114                         int numsocks);
115 static CURLcode smtp_doing(struct connectdata *conn,
116                            bool *dophase_done);
117 static CURLcode smtp_setup_connection(struct connectdata * conn);
118
119
120 /*
121  * SMTP protocol handler.
122  */
123
124 const struct Curl_handler Curl_handler_smtp = {
125   "SMTP",                           /* scheme */
126   smtp_setup_connection,            /* setup_connection */
127   smtp_do,                          /* do_it */
128   smtp_done,                        /* done */
129   ZERO_NULL,                        /* do_more */
130   smtp_connect,                     /* connect_it */
131   smtp_multi_statemach,             /* connecting */
132   smtp_doing,                       /* doing */
133   smtp_getsock,                     /* proto_getsock */
134   smtp_getsock,                     /* doing_getsock */
135   ZERO_NULL,                        /* perform_getsock */
136   smtp_disconnect,                  /* disconnect */
137   PORT_SMTP,                        /* defport */
138   PROT_SMTP                         /* protocol */
139 };
140
141
142 #ifdef USE_SSL
143 /*
144  * SMTPS protocol handler.
145  */
146
147 const struct Curl_handler Curl_handler_smtps = {
148   "SMTPS",                          /* scheme */
149   smtp_setup_connection,            /* setup_connection */
150   smtp_do,                          /* do_it */
151   smtp_done,                        /* done */
152   ZERO_NULL,                        /* do_more */
153   smtp_connect,                     /* connect_it */
154   smtp_multi_statemach,             /* connecting */
155   smtp_doing,                       /* doing */
156   smtp_getsock,                     /* proto_getsock */
157   smtp_getsock,                     /* doing_getsock */
158   ZERO_NULL,                        /* perform_getsock */
159   smtp_disconnect,                  /* disconnect */
160   PORT_SMTPS,                       /* defport */
161   PROT_SMTP | PROT_SMTPS | PROT_SSL  /* protocol */
162 };
163 #endif
164
165 #ifndef CURL_DISABLE_HTTP
166 /*
167  * HTTP-proxyed SMTP protocol handler.
168  */
169
170 static const struct Curl_handler Curl_handler_smtp_proxy = {
171   "SMTP",                               /* scheme */
172   ZERO_NULL,                            /* setup_connection */
173   Curl_http,                            /* do_it */
174   Curl_http_done,                       /* done */
175   ZERO_NULL,                            /* do_more */
176   ZERO_NULL,                            /* connect_it */
177   ZERO_NULL,                            /* connecting */
178   ZERO_NULL,                            /* doing */
179   ZERO_NULL,                            /* proto_getsock */
180   ZERO_NULL,                            /* doing_getsock */
181   ZERO_NULL,                            /* perform_getsock */
182   ZERO_NULL,                            /* disconnect */
183   PORT_SMTP,                            /* defport */
184   PROT_HTTP                             /* protocol */
185 };
186
187
188 #ifdef USE_SSL
189 /*
190  * HTTP-proxyed SMTPS protocol handler.
191  */
192
193 static const struct Curl_handler Curl_handler_smtps_proxy = {
194   "SMTPS",                              /* scheme */
195   ZERO_NULL,                            /* setup_connection */
196   Curl_http,                            /* do_it */
197   Curl_http_done,                       /* done */
198   ZERO_NULL,                            /* do_more */
199   ZERO_NULL,                            /* connect_it */
200   ZERO_NULL,                            /* connecting */
201   ZERO_NULL,                            /* doing */
202   ZERO_NULL,                            /* proto_getsock */
203   ZERO_NULL,                            /* doing_getsock */
204   ZERO_NULL,                            /* perform_getsock */
205   ZERO_NULL,                            /* disconnect */
206   PORT_SMTPS,                           /* defport */
207   PROT_HTTP                             /* protocol */
208 };
209 #endif
210 #endif
211
212
213 /* Function that checks for an ending smtp status code at the start of the
214    given string.
215    As a side effect, it also flags allowed authentication mechanisms according
216    to EHLO AUTH response. */
217 static int smtp_endofresp(struct pingpong *pp, int *resp)
218 {
219   char *line = pp->linestart_resp;
220   size_t len = pp->nread_resp;
221   struct connectdata *conn = pp->conn;
222   struct smtp_conn *smtpc = &conn->proto.smtpc;
223   int result;
224   size_t wordlen;
225
226   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
227     return FALSE;       /* Nothing for us. */
228
229   if((result = (line[3] == ' ')) != 0)
230     *resp = curlx_sltosi(strtol(line, NULL, 10));
231
232   line += 4;
233   len -= 4;
234
235   if(smtpc->state == SMTP_EHLO && len >= 5 && !memcmp(line, "AUTH ", 5)) {
236     line += 5;
237     len -= 5;
238
239     for (;;) {
240       while (len &&
241              (*line == ' ' || *line == '\t' ||
242               *line == '\r' || *line == '\n')) {
243         line++;
244         len--;
245       }
246
247       if(!len)
248         break;
249
250       for (wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
251              line[wordlen] != '\t' && line[wordlen] != '\r' &&
252              line[wordlen] != '\n';)
253         wordlen++;
254
255       if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
256         smtpc->authmechs |= SMTP_AUTH_LOGIN;
257       else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
258         smtpc->authmechs |= SMTP_AUTH_PLAIN;
259       else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
260         smtpc->authmechs |= SMTP_AUTH_CRAM_MD5;
261       else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
262         smtpc->authmechs |= SMTP_AUTH_DIGEST_MD5;
263       else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
264         smtpc->authmechs |= SMTP_AUTH_GSSAPI;
265       else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
266         smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
267
268       line += wordlen;
269       len -= wordlen;
270     }
271   }
272
273   return result;
274 }
275
276 /* This is the ONLY way to change SMTP state! */
277 static void state(struct connectdata *conn,
278                   smtpstate newstate)
279 {
280   struct smtp_conn *smtpc = &conn->proto.smtpc;
281 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
282   /* for debug purposes */
283   static const char * const names[]={
284     "STOP",
285     "SERVERGREET",
286     "EHLO",
287     "HELO",
288     "STARTTLS",
289     "AUTHPLAIN",
290     "AUTHLOGIN",
291     "AUTHPASSWD",
292     "AUTHCRAM",
293     "AUTH",
294     "MAIL",
295     "RCPT",
296     "DATA",
297     "POSTDATA",
298     "QUIT",
299     /* LAST */
300   };
301   if(smtpc->state != newstate)
302     infof(conn->data, "SMTP %p state change from %s to %s\n",
303           smtpc, names[smtpc->state], names[newstate]);
304 #endif
305   smtpc->state = newstate;
306 }
307
308 static CURLcode smtp_state_ehlo(struct connectdata *conn)
309 {
310   CURLcode result;
311   struct smtp_conn *smtpc = &conn->proto.smtpc;
312
313   smtpc->authmechs = 0;         /* No known authentication mechanisms yet. */
314
315   /* send EHLO */
316   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
317
318   if(result)
319     return result;
320
321   state(conn, SMTP_EHLO);
322   return CURLE_OK;
323 }
324
325 static CURLcode smtp_state_helo(struct connectdata *conn)
326 {
327   CURLcode result;
328   struct smtp_conn *smtpc = &conn->proto.smtpc;
329
330   /* send HELO */
331   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
332
333   if(result)
334     return result;
335
336   state(conn, SMTP_HELO);
337   return CURLE_OK;
338 }
339
340 static size_t smtp_auth_plain_data(struct connectdata * conn, char * * outptr)
341 {
342   char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH];
343   size_t ulen;
344   size_t plen;
345
346   ulen = strlen(conn->user);
347   plen = strlen(conn->passwd);
348
349   if(2 * ulen + plen + 2 > sizeof plainauth)
350     return 0;
351
352   memcpy(plainauth, conn->user, ulen);
353   plainauth[ulen] = '\0';
354   memcpy(plainauth + ulen + 1, conn->user, ulen);
355   plainauth[2 * ulen + 1] = '\0';
356   memcpy(plainauth + 2 * ulen + 2, conn->passwd, plen);
357   return Curl_base64_encode(conn->data, plainauth, 2 * ulen + plen + 2, outptr);
358 }
359
360 static size_t smtp_auth_login_user(struct connectdata * conn, char * * outptr)
361 {
362   size_t ulen;
363
364   ulen = strlen(conn->user);
365
366   if(!ulen) {
367     *outptr = strdup("=");
368     return *outptr? 1: 0;
369   }
370
371   return Curl_base64_encode(conn->data, conn->user, ulen, outptr);
372 }
373
374 static CURLcode smtp_authenticate(struct connectdata *conn)
375 {
376   CURLcode result = CURLE_OK;
377   struct smtp_conn *smtpc = &conn->proto.smtpc;
378   char * initresp;
379   const char * mech;
380   size_t l;
381   smtpstate state1;
382   smtpstate state2;
383
384   if(!conn->bits.user_passwd)
385     state(conn, SMTP_STOP);             /* End of connect phase. */
386   else {
387     initresp = (char *) NULL;
388     l = 1;
389
390     /* Check supported authentication mechanisms by decreasing order of
391        preference. */
392     mech = (const char *) NULL;         /* Avoid compiler warnings. */
393     state1 = SMTP_STOP;
394     state2 = SMTP_STOP;
395
396 #ifndef CURL_DISABLE_CRYPTO_AUTH
397     if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
398       mech = "CRAM-MD5";
399       state1 = SMTP_AUTHCRAM;
400     }
401     else
402 #endif
403     if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
404       mech = "PLAIN";
405       state1 = SMTP_AUTHPLAIN;
406       state2 = SMTP_AUTH;
407       l = smtp_auth_plain_data(conn, &initresp);
408     }
409     else if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
410       mech = "LOGIN";
411       state1 = SMTP_AUTHLOGIN;
412       state2 = SMTP_AUTHPASSWD;
413       l = smtp_auth_login_user(conn, &initresp);
414     }
415     else {
416       infof(conn->data, "No known auth mechanisms supported!\n");
417       result = CURLE_LOGIN_DENIED;      /* Other mechanisms not supported. */
418     }
419
420     if(!result) {
421       if(!l)
422         result = CURLE_OUT_OF_MEMORY;
423       else if(initresp &&
424               l + strlen(mech) <= 512 - 8) {   /* AUTH <mech> ...<crlf> */
425         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
426         free(initresp);
427
428         if(!result)
429           state(conn, state2);
430       }
431       else {
432         Curl_safefree(initresp);
433
434         result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
435
436         if(!result)
437           state(conn, state1);
438       }
439     }
440   }
441
442   return result;
443 }
444
445 /* For the SMTP "protocol connect" and "doing" phases only */
446 static int smtp_getsock(struct connectdata *conn,
447                         curl_socket_t *socks,
448                         int numsocks)
449 {
450   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
451 }
452
453 /* for STARTTLS responses */
454 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
455                                          int smtpcode,
456                                          smtpstate instate)
457 {
458   CURLcode result = CURLE_OK;
459   struct SessionHandle *data = conn->data;
460   (void)instate; /* no use for this yet */
461
462   if(smtpcode != 220) {
463     if(data->set.ftp_ssl != CURLUSESSL_TRY) {
464       failf(data, "STARTTLS denied. %c", smtpcode);
465       result = CURLE_LOGIN_DENIED;
466     }
467     else
468       result = smtp_authenticate(conn);
469   }
470   else {
471     /* Curl_ssl_connect is BLOCKING */
472     result = Curl_ssl_connect(conn, FIRSTSOCKET);
473     if(CURLE_OK == result) {
474       conn->protocol |= PROT_SMTPS;
475       result = smtp_state_ehlo(conn);
476     }
477   }
478   return result;
479 }
480
481 /* for EHLO responses */
482 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
483                                      int smtpcode,
484                                      smtpstate instate)
485 {
486   CURLcode result = CURLE_OK;
487   struct SessionHandle *data = conn->data;
488
489   (void)instate; /* no use for this yet */
490
491   if(smtpcode/100 != 2) {
492     if((data->set.ftp_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
493      !conn->bits.user_passwd)
494       result = smtp_state_helo(conn);
495     else {
496       failf(data, "Access denied: %d", smtpcode);
497       result = CURLE_LOGIN_DENIED;
498     }
499   }
500   else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
501     /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
502        to TLS connection now */
503     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS");
504     state(conn, SMTP_STARTTLS);
505   }
506   else
507     result = smtp_authenticate(conn);
508
509   return result;
510 }
511
512 /* for HELO responses */
513 static CURLcode smtp_state_helo_resp(struct connectdata *conn,
514                                      int smtpcode,
515                                      smtpstate instate)
516 {
517   CURLcode result = CURLE_OK;
518   struct SessionHandle *data = conn->data;
519
520   (void)instate; /* no use for this yet */
521
522   if(smtpcode/100 != 2) {
523     failf(data, "Access denied: %d", smtpcode);
524     result = CURLE_LOGIN_DENIED;
525   }
526   else {
527     /* end the connect phase */
528     state(conn, SMTP_STOP);
529   }
530   return result;
531 }
532
533 /* for AUTH PLAIN (without initial response) responses */
534 static CURLcode smtp_state_authplain_resp(struct connectdata *conn,
535                                           int smtpcode,
536                                           smtpstate instate)
537 {
538   CURLcode result = CURLE_OK;
539   struct SessionHandle *data = conn->data;
540   size_t l;
541   char * plainauth;
542
543   (void)instate; /* no use for this yet */
544
545   if(smtpcode != 334) {
546     failf(data, "Access denied: %d", smtpcode);
547     result = CURLE_LOGIN_DENIED;
548   }
549   else {
550     l = smtp_auth_plain_data(conn, &plainauth);
551
552     if(!l)
553       result = CURLE_OUT_OF_MEMORY;
554     else {
555       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", plainauth);
556       free(plainauth);
557
558       if(!result)
559         state(conn, SMTP_AUTH);
560     }
561   }
562
563   return result;
564 }
565
566 /* for AUTH LOGIN (without initial response) responses */
567 static CURLcode smtp_state_authlogin_resp(struct connectdata *conn,
568                                           int smtpcode,
569                                           smtpstate instate)
570 {
571   CURLcode result = CURLE_OK;
572   struct SessionHandle *data = conn->data;
573   size_t l;
574   char * authuser;
575
576   (void)instate; /* no use for this yet */
577
578   if(smtpcode != 334) {
579     failf(data, "Access denied: %d", smtpcode);
580     result = CURLE_LOGIN_DENIED;
581   }
582   else {
583     l = smtp_auth_login_user(conn, &authuser);
584
585     if(!l)
586       result = CURLE_OUT_OF_MEMORY;
587     else {
588       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authuser);
589       free(authuser);
590
591       if(!result)
592         state(conn, SMTP_AUTHPASSWD);
593     }
594   }
595
596   return result;
597 }
598
599 /* for responses to user entry of AUTH LOGIN. */
600 static CURLcode smtp_state_authpasswd_resp(struct connectdata *conn,
601                                            int smtpcode,
602                                            smtpstate instate)
603 {
604   CURLcode result = CURLE_OK;
605   struct SessionHandle *data = conn->data;
606   size_t plen;
607   size_t l;
608   char *authpasswd;
609
610   (void)instate; /* no use for this yet */
611
612   if(smtpcode != 334) {
613     failf(data, "Access denied: %d", smtpcode);
614     result = CURLE_LOGIN_DENIED;
615   }
616   else {
617     plen = strlen(conn->passwd);
618
619     if(!plen)
620       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "=");
621     else {
622       l = Curl_base64_encode(data, conn->passwd, plen, &authpasswd);
623
624       if(!l)
625         result = CURLE_OUT_OF_MEMORY;
626       else {
627         result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", authpasswd);
628         free(authpasswd);
629
630         if(!result)
631           state(conn, SMTP_AUTH);
632       }
633     }
634   }
635
636   return result;
637 }
638
639 #ifndef CURL_DISABLE_CRYPTO_AUTH
640
641 /* for AUTH CRAM-MD5 responses. */
642 static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
643                                          int smtpcode,
644                                          smtpstate instate)
645 {
646   CURLcode result = CURLE_OK;
647   struct SessionHandle *data = conn->data;
648   char * chlg64 = data->state.buffer;
649   unsigned char * chlg;
650   size_t chlglen;
651   size_t l;
652   char * rplyb64;
653   HMAC_context * ctxt;
654   unsigned char digest[16];
655   char reply[MAX_CURL_USER_LENGTH + 32 /* 2 * size of MD5 digest */ + 1];
656
657   (void)instate; /* no use for this yet */
658
659   if(smtpcode != 334) {
660     failf(data, "Access denied: %d", smtpcode);
661     return CURLE_LOGIN_DENIED;
662   }
663
664   /* Get the challenge. */
665   for (chlg64 += 4; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
666     ;
667
668   chlg = (unsigned char *) NULL;
669   chlglen = 0;
670
671   if(*chlg64 != '=') {
672     for (l = strlen(chlg64); l--;)
673       if(chlg64[l] != '\r' && chlg64[l] != '\n' && chlg64[l] != ' ' &&
674        chlg64[l] != '\t')
675         break;
676
677     if(++l) {
678       chlg64[l] = '\0';
679
680       chlglen = Curl_base64_decode(chlg64, &chlg);
681       if(!chlglen)
682         return CURLE_OUT_OF_MEMORY;
683     }
684   }
685
686   /* Compute digest. */
687   ctxt = Curl_HMAC_init(Curl_HMAC_MD5,
688                         (const unsigned char *) conn->passwd,
689                         (unsigned int)(strlen(conn->passwd)));
690
691   if(!ctxt) {
692     if(chlg)
693       free(chlg);
694
695     return CURLE_OUT_OF_MEMORY;
696   }
697
698   if(chlglen > 0)
699     Curl_HMAC_update(ctxt, chlg, (unsigned int)(chlglen));
700
701   if(chlg)
702     free(chlg);
703
704   Curl_HMAC_final(ctxt, digest);
705
706   /* Prepare the reply. */
707   snprintf(reply, sizeof reply,
708    "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
709    conn->user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
710    digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
711    digest[12], digest[13], digest[14], digest[15]);
712
713   /* Encode it to base64 and send it. */
714   l = Curl_base64_encode(data, reply, 0, &rplyb64);
715
716   if(!l)
717     result = CURLE_OUT_OF_MEMORY;
718   else {
719     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
720     free(rplyb64);
721
722     if(!result)
723       state(conn, SMTP_AUTH);
724   }
725
726   return result;
727 }
728
729 #endif
730
731 /* for final responses to AUTH sequences. */
732 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
733                                      int smtpcode,
734                                      smtpstate instate)
735 {
736   CURLcode result = CURLE_OK;
737   struct SessionHandle *data = conn->data;
738
739   (void)instate; /* no use for this yet */
740
741   if(smtpcode != 235) {
742     failf(data, "Authentication failed: %d", smtpcode);
743     result = CURLE_LOGIN_DENIED;
744   }
745   else
746     state(conn, SMTP_STOP);             /* End of connect phase. */
747
748   return result;
749 }
750
751 /* start the DO phase */
752 static CURLcode smtp_mail(struct connectdata *conn)
753 {
754   CURLcode result = CURLE_OK;
755   struct SessionHandle *data = conn->data;
756
757   /* send MAIL */
758   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
759                          data->set.str[STRING_MAIL_FROM]);
760   if(result)
761     return result;
762
763   state(conn, SMTP_MAIL);
764   return result;
765 }
766
767 static CURLcode smtp_rcpt_to(struct connectdata *conn)
768 {
769   CURLcode result = CURLE_OK;
770   struct smtp_conn *smtpc = &conn->proto.smtpc;
771
772   /* send RCPT TO */
773   if(smtpc->rcpt) {
774     if(smtpc->rcpt->data[0] == '<')
775       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
776                              smtpc->rcpt->data);
777     else
778       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
779                              smtpc->rcpt->data);
780     if(!result)
781       state(conn, SMTP_RCPT);
782   }
783   return result;
784 }
785
786 /* for MAIL responses */
787 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
788                                      int smtpcode,
789                                      smtpstate instate)
790 {
791   CURLcode result = CURLE_OK;
792   struct SessionHandle *data = conn->data;
793   (void)instate; /* no use for this yet */
794
795   if(smtpcode/100 != 2) {
796     failf(data, "Access denied: %d", smtpcode);
797     result = CURLE_LOGIN_DENIED;
798     state(conn, SMTP_STOP);
799   }
800   else {
801     struct smtp_conn *smtpc = &conn->proto.smtpc;
802     smtpc->rcpt = data->set.mail_rcpt;
803
804     result = smtp_rcpt_to(conn);
805   }
806
807   return result;
808 }
809
810 /* for RCPT responses */
811 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
812                                      int smtpcode,
813                                      smtpstate instate)
814 {
815   CURLcode result = CURLE_OK;
816   struct SessionHandle *data = conn->data;
817   (void)instate; /* no use for this yet */
818
819   if(smtpcode/100 != 2) {
820     failf(data, "Access denied: %d", smtpcode);
821     result = CURLE_LOGIN_DENIED;
822     state(conn, SMTP_STOP);
823   }
824   else {
825     struct smtp_conn *smtpc = &conn->proto.smtpc;
826
827     if(smtpc->rcpt) {
828       smtpc->rcpt = smtpc->rcpt->next;
829       result = smtp_rcpt_to(conn);
830
831       /* if we failed or still is in RCPT sending, return */
832       if(result || smtpc->rcpt)
833         return result;
834     }
835
836     /* send DATA */
837     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA");
838     if(result)
839       return result;
840
841     state(conn, SMTP_DATA);
842   }
843   return result;
844 }
845
846 /* for the DATA response */
847 static CURLcode smtp_state_data_resp(struct connectdata *conn,
848                                      int smtpcode,
849                                      smtpstate instate)
850 {
851   struct SessionHandle *data = conn->data;
852   struct FTP *smtp = data->state.proto.smtp;
853
854   (void)instate; /* no use for this yet */
855
856   if(smtpcode != 354) {
857     state(conn, SMTP_STOP);
858     return CURLE_RECV_ERROR;
859   }
860
861   /* SMTP upload */
862   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
863                       FIRSTSOCKET, smtp->bytecountp);
864
865   state(conn, SMTP_STOP);
866   return CURLE_OK;
867 }
868
869 /* for the POSTDATA response, which is received after the entire DATA
870    part has been sent off to the server */
871 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
872                                      int smtpcode,
873                                      smtpstate instate)
874 {
875   CURLcode result = CURLE_OK;
876
877   (void)instate; /* no use for this yet */
878
879   if(smtpcode != 250)
880     result = CURLE_RECV_ERROR;
881
882   state(conn, SMTP_STOP);
883   return result;
884 }
885
886 static CURLcode smtp_statemach_act(struct connectdata *conn)
887 {
888   CURLcode result;
889   curl_socket_t sock = conn->sock[FIRSTSOCKET];
890   struct SessionHandle *data=conn->data;
891   int smtpcode;
892   struct smtp_conn *smtpc = &conn->proto.smtpc;
893   struct pingpong *pp = &smtpc->pp;
894   size_t nread = 0;
895
896   if(pp->sendleft)
897     /* we have a piece of a command still left to send */
898     return Curl_pp_flushsend(pp);
899
900   /* we read a piece of response */
901   result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
902   if(result)
903     return result;
904
905   if(smtpcode) {
906     /* we have now received a full SMTP server response */
907     switch(smtpc->state) {
908     case SMTP_SERVERGREET:
909       if(smtpcode/100 != 2) {
910         failf(data, "Got unexpected smtp-server response: %d", smtpcode);
911         return CURLE_FTP_WEIRD_SERVER_REPLY;
912       }
913
914       result = smtp_state_ehlo(conn);
915       if(result)
916         return result;
917       break;
918
919     case SMTP_EHLO:
920       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
921       break;
922
923     case SMTP_HELO:
924       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
925       break;
926
927     case SMTP_STARTTLS:
928       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
929       break;
930
931     case SMTP_AUTHPLAIN:
932       result = smtp_state_authplain_resp(conn, smtpcode, smtpc->state);
933       break;
934
935     case SMTP_AUTHLOGIN:
936       result = smtp_state_authlogin_resp(conn, smtpcode, smtpc->state);
937       break;
938
939     case SMTP_AUTHPASSWD:
940       result = smtp_state_authpasswd_resp(conn, smtpcode, smtpc->state);
941       break;
942
943 #ifndef CURL_DISABLE_CRYPTO_AUTH
944     case SMTP_AUTHCRAM:
945       result = smtp_state_authcram_resp(conn, smtpcode, smtpc->state);
946       break;
947 #endif
948
949     case SMTP_AUTH:
950       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
951       break;
952
953     case SMTP_MAIL:
954       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
955       break;
956
957     case SMTP_RCPT:
958       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
959       break;
960
961     case SMTP_DATA:
962       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
963       break;
964
965     case SMTP_POSTDATA:
966       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
967       break;
968
969     case SMTP_QUIT:
970       /* fallthrough, just stop! */
971     default:
972       /* internal error */
973       state(conn, SMTP_STOP);
974       break;
975     }
976   }
977   return result;
978 }
979
980 /* called repeatedly until done from multi.c */
981 static CURLcode smtp_multi_statemach(struct connectdata *conn,
982                                      bool *done)
983 {
984   struct smtp_conn *smtpc = &conn->proto.smtpc;
985   CURLcode result = Curl_pp_multi_statemach(&smtpc->pp);
986
987   *done = (bool)(smtpc->state == SMTP_STOP);
988
989   return result;
990 }
991
992 static CURLcode smtp_easy_statemach(struct connectdata *conn)
993 {
994   struct smtp_conn *smtpc = &conn->proto.smtpc;
995   struct pingpong *pp = &smtpc->pp;
996   CURLcode result = CURLE_OK;
997
998   while(smtpc->state != SMTP_STOP) {
999     result = Curl_pp_easy_statemach(pp);
1000     if(result)
1001       break;
1002   }
1003
1004   return result;
1005 }
1006
1007 /*
1008  * Allocate and initialize the struct SMTP for the current SessionHandle.  If
1009  * need be.
1010  */
1011 static CURLcode smtp_init(struct connectdata *conn)
1012 {
1013   struct SessionHandle *data = conn->data;
1014   struct FTP *smtp = data->state.proto.smtp;
1015   if(!smtp) {
1016     smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
1017     if(!smtp)
1018       return CURLE_OUT_OF_MEMORY;
1019   }
1020
1021   /* get some initial data into the smtp struct */
1022   smtp->bytecountp = &data->req.bytecount;
1023
1024   /* No need to duplicate user+password, the connectdata struct won't change
1025      during a session, but we re-init them here since on subsequent inits
1026      since the conn struct may have changed or been replaced.
1027   */
1028   smtp->user = conn->user;
1029   smtp->passwd = conn->passwd;
1030
1031   return CURLE_OK;
1032 }
1033
1034 /*
1035  * smtp_connect() should do everything that is to be considered a part of
1036  * the connection phase.
1037  *
1038  * The variable 'done' points to will be TRUE if the protocol-layer connect
1039  * phase is done when this function returns, or FALSE is not. When called as
1040  * a part of the easy interface, it will always be TRUE.
1041  */
1042 static CURLcode smtp_connect(struct connectdata *conn,
1043                              bool *done) /* see description above */
1044 {
1045   CURLcode result;
1046   struct smtp_conn *smtpc = &conn->proto.smtpc;
1047   struct SessionHandle *data=conn->data;
1048   struct pingpong *pp=&smtpc->pp;
1049   const char *path = conn->data->state.path;
1050   int len;
1051   char localhost[1024 + 1];
1052
1053   *done = FALSE; /* default to not done yet */
1054
1055   /* If there already is a protocol-specific struct allocated for this
1056      sessionhandle, deal with it */
1057   Curl_reset_reqproto(conn);
1058
1059   result = smtp_init(conn);
1060   if(CURLE_OK != result)
1061     return result;
1062
1063   /* We always support persistant connections on smtp */
1064   conn->bits.close = FALSE;
1065
1066   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1067   pp->statemach_act = smtp_statemach_act;
1068   pp->endofresp = smtp_endofresp;
1069   pp->conn = conn;
1070
1071 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
1072   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1073     /* for SMTP over HTTP proxy */
1074     struct HTTP http_proxy;
1075     struct FTP *smtp_save;
1076
1077     /* BLOCKING */
1078     /* We want "seamless" SMTP operations through HTTP proxy tunnel */
1079
1080     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1081      * conn->proto.http; we want SMTP through HTTP and we have to change the
1082      * member temporarily for connecting to the HTTP proxy. After
1083      * Curl_proxyCONNECT we have to set back the member to the original struct
1084      * SMTP pointer
1085      */
1086     smtp_save = data->state.proto.smtp;
1087     memset(&http_proxy, 0, sizeof(http_proxy));
1088     data->state.proto.http = &http_proxy;
1089
1090     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
1091                                conn->host.name, conn->remote_port);
1092
1093     data->state.proto.smtp = smtp_save;
1094
1095     if(CURLE_OK != result)
1096       return result;
1097   }
1098 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
1099
1100   if(conn->protocol & PROT_SMTPS) {
1101     /* BLOCKING */
1102     /* SMTPS is simply smtp with SSL for the control channel */
1103     /* now, perform the SSL initialization for this socket */
1104     result = Curl_ssl_connect(conn, FIRSTSOCKET);
1105     if(result)
1106       return result;
1107   }
1108
1109   Curl_pp_init(pp); /* init the response reader stuff */
1110
1111   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
1112   pp->statemach_act = smtp_statemach_act;
1113   pp->endofresp = smtp_endofresp;
1114   pp->conn = conn;
1115
1116   if(!*path) {
1117     if(!Curl_gethostname(localhost, sizeof localhost))
1118       path = localhost;
1119     else
1120       path = "localhost";
1121   }
1122
1123   /* url decode the path and use it as domain with EHLO */
1124   smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
1125   if(!smtpc->domain)
1126     return CURLE_OUT_OF_MEMORY;
1127
1128   /* When we connect, we start in the state where we await the server greeting
1129    */
1130   state(conn, SMTP_SERVERGREET);
1131
1132   if(data->state.used_interface == Curl_if_multi)
1133     result = smtp_multi_statemach(conn, done);
1134   else {
1135     result = smtp_easy_statemach(conn);
1136     if(!result)
1137       *done = TRUE;
1138   }
1139
1140   return result;
1141 }
1142
1143 /***********************************************************************
1144  *
1145  * smtp_done()
1146  *
1147  * The DONE function. This does what needs to be done after a single DO has
1148  * performed.
1149  *
1150  * Input argument is already checked for validity.
1151  */
1152 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1153                           bool premature)
1154 {
1155   struct SessionHandle *data = conn->data;
1156   struct FTP *smtp = data->state.proto.smtp;
1157   CURLcode result=CURLE_OK;
1158   ssize_t bytes_written;
1159   (void)premature;
1160
1161   if(!smtp)
1162     /* When the easy handle is removed from the multi while libcurl is still
1163      * trying to resolve the host name, it seems that the smtp struct is not
1164      * yet initialized, but the removal action calls Curl_done() which calls
1165      * this function. So we simply return success if no smtp pointer is set.
1166      */
1167     return CURLE_OK;
1168
1169   if(status) {
1170     conn->bits.close = TRUE; /* marked for closure */
1171     result = status;      /* use the already set error code */
1172   }
1173   else
1174     /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
1175
1176     /* write to socket (send away data) */
1177     result = Curl_write(conn,
1178                         conn->writesockfd,  /* socket to send to */
1179                         SMTP_EOB,           /* buffer pointer */
1180                         SMTP_EOB_LEN,       /* buffer size */
1181                         &bytes_written);    /* actually sent away */
1182
1183
1184   if(status == CURLE_OK) {
1185     struct smtp_conn *smtpc = &conn->proto.smtpc;
1186     struct pingpong *pp= &smtpc->pp;
1187     pp->response = Curl_tvnow(); /* timeout relative now */
1188
1189     state(conn, SMTP_POSTDATA);
1190     /* run the state-machine
1191
1192        TODO: when the multi interface is used, this _really_ should be using
1193        the smtp_multi_statemach function but we have no general support for
1194        non-blocking DONE operations, not in the multi state machine and with
1195        Curl_done() invokes on several places in the code!
1196     */
1197     result = smtp_easy_statemach(conn);
1198   }
1199
1200   /* clear these for next connection */
1201   smtp->transfer = FTPTRANSFER_BODY;
1202
1203   return result;
1204 }
1205
1206 /***********************************************************************
1207  *
1208  * smtp_perform()
1209  *
1210  * This is the actual DO function for SMTP. Get a file/directory according to
1211  * the options previously setup.
1212  */
1213
1214 static
1215 CURLcode smtp_perform(struct connectdata *conn,
1216                      bool *connected,  /* connect status after PASV / PORT */
1217                      bool *dophase_done)
1218 {
1219   /* this is SMTP and no proxy */
1220   CURLcode result=CURLE_OK;
1221
1222   DEBUGF(infof(conn->data, "DO phase starts\n"));
1223
1224   if(conn->data->set.opt_no_body) {
1225     /* requested no body means no transfer... */
1226     struct FTP *smtp = conn->data->state.proto.smtp;
1227     smtp->transfer = FTPTRANSFER_INFO;
1228   }
1229
1230   *dophase_done = FALSE; /* not done yet */
1231
1232   /* start the first command in the DO phase */
1233   result = smtp_mail(conn);
1234   if(result)
1235     return result;
1236
1237   /* run the state-machine */
1238   if(conn->data->state.used_interface == Curl_if_multi)
1239     result = smtp_multi_statemach(conn, dophase_done);
1240   else {
1241     result = smtp_easy_statemach(conn);
1242     *dophase_done = TRUE; /* with the easy interface we are done here */
1243   }
1244   *connected = conn->bits.tcpconnect;
1245
1246   if(*dophase_done)
1247     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1248
1249   return result;
1250 }
1251
1252 /***********************************************************************
1253  *
1254  * smtp_do()
1255  *
1256  * This function is registered as 'curl_do' function. It decodes the path
1257  * parts etc as a wrapper to the actual DO function (smtp_perform).
1258  *
1259  * The input argument is already checked for validity.
1260  */
1261 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1262 {
1263   CURLcode retcode = CURLE_OK;
1264
1265   *done = FALSE; /* default to false */
1266
1267   /*
1268     Since connections can be re-used between SessionHandles, this might be a
1269     connection already existing but on a fresh SessionHandle struct so we must
1270     make sure we have a good 'struct SMTP' to play with. For new connections,
1271     the struct SMTP is allocated and setup in the smtp_connect() function.
1272   */
1273   Curl_reset_reqproto(conn);
1274   retcode = smtp_init(conn);
1275   if(retcode)
1276     return retcode;
1277
1278   retcode = smtp_regular_transfer(conn, done);
1279
1280   return retcode;
1281 }
1282
1283 /***********************************************************************
1284  *
1285  * smtp_quit()
1286  *
1287  * This should be called before calling sclose().  We should then wait for the
1288  * response from the server before returning. The calling code should then try
1289  * to close the connection.
1290  *
1291  */
1292 static CURLcode smtp_quit(struct connectdata *conn)
1293 {
1294   CURLcode result = CURLE_OK;
1295
1296   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT");
1297   if(result)
1298     return result;
1299   state(conn, SMTP_QUIT);
1300
1301   result = smtp_easy_statemach(conn);
1302
1303   return result;
1304 }
1305
1306 /***********************************************************************
1307  *
1308  * smtp_disconnect()
1309  *
1310  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1311  * resources. BLOCKING.
1312  */
1313 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1314 {
1315   struct smtp_conn *smtpc= &conn->proto.smtpc;
1316
1317   /* We cannot send quit unconditionally. If this connection is stale or
1318      bad in any way, sending quit and waiting around here will make the
1319      disconnect wait in vain and cause more problems than we need to.
1320   */
1321
1322   /* The SMTP session may or may not have been allocated/setup at this
1323      point! */
1324   if(!dead_connection && smtpc->pp.conn)
1325     (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
1326
1327   Curl_pp_disconnect(&smtpc->pp);
1328
1329   /* This won't already be freed in some error cases */
1330   Curl_safefree(smtpc->domain);
1331   smtpc->domain = NULL;
1332
1333   return CURLE_OK;
1334 }
1335
1336 /* call this when the DO phase has completed */
1337 static CURLcode smtp_dophase_done(struct connectdata *conn,
1338                                   bool connected)
1339 {
1340   struct FTP *smtp = conn->data->state.proto.smtp;
1341   struct smtp_conn *smtpc= &conn->proto.smtpc;
1342   (void)connected;
1343
1344   if(smtp->transfer != FTPTRANSFER_BODY)
1345     /* no data to transfer */
1346     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1347
1348   free(smtpc->domain);
1349   smtpc->domain = NULL;
1350
1351   return CURLE_OK;
1352 }
1353
1354 /* called from multi.c while DOing */
1355 static CURLcode smtp_doing(struct connectdata *conn,
1356                                bool *dophase_done)
1357 {
1358   CURLcode result;
1359   result = smtp_multi_statemach(conn, dophase_done);
1360
1361   if(*dophase_done) {
1362     result = smtp_dophase_done(conn, FALSE /* not connected */);
1363
1364     DEBUGF(infof(conn->data, "DO phase is complete\n"));
1365   }
1366   return result;
1367 }
1368
1369 /***********************************************************************
1370  *
1371  * smtp_regular_transfer()
1372  *
1373  * The input argument is already checked for validity.
1374  *
1375  * Performs all commands done before a regular transfer between a local and a
1376  * remote host.
1377  */
1378 static
1379 CURLcode smtp_regular_transfer(struct connectdata *conn,
1380                               bool *dophase_done)
1381 {
1382   CURLcode result=CURLE_OK;
1383   bool connected=FALSE;
1384   struct SessionHandle *data = conn->data;
1385   data->req.size = -1; /* make sure this is unknown at this point */
1386
1387   Curl_pgrsSetUploadCounter(data, 0);
1388   Curl_pgrsSetDownloadCounter(data, 0);
1389   Curl_pgrsSetUploadSize(data, 0);
1390   Curl_pgrsSetDownloadSize(data, 0);
1391
1392   result = smtp_perform(conn,
1393                         &connected, /* have we connected after PASV/PORT */
1394                         dophase_done); /* all commands in the DO-phase done? */
1395
1396   if(CURLE_OK == result) {
1397
1398     if(!*dophase_done)
1399       /* the DO phase has not completed yet */
1400       return CURLE_OK;
1401
1402     result = smtp_dophase_done(conn, connected);
1403     if(result)
1404       return result;
1405   }
1406
1407   return result;
1408 }
1409
1410 static CURLcode smtp_setup_connection(struct connectdata * conn)
1411 {
1412   struct SessionHandle *data = conn->data;
1413
1414   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1415     /* Unless we have asked to tunnel smtp operations through the proxy, we
1416        switch and use HTTP operations only */
1417 #ifndef CURL_DISABLE_HTTP
1418     if(conn->handler == &Curl_handler_smtp)
1419       conn->handler = &Curl_handler_smtp_proxy;
1420     else {
1421 #ifdef USE_SSL
1422       conn->handler = &Curl_handler_smtps_proxy;
1423 #else
1424       failf(data, "SMTPS not supported!");
1425       return CURLE_UNSUPPORTED_PROTOCOL;
1426 #endif
1427     }
1428     /*
1429      * We explicitly mark this connection as persistent here as we're doing
1430      * SMTP over HTTP and thus we accidentally avoid setting this value
1431      * otherwise.
1432      */
1433     conn->bits.close = FALSE;
1434 #else
1435     failf(data, "SMTP over http proxy requires HTTP support built-in!");
1436     return CURLE_UNSUPPORTED_PROTOCOL;
1437 #endif
1438   }
1439
1440   data->state.path++;   /* don't include the initial slash */
1441
1442   return CURLE_OK;
1443 }
1444
1445 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1446 {
1447   /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
1448    * the data and make sure it is sent as CRLF..CRLF instead, as
1449    * otherwise it will wrongly be detected as end of data by the server.
1450    */
1451   ssize_t i;
1452   ssize_t si;
1453   struct smtp_conn *smtpc = &conn->proto.smtpc;
1454   struct SessionHandle *data = conn->data;
1455
1456   if(data->state.scratch == NULL)
1457     data->state.scratch = malloc(2*BUFSIZE);
1458   if(data->state.scratch == NULL) {
1459     failf (data, "Failed to alloc scratch buffer!");
1460     return CURLE_OUT_OF_MEMORY;
1461   }
1462   /* This loop can be improved by some kind of Boyer-Moore style of
1463      approach but that is saved for later... */
1464   for(i = 0, si = 0; i < nread; i++, si++) {
1465     ssize_t left = nread - i;
1466
1467     if(left>= (ssize_t)(SMTP_EOB_LEN-smtpc->eob)) {
1468       if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1469                  SMTP_EOB_LEN-smtpc->eob)) {
1470         /* It matched, copy the replacement data to the target buffer
1471            instead. Note that the replacement does not contain the
1472            trailing CRLF but we instead continue to match on that one
1473            to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1474         */
1475         memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1476                SMTP_EOB_REPL_LEN);
1477         si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
1478                                           it */
1479         i+=SMTP_EOB_LEN-smtpc->eob-1-2;
1480         smtpc->eob = 0; /* start over */
1481         continue;
1482       }
1483     }
1484     else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1485                     left)) {
1486       /* the last piece of the data matches the EOB so we can't send that
1487          until we know the rest of it */
1488       smtpc->eob += left;
1489       break;
1490     }
1491
1492     data->state.scratch[si] = data->req.upload_fromhere[i];
1493   } /* for() */
1494
1495   if(si != nread) {
1496     /* only use the new buffer if we replaced something */
1497     nread = si;
1498
1499     /* upload from the new (replaced) buffer instead */
1500     data->req.upload_fromhere = data->state.scratch;
1501
1502     /* set the new amount too */
1503     data->req.upload_present = nread;
1504   }
1505   return CURLE_OK;
1506 }
1507
1508 #endif /* CURL_DISABLE_SMTP */