f879651c6ae5568623164df81619a2885f20cc63
[platform/upstream/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  *
23  * $Id$
24  ***************************************************************************/
25
26 #include "setup.h"
27
28 #ifndef CURL_DISABLE_SMTP
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <ctype.h>
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #ifdef HAVE_SYS_SOCKET_H
40 #include <sys/socket.h>
41 #endif
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
47 #endif
48 #ifdef HAVE_UTSNAME_H
49 #include <sys/utsname.h>
50 #endif
51 #ifdef HAVE_NETDB_H
52 #include <netdb.h>
53 #endif
54 #ifdef __VMS
55 #include <in.h>
56 #include <inet.h>
57 #endif
58
59 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #undef in_addr_t
61 #define in_addr_t unsigned long
62 #endif
63
64 #include <curl/curl.h>
65 #include "urldata.h"
66 #include "sendf.h"
67 #include "easyif.h" /* for Curl_convert_... prototypes */
68
69 #include "if2ip.h"
70 #include "hostip.h"
71 #include "progress.h"
72 #include "transfer.h"
73 #include "escape.h"
74 #include "http.h" /* for HTTP proxy tunnel stuff */
75 #include "socks.h"
76 #include "smtp.h"
77
78 #include "strtoofft.h"
79 #include "strequal.h"
80 #include "sslgen.h"
81 #include "connect.h"
82 #include "strerror.h"
83 #include "select.h"
84 #include "multiif.h"
85 #include "url.h"
86 #include "rawstr.h"
87 #include "strtoofft.h"
88
89 #define _MPRINTF_REPLACE /* use our functions only */
90 #include <curl/mprintf.h>
91
92 #include "curl_memory.h"
93 /* The last #include file should be: */
94 #include "memdebug.h"
95
96 /* Local API functions */
97 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
98 static CURLcode smtp_do(struct connectdata *conn, bool *done);
99 static CURLcode smtp_done(struct connectdata *conn,
100                           CURLcode, bool premature);
101 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
102 static CURLcode smtp_disconnect(struct connectdata *conn);
103 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
104 static int smtp_getsock(struct connectdata *conn,
105                         curl_socket_t *socks,
106                         int numsocks);
107 static CURLcode smtp_doing(struct connectdata *conn,
108                            bool *dophase_done);
109 static CURLcode smtp_setup_connection(struct connectdata * conn);
110
111
112 /*
113  * SMTP protocol handler.
114  */
115
116 const struct Curl_handler Curl_handler_smtp = {
117   "SMTP",                           /* scheme */
118   smtp_setup_connection,            /* setup_connection */
119   smtp_do,                          /* do_it */
120   smtp_done,                        /* done */
121   ZERO_NULL,                        /* do_more */
122   smtp_connect,                     /* connect_it */
123   smtp_multi_statemach,             /* connecting */
124   smtp_doing,                       /* doing */
125   smtp_getsock,                     /* proto_getsock */
126   smtp_getsock,                     /* doing_getsock */
127   ZERO_NULL,                        /* perform_getsock */
128   smtp_disconnect,                  /* disconnect */
129   PORT_SMTP,                        /* defport */
130   PROT_SMTP                         /* protocol */
131 };
132
133
134 #ifdef USE_SSL
135 /*
136  * SMTPS protocol handler.
137  */
138
139 const struct Curl_handler Curl_handler_smtps = {
140   "SMTPS",                          /* scheme */
141   smtp_setup_connection,            /* setup_connection */
142   smtp_do,                          /* do_it */
143   smtp_done,                        /* done */
144   ZERO_NULL,                        /* do_more */
145   smtp_connect,                     /* connect_it */
146   smtp_multi_statemach,             /* connecting */
147   smtp_doing,                       /* doing */
148   smtp_getsock,                     /* proto_getsock */
149   smtp_getsock,                     /* doing_getsock */
150   ZERO_NULL,                        /* perform_getsock */
151   smtp_disconnect,                  /* disconnect */
152   PORT_SMTPS,                       /* defport */
153   PROT_SMTP | PROT_SMTPS | PROT_SSL  /* protocol */
154 };
155 #endif
156
157 #ifndef CURL_DISABLE_HTTP
158 /*
159  * HTTP-proxyed SMTP protocol handler.
160  */
161
162 static const struct Curl_handler Curl_handler_smtp_proxy = {
163   "SMTP",                               /* scheme */
164   ZERO_NULL,                            /* setup_connection */
165   Curl_http,                            /* do_it */
166   Curl_http_done,                       /* done */
167   ZERO_NULL,                            /* do_more */
168   ZERO_NULL,                            /* connect_it */
169   ZERO_NULL,                            /* connecting */
170   ZERO_NULL,                            /* doing */
171   ZERO_NULL,                            /* proto_getsock */
172   ZERO_NULL,                            /* doing_getsock */
173   ZERO_NULL,                            /* perform_getsock */
174   ZERO_NULL,                            /* disconnect */
175   PORT_SMTP,                            /* defport */
176   PROT_HTTP                             /* protocol */
177 };
178
179
180 #ifdef USE_SSL
181 /*
182  * HTTP-proxyed SMTPS protocol handler.
183  */
184
185 static const struct Curl_handler Curl_handler_smtps_proxy = {
186   "SMTPS",                              /* scheme */
187   ZERO_NULL,                            /* setup_connection */
188   Curl_http,                            /* do_it */
189   Curl_http_done,                       /* done */
190   ZERO_NULL,                            /* do_more */
191   ZERO_NULL,                            /* connect_it */
192   ZERO_NULL,                            /* connecting */
193   ZERO_NULL,                            /* doing */
194   ZERO_NULL,                            /* proto_getsock */
195   ZERO_NULL,                            /* doing_getsock */
196   ZERO_NULL,                            /* perform_getsock */
197   ZERO_NULL,                            /* disconnect */
198   PORT_SMTPS,                           /* defport */
199   PROT_HTTP                             /* protocol */
200 };
201 #endif
202 #endif
203
204
205 /* fucntion that checks for an ending smtp status code at the start of the
206    given string */
207 static int smtp_endofresp(struct pingpong *pp, int *resp)
208 {
209   char *line = pp->linestart_resp;
210   size_t len = pp->nread_resp;
211
212   if( (len >= 4) && (' ' == line[3]) &&
213       ISDIGIT(line[0]) && ISDIGIT(line[1]) && ISDIGIT(line[2])) {
214     *resp=atoi(line);
215     return TRUE;
216   }
217
218   return FALSE; /* nothing for us */
219 }
220
221 /* This is the ONLY way to change SMTP state! */
222 static void state(struct connectdata *conn,
223                   smtpstate newstate)
224 {
225 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
226   /* for debug purposes */
227   static const char * const names[]={
228     "STOP",
229     "SERVERGREET",
230     "EHLO",
231     "STARTTLS",
232     "MAIL",
233     "RCPT",
234     "DATA",
235     "POSTDATA",
236     "QUIT",
237     /* LAST */
238   };
239 #endif
240   struct smtp_conn *smtpc = &conn->proto.smtpc;
241 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
242   if(smtpc->state != newstate)
243     infof(conn->data, "SMTP %p state change from %s to %s\n",
244           smtpc, names[smtpc->state], names[newstate]);
245 #endif
246   smtpc->state = newstate;
247 }
248
249 static CURLcode smtp_state_ehlo(struct connectdata *conn)
250 {
251   CURLcode result;
252   struct smtp_conn *smtpc = &conn->proto.smtpc;
253
254   /* send EHLO */
255   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "EHLO %s", smtpc->domain);
256   if(result)
257     return result;
258
259   state(conn, SMTP_EHLO);
260
261   return CURLE_OK;
262 }
263
264 /* For the SMTP "protocol connect" and "doing" phases only */
265 static int smtp_getsock(struct connectdata *conn,
266                         curl_socket_t *socks,
267                         int numsocks)
268 {
269   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
270 }
271
272 /* for STARTTLS responses */
273 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
274                                          int smtpcode,
275                                          smtpstate instate)
276 {
277   CURLcode result = CURLE_OK;
278   struct SessionHandle *data = conn->data;
279   (void)instate; /* no use for this yet */
280
281   if(smtpcode != 'O') {
282     failf(data, "STARTTLS denied. %c", smtpcode);
283     result = CURLE_LOGIN_DENIED;
284   }
285   else {
286     /* Curl_ssl_connect is BLOCKING */
287     result = Curl_ssl_connect(conn, FIRSTSOCKET);
288     if(CURLE_OK == result) {
289       conn->protocol |= PROT_SMTPS;
290       result = smtp_state_ehlo(conn);
291     }
292   }
293   state(conn, SMTP_STOP);
294   return result;
295 }
296
297 /* for EHLO responses */
298 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
299                                      int smtpcode,
300                                      smtpstate instate)
301 {
302   CURLcode result = CURLE_OK;
303   struct SessionHandle *data = conn->data;
304
305   (void)instate; /* no use for this yet */
306
307   if(smtpcode/100 != 2) {
308     failf(data, "Access denied: %d", smtpcode);
309     result = CURLE_LOGIN_DENIED;
310   }
311
312   /* end the connect phase */
313   state(conn, SMTP_STOP);
314   return result;
315 }
316
317 /* start the DO phase */
318 static CURLcode smtp_mail(struct connectdata *conn)
319 {
320   CURLcode result = CURLE_OK;
321   struct SessionHandle *data = conn->data;
322
323   /* send MAIL */
324   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
325                          data->set.str[STRING_MAIL_FROM]);
326   if(result)
327     return result;
328
329   state(conn, SMTP_MAIL);
330   return result;
331 }
332
333 /* for MAIL responses */
334 static CURLcode smtp_state_mail_resp(struct connectdata *conn,
335                                      int smtpcode,
336                                      smtpstate instate)
337 {
338   CURLcode result = CURLE_OK;
339   struct SessionHandle *data = conn->data;
340   (void)instate; /* no use for this yet */
341
342   if(smtpcode/100 != 2) {
343     failf(data, "Access denied: %d", smtpcode);
344     result = CURLE_LOGIN_DENIED;
345     state(conn, SMTP_STOP);
346   }
347   else {
348     struct smtp_conn *smtpc = &conn->proto.smtpc;
349
350     /* send RCPT TO */
351     smtpc->rcpt = data->set.mail_rcpt;
352
353     if(smtpc->rcpt) {
354       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
355                              smtpc->rcpt->data);
356       if(result)
357         return result;
358     }
359
360     state(conn, SMTP_RCPT);
361   }
362   return result;
363 }
364
365 /* for RCPT responses */
366 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
367                                      int smtpcode,
368                                      smtpstate instate)
369 {
370   CURLcode result = CURLE_OK;
371   struct SessionHandle *data = conn->data;
372   (void)instate; /* no use for this yet */
373
374   if(smtpcode/100 != 2) {
375     failf(data, "Access denied: %d", smtpcode);
376     result = CURLE_LOGIN_DENIED;
377     state(conn, SMTP_STOP);
378   }
379   else {
380     struct smtp_conn *smtpc = &conn->proto.smtpc;
381
382     /* one RCPT is done, but if there's one more to send go on */
383     smtpc->rcpt = smtpc->rcpt->next;
384     if(smtpc->rcpt) {
385       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
386                              smtpc->rcpt->data);
387       if(result)
388         return result;
389
390       state(conn, SMTP_RCPT);
391       return CURLE_OK;
392     }
393
394     /* send DATA */
395     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA", "");
396     if(result)
397       return result;
398
399     state(conn, SMTP_DATA);
400   }
401   return result;
402 }
403
404 /* for the DATA response */
405 static CURLcode smtp_state_data_resp(struct connectdata *conn,
406                                      int smtpcode,
407                                      smtpstate instate)
408 {
409   CURLcode result = CURLE_OK;
410   struct SessionHandle *data = conn->data;
411   struct FTP *smtp = data->state.proto.smtp;
412
413   (void)instate; /* no use for this yet */
414
415   if(smtpcode != 354) {
416     state(conn, SMTP_STOP);
417     return CURLE_RECV_ERROR;
418   }
419
420   /* SMTP upload */
421   result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
422                                FIRSTSOCKET, smtp->bytecountp);
423
424   state(conn, SMTP_STOP);
425   return result;
426 }
427
428 /* for the POSTDATA response, which is received after the entire DATA
429    part has been sent off to the server */
430 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
431                                      int smtpcode,
432                                      smtpstate instate)
433 {
434   CURLcode result = CURLE_OK;
435   struct SessionHandle *data = conn->data;
436   struct FTP *smtp = data->state.proto.smtp;
437
438   (void)instate; /* no use for this yet */
439
440   if(smtpcode != 250)
441     result = CURLE_RECV_ERROR;
442
443   state(conn, SMTP_STOP);
444   return result;
445 }
446
447 static CURLcode smtp_statemach_act(struct connectdata *conn)
448 {
449   CURLcode result;
450   curl_socket_t sock = conn->sock[FIRSTSOCKET];
451   struct SessionHandle *data=conn->data;
452   int smtpcode;
453   struct smtp_conn *smtpc = &conn->proto.smtpc;
454   struct pingpong *pp = &smtpc->pp;
455   size_t nread = 0;
456
457   if(pp->sendleft)
458     /* we have a piece of a command still left to send */
459     return Curl_pp_flushsend(pp);
460
461   /* we read a piece of response */
462   result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
463   if(result)
464     return result;
465
466   if(smtpcode) {
467     /* we have now received a full SMTP server response */
468     switch(smtpc->state) {
469     case SMTP_SERVERGREET:
470       if(smtpcode/100 != 2) {
471         failf(data, "Got unexpected smtp-server response: %d", smtpcode);
472         return CURLE_FTP_WEIRD_SERVER_REPLY;
473       }
474
475       if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
476         /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
477            to TLS connection now */
478         result = Curl_pp_sendf(&smtpc->pp, "STARTTLS", NULL);
479         state(conn, SMTP_STARTTLS);
480       }
481       else
482         result = smtp_state_ehlo(conn);
483       if(result)
484         return result;
485       break;
486
487     case SMTP_EHLO:
488       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
489       break;
490
491     case SMTP_MAIL:
492       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
493       break;
494
495     case SMTP_RCPT:
496       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
497       break;
498
499     case SMTP_STARTTLS:
500       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
501       break;
502
503     case SMTP_DATA:
504       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
505       break;
506
507     case SMTP_POSTDATA:
508       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
509       break;
510
511     case SMTP_QUIT:
512       /* fallthrough, just stop! */
513     default:
514       /* internal error */
515       state(conn, SMTP_STOP);
516       break;
517     }
518   }
519   return result;
520 }
521
522 /* called repeatedly until done from multi.c */
523 static CURLcode smtp_multi_statemach(struct connectdata *conn,
524                                      bool *done)
525 {
526   struct smtp_conn *smtpc = &conn->proto.smtpc;
527   CURLcode result = Curl_pp_multi_statemach(&smtpc->pp);
528
529   *done = (bool)(smtpc->state == SMTP_STOP);
530
531   return result;
532 }
533
534 static CURLcode smtp_easy_statemach(struct connectdata *conn)
535 {
536   struct smtp_conn *smtpc = &conn->proto.smtpc;
537   struct pingpong *pp = &smtpc->pp;
538   CURLcode result = CURLE_OK;
539
540   while(smtpc->state != SMTP_STOP) {
541     result = Curl_pp_easy_statemach(pp);
542     if(result)
543       break;
544   }
545
546   return result;
547 }
548
549 /*
550  * Allocate and initialize the struct SMTP for the current SessionHandle.  If
551  * need be.
552  */
553 static CURLcode smtp_init(struct connectdata *conn)
554 {
555   struct SessionHandle *data = conn->data;
556   struct FTP *smtp = data->state.proto.smtp;
557   if(!smtp) {
558     smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
559     if(!smtp)
560       return CURLE_OUT_OF_MEMORY;
561   }
562
563   /* get some initial data into the smtp struct */
564   smtp->bytecountp = &data->req.bytecount;
565
566   /* No need to duplicate user+password, the connectdata struct won't change
567      during a session, but we re-init them here since on subsequent inits
568      since the conn struct may have changed or been replaced.
569   */
570   smtp->user = conn->user;
571   smtp->passwd = conn->passwd;
572
573   return CURLE_OK;
574 }
575
576 /*
577  * smtp_connect() should do everything that is to be considered a part of
578  * the connection phase.
579  *
580  * The variable 'done' points to will be TRUE if the protocol-layer connect
581  * phase is done when this function returns, or FALSE is not. When called as
582  * a part of the easy interface, it will always be TRUE.
583  */
584 static CURLcode smtp_connect(struct connectdata *conn,
585                                  bool *done) /* see description above */
586 {
587   CURLcode result;
588   struct smtp_conn *smtpc = &conn->proto.smtpc;
589   struct SessionHandle *data=conn->data;
590   struct pingpong *pp=&smtpc->pp;
591   const char *path = conn->data->state.path;
592   int len;
593
594   *done = FALSE; /* default to not done yet */
595
596   /* If there already is a protocol-specific struct allocated for this
597      sessionhandle, deal with it */
598   Curl_reset_reqproto(conn);
599
600   result = smtp_init(conn);
601   if(CURLE_OK != result)
602     return result;
603
604   /* We always support persistant connections on smtp */
605   conn->bits.close = FALSE;
606
607   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
608   pp->statemach_act = smtp_statemach_act;
609   pp->endofresp = smtp_endofresp;
610   pp->conn = conn;
611
612 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
613   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
614     /* for SMTP over HTTP proxy */
615     struct HTTP http_proxy;
616     struct FTP *smtp_save;
617
618     /* BLOCKING */
619     /* We want "seamless" SMTP operations through HTTP proxy tunnel */
620
621     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
622      * conn->proto.http; we want SMTP through HTTP and we have to change the
623      * member temporarily for connecting to the HTTP proxy. After
624      * Curl_proxyCONNECT we have to set back the member to the original struct
625      * SMTP pointer
626      */
627     smtp_save = data->state.proto.smtp;
628     memset(&http_proxy, 0, sizeof(http_proxy));
629     data->state.proto.http = &http_proxy;
630
631     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
632                                conn->host.name, conn->remote_port);
633
634     data->state.proto.smtp = smtp_save;
635
636     if(CURLE_OK != result)
637       return result;
638   }
639 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
640
641   if(conn->protocol & PROT_SMTPS) {
642     /* BLOCKING */
643     /* SMTPS is simply smtp with SSL for the control channel */
644     /* now, perform the SSL initialization for this socket */
645     result = Curl_ssl_connect(conn, FIRSTSOCKET);
646     if(result)
647       return result;
648   }
649
650   Curl_pp_init(pp); /* init the response reader stuff */
651
652   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
653   pp->statemach_act = smtp_statemach_act;
654   pp->endofresp = smtp_endofresp;
655   pp->conn = conn;
656
657   if(!*path)
658     path = "localhost";
659
660   /* url decode the path and use it as domain with EHLO */
661   smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
662
663   /* When we connect, we start in the state where we await the server greeting
664    */
665   state(conn, SMTP_SERVERGREET);
666
667   if(data->state.used_interface == Curl_if_multi)
668     result = smtp_multi_statemach(conn, done);
669   else {
670     result = smtp_easy_statemach(conn);
671     if(!result)
672       *done = TRUE;
673   }
674
675   return result;
676 }
677
678 /***********************************************************************
679  *
680  * smtp_done()
681  *
682  * The DONE function. This does what needs to be done after a single DO has
683  * performed.
684  *
685  * Input argument is already checked for validity.
686  */
687 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
688                           bool premature)
689 {
690   struct SessionHandle *data = conn->data;
691   struct FTP *smtp = data->state.proto.smtp;
692   CURLcode result=CURLE_OK;
693   ssize_t bytes_written;
694   (void)premature;
695
696   if(!smtp)
697     /* When the easy handle is removed from the multi while libcurl is still
698      * trying to resolve the host name, it seems that the smtp struct is not
699      * yet initialized, but the removal action calls Curl_done() which calls
700      * this function. So we simply return success if no smtp pointer is set.
701      */
702     return CURLE_OK;
703
704   if(status) {
705     conn->bits.close = TRUE; /* marked for closure */
706     result = status;      /* use the already set error code */
707   }
708   else
709     /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
710
711     /* write to socket (send away data) */
712     result = Curl_write(conn,
713                         conn->writesockfd,  /* socket to send to */
714                         SMTP_EOB,           /* buffer pointer */
715                         SMTP_EOB_LEN,       /* buffer size */
716                         &bytes_written);    /* actually sent away */
717
718
719   if(status == CURLE_OK) {
720     state(conn, SMTP_POSTDATA);
721     /* run the state-machine
722
723        TODO: when the multi interface is used, this _really_ should be using
724        the smtp_multi_statemach function but we have no general support for
725        non-blocking DONE operations, not in the multi state machine and with
726        Curl_done() invokes on several places in the code!
727     */
728     result = smtp_easy_statemach(conn);
729   }
730
731   /* clear these for next connection */
732   smtp->transfer = FTPTRANSFER_BODY;
733
734   return result;
735 }
736
737 /***********************************************************************
738  *
739  * smtp_perform()
740  *
741  * This is the actual DO function for SMTP. Get a file/directory according to
742  * the options previously setup.
743  */
744
745 static
746 CURLcode smtp_perform(struct connectdata *conn,
747                      bool *connected,  /* connect status after PASV / PORT */
748                      bool *dophase_done)
749 {
750   /* this is SMTP and no proxy */
751   CURLcode result=CURLE_OK;
752
753   DEBUGF(infof(conn->data, "DO phase starts\n"));
754
755   if(conn->data->set.opt_no_body) {
756     /* requested no body means no transfer... */
757     struct FTP *smtp = conn->data->state.proto.smtp;
758     smtp->transfer = FTPTRANSFER_INFO;
759   }
760
761   *dophase_done = FALSE; /* not done yet */
762
763   /* start the first command in the DO phase */
764   result = smtp_mail(conn);
765   if(result)
766     return result;
767
768   /* run the state-machine */
769   if(conn->data->state.used_interface == Curl_if_multi)
770     result = smtp_multi_statemach(conn, dophase_done);
771   else {
772     result = smtp_easy_statemach(conn);
773     *dophase_done = TRUE; /* with the easy interface we are done here */
774   }
775   *connected = conn->bits.tcpconnect;
776
777   if(*dophase_done)
778     DEBUGF(infof(conn->data, "DO phase is complete\n"));
779
780   return result;
781 }
782
783 /***********************************************************************
784  *
785  * smtp_do()
786  *
787  * This function is registered as 'curl_do' function. It decodes the path
788  * parts etc as a wrapper to the actual DO function (smtp_perform).
789  *
790  * The input argument is already checked for validity.
791  */
792 static CURLcode smtp_do(struct connectdata *conn, bool *done)
793 {
794   CURLcode retcode = CURLE_OK;
795
796   *done = FALSE; /* default to false */
797
798   /*
799     Since connections can be re-used between SessionHandles, this might be a
800     connection already existing but on a fresh SessionHandle struct so we must
801     make sure we have a good 'struct SMTP' to play with. For new connections,
802     the struct SMTP is allocated and setup in the smtp_connect() function.
803   */
804   Curl_reset_reqproto(conn);
805   retcode = smtp_init(conn);
806   if(retcode)
807     return retcode;
808
809   retcode = smtp_regular_transfer(conn, done);
810
811   return retcode;
812 }
813
814 /***********************************************************************
815  *
816  * smtp_quit()
817  *
818  * This should be called before calling sclose().  We should then wait for the
819  * response from the server before returning. The calling code should then try
820  * to close the connection.
821  *
822  */
823 static CURLcode smtp_quit(struct connectdata *conn)
824 {
825   CURLcode result = CURLE_OK;
826
827   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT", NULL);
828   if(result)
829     return result;
830   state(conn, SMTP_QUIT);
831
832   result = smtp_easy_statemach(conn);
833
834   return result;
835 }
836
837 /***********************************************************************
838  *
839  * smtp_disconnect()
840  *
841  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
842  * resources. BLOCKING.
843  */
844 static CURLcode smtp_disconnect(struct connectdata *conn)
845 {
846   struct smtp_conn *smtpc= &conn->proto.smtpc;
847
848   /* We cannot send quit unconditionally. If this connection is stale or
849      bad in any way, sending quit and waiting around here will make the
850      disconnect wait in vain and cause more problems than we need to.
851   */
852
853   /* The SMTP session may or may not have been allocated/setup at this
854      point! */
855   (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
856
857   Curl_pp_disconnect(&smtpc->pp);
858
859   return CURLE_OK;
860 }
861
862 /* call this when the DO phase has completed */
863 static CURLcode smtp_dophase_done(struct connectdata *conn,
864                                   bool connected)
865 {
866   CURLcode result = CURLE_OK;
867   struct FTP *smtp = conn->data->state.proto.smtp;
868   struct smtp_conn *smtpc= &conn->proto.smtpc;
869   (void)connected;
870
871   if(smtp->transfer != FTPTRANSFER_BODY)
872     /* no data to transfer */
873     result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
874
875   free(smtpc->domain);
876
877   return result;
878 }
879
880 /* called from multi.c while DOing */
881 static CURLcode smtp_doing(struct connectdata *conn,
882                                bool *dophase_done)
883 {
884   CURLcode result;
885   result = smtp_multi_statemach(conn, dophase_done);
886
887   if(*dophase_done) {
888     result = smtp_dophase_done(conn, FALSE /* not connected */);
889
890     DEBUGF(infof(conn->data, "DO phase is complete\n"));
891   }
892   return result;
893 }
894
895 /***********************************************************************
896  *
897  * smtp_regular_transfer()
898  *
899  * The input argument is already checked for validity.
900  *
901  * Performs all commands done before a regular transfer between a local and a
902  * remote host.
903  */
904 static
905 CURLcode smtp_regular_transfer(struct connectdata *conn,
906                               bool *dophase_done)
907 {
908   CURLcode result=CURLE_OK;
909   bool connected=FALSE;
910   struct SessionHandle *data = conn->data;
911   data->req.size = -1; /* make sure this is unknown at this point */
912
913   Curl_pgrsSetUploadCounter(data, 0);
914   Curl_pgrsSetDownloadCounter(data, 0);
915   Curl_pgrsSetUploadSize(data, 0);
916   Curl_pgrsSetDownloadSize(data, 0);
917
918   result = smtp_perform(conn,
919                         &connected, /* have we connected after PASV/PORT */
920                         dophase_done); /* all commands in the DO-phase done? */
921
922   if(CURLE_OK == result) {
923
924     if(!*dophase_done)
925       /* the DO phase has not completed yet */
926       return CURLE_OK;
927
928     result = smtp_dophase_done(conn, connected);
929     if(result)
930       return result;
931   }
932
933   return result;
934 }
935
936 static CURLcode smtp_setup_connection(struct connectdata * conn)
937 {
938   struct SessionHandle *data = conn->data;
939
940   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
941     /* Unless we have asked to tunnel smtp operations through the proxy, we
942        switch and use HTTP operations only */
943 #ifndef CURL_DISABLE_HTTP
944     if(conn->handler == &Curl_handler_smtp)
945       conn->handler = &Curl_handler_smtp_proxy;
946     else {
947 #ifdef USE_SSL
948       conn->handler = &Curl_handler_smtps_proxy;
949 #else
950       failf(data, "SMTPS not supported!");
951       return CURLE_UNSUPPORTED_PROTOCOL;
952 #endif
953     }
954     /*
955      * We explicitly mark this connection as persistent here as we're doing
956      * SMTP over HTTP and thus we accidentally avoid setting this value
957      * otherwise.
958      */
959     conn->bits.close = FALSE;
960 #else
961     failf(data, "SMTP over http proxy requires HTTP support built-in!");
962     return CURLE_UNSUPPORTED_PROTOCOL;
963 #endif
964   }
965
966   data->state.path++;   /* don't include the initial slash */
967
968   return CURLE_OK;
969 }
970
971 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, int nread)
972 {
973   /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
974    * the data and make sure it is sent as CRLF..CRLF instead, as
975    * otherwise it will wrongly be detected as end of data by the server.
976    */
977   int i;
978   int si;
979   struct smtp_conn *smtpc = &conn->proto.smtpc;
980   struct SessionHandle *data = conn->data;
981
982   if(data->state.scratch == NULL)
983     data->state.scratch = malloc(2*BUFSIZE);
984   if(data->state.scratch == NULL) {
985     failf (data, "Failed to alloc scratch buffer!");
986     return CURLE_OUT_OF_MEMORY;
987   }
988   /* This loop can be improved by some kind of Boyer-Moore style of
989      approach but that is saved for later... */
990   for(i = 0, si = 0; i < nread; i++, si++) {
991     int left = nread - i;
992
993     if(left>= (SMTP_EOB_LEN-smtpc->eob)) {
994       if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
995                  SMTP_EOB_LEN-smtpc->eob)) {
996         /* It matched, copy the replacement data to the target buffer
997            instead. Note that the replacement does not contain the
998            trailing CRLF but we instead continue to match on that one
999            to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1000         */
1001         memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1002                SMTP_EOB_REPL_LEN);
1003         si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
1004                                           it */
1005         i+=SMTP_EOB_LEN-smtpc->eob-1-2;
1006         smtpc->eob = 0; /* start over */
1007         continue;
1008       }
1009     }
1010     else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1011                     left)) {
1012       /* the last piece of the data matches the EOB so we can't send that
1013          until we know the rest of it */
1014       smtpc->eob += left;
1015       break;
1016     }
1017
1018     data->state.scratch[si] = data->req.upload_fromhere[i];
1019   } /* for() */
1020
1021   if(si != nread) {
1022     /* only use the new buffer if we replaced something */
1023     nread = si;
1024
1025     /* upload from the new (replaced) buffer instead */
1026     data->req.upload_fromhere = data->state.scratch;
1027
1028     /* set the new amount too */
1029     data->req.upload_present = nread;
1030   }
1031   return CURLE_OK;
1032 }
1033
1034 #endif /* CURL_DISABLE_SMTP */