blob: 654f4350591a94ffecd27bae74dfb397d215273b [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
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 *
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 */
97static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
98static CURLcode smtp_do(struct connectdata *conn, bool *done);
99static CURLcode smtp_done(struct connectdata *conn,
100 CURLcode, bool premature);
101static CURLcode smtp_connect(struct connectdata *conn, bool *done);
102static CURLcode smtp_disconnect(struct connectdata *conn);
103static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
104static int smtp_getsock(struct connectdata *conn,
105 curl_socket_t *socks,
106 int numsocks);
107static CURLcode smtp_doing(struct connectdata *conn,
108 bool *dophase_done);
109static CURLcode smtp_setup_connection(struct connectdata * conn);
110
111
112/*
113 * SMTP protocol handler.
114 */
115
116const 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
139const 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
162static 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
185static 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 */
207static 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! */
222static 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 "HELO",
232 "STARTTLS",
233 "MAIL",
234 "RCPT",
235 "DATA",
236 "POSTDATA",
237 "QUIT",
238 /* LAST */
239 };
240#endif
241 struct smtp_conn *smtpc = &conn->proto.smtpc;
242#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
243 if(smtpc->state != newstate)
244 infof(conn->data, "SMTP %p state change from %s to %s\n",
245 smtpc, names[smtpc->state], names[newstate]);
246#endif
247 smtpc->state = newstate;
248}
249
250static CURLcode smtp_state_ehlo(struct connectdata *conn)
251{
252 CURLcode result;
253 struct smtp_conn *smtpc = &conn->proto.smtpc;
254
255 /* send EHLO */
256 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "EHLO %s", smtpc->domain);
257
258 if(result)
259 return result;
260
261 state(conn, SMTP_EHLO);
262 return CURLE_OK;
263}
264
265static CURLcode smtp_state_helo(struct connectdata *conn)
266{
267 CURLcode result;
268 struct smtp_conn *smtpc = &conn->proto.smtpc;
269
270 /* send HELO */
271 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "HELO %s", smtpc->domain);
272
273 if(result)
274 return result;
275
276 state(conn, SMTP_HELO);
277 return CURLE_OK;
278}
279
280/* For the SMTP "protocol connect" and "doing" phases only */
281static int smtp_getsock(struct connectdata *conn,
282 curl_socket_t *socks,
283 int numsocks)
284{
285 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
286}
287
288/* for STARTTLS responses */
289static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
290 int smtpcode,
291 smtpstate instate)
292{
293 CURLcode result = CURLE_OK;
294 struct SessionHandle *data = conn->data;
295 (void)instate; /* no use for this yet */
296
297 if(smtpcode != 220) {
298 if(data->set.ftp_ssl == CURLUSESSL_TRY)
299 state(conn, SMTP_STOP);
300 else {
301 failf(data, "STARTTLS denied. %c", smtpcode);
302 result = CURLE_LOGIN_DENIED;
303 }
304 }
305 else {
306 /* Curl_ssl_connect is BLOCKING */
307 result = Curl_ssl_connect(conn, FIRSTSOCKET);
308 if(CURLE_OK == result) {
309 conn->protocol |= PROT_SMTPS;
310 result = smtp_state_ehlo(conn);
311 }
312 }
313 return result;
314}
315
316/* for EHLO responses */
317static CURLcode smtp_state_ehlo_resp(struct connectdata *conn,
318 int smtpcode,
319 smtpstate instate)
320{
321 CURLcode result = CURLE_OK;
322 struct SessionHandle *data = conn->data;
323
324 (void)instate; /* no use for this yet */
325
326 if(smtpcode/100 != 2) {
327 if(data->set.ftp_ssl <= CURLUSESSL_TRY)
328 result = smtp_state_helo(conn);
329 else {
330 failf(data, "Access denied: %d", smtpcode);
331 result = CURLE_LOGIN_DENIED;
332 }
333 }
334 else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
335 /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
336 to TLS connection now */
337 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS", NULL);
338 state(conn, SMTP_STARTTLS);
339 }
340 else {
341 /* end the connect phase */
342 state(conn, SMTP_STOP);
343 }
344 return result;
345}
346
347/* for HELO responses */
348static CURLcode smtp_state_helo_resp(struct connectdata *conn,
349 int smtpcode,
350 smtpstate instate)
351{
352 CURLcode result = CURLE_OK;
353 struct SessionHandle *data = conn->data;
354
355 (void)instate; /* no use for this yet */
356
357 if(smtpcode/100 != 2) {
358 failf(data, "Access denied: %d", smtpcode);
359 result = CURLE_LOGIN_DENIED;
360 }
361 else {
362 /* end the connect phase */
363 state(conn, SMTP_STOP);
364 }
365 return result;
366}
367
368/* start the DO phase */
369static CURLcode smtp_mail(struct connectdata *conn)
370{
371 CURLcode result = CURLE_OK;
372 struct SessionHandle *data = conn->data;
373
374 /* send MAIL */
375 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
376 data->set.str[STRING_MAIL_FROM]);
377 if(result)
378 return result;
379
380 state(conn, SMTP_MAIL);
381 return result;
382}
383
384static CURLcode smtp_rcpt_to(struct connectdata *conn)
385{
386 CURLcode result = CURLE_OK;
387 struct smtp_conn *smtpc = &conn->proto.smtpc;
388
389 /* send RCPT TO */
390 if(smtpc->rcpt) {
391 if(smtpc->rcpt->data[0] == '<')
392 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
393 smtpc->rcpt->data);
394 else
395 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
396 smtpc->rcpt->data);
397 if(!result)
398 state(conn, SMTP_RCPT);
399 }
400 return result;
401}
402
403/* for MAIL responses */
404static CURLcode smtp_state_mail_resp(struct connectdata *conn,
405 int smtpcode,
406 smtpstate instate)
407{
408 CURLcode result = CURLE_OK;
409 struct SessionHandle *data = conn->data;
410 (void)instate; /* no use for this yet */
411
412 if(smtpcode/100 != 2) {
413 failf(data, "Access denied: %d", smtpcode);
414 result = CURLE_LOGIN_DENIED;
415 state(conn, SMTP_STOP);
416 }
417 else {
418 struct smtp_conn *smtpc = &conn->proto.smtpc;
419 smtpc->rcpt = data->set.mail_rcpt;
420
421 result = smtp_rcpt_to(conn);
422 }
423
424 return result;
425}
426
427/* for RCPT responses */
428static CURLcode smtp_state_rcpt_resp(struct connectdata *conn,
429 int smtpcode,
430 smtpstate instate)
431{
432 CURLcode result = CURLE_OK;
433 struct SessionHandle *data = conn->data;
434 (void)instate; /* no use for this yet */
435
436 if(smtpcode/100 != 2) {
437 failf(data, "Access denied: %d", smtpcode);
438 result = CURLE_LOGIN_DENIED;
439 state(conn, SMTP_STOP);
440 }
441 else {
442 struct smtp_conn *smtpc = &conn->proto.smtpc;
443
444 if(smtpc->rcpt) {
445 smtpc->rcpt = smtpc->rcpt->next;
446 result = smtp_rcpt_to(conn);
447
448 /* if we failed or still is in RCPT sending, return */
449 if(result || smtpc->rcpt)
450 return result;
451 }
452
453 /* send DATA */
454 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "DATA", "");
455 if(result)
456 return result;
457
458 state(conn, SMTP_DATA);
459 }
460 return result;
461}
462
463/* for the DATA response */
464static CURLcode smtp_state_data_resp(struct connectdata *conn,
465 int smtpcode,
466 smtpstate instate)
467{
468 CURLcode result = CURLE_OK;
469 struct SessionHandle *data = conn->data;
470 struct FTP *smtp = data->state.proto.smtp;
471
472 (void)instate; /* no use for this yet */
473
474 if(smtpcode != 354) {
475 state(conn, SMTP_STOP);
476 return CURLE_RECV_ERROR;
477 }
478
479 /* SMTP upload */
480 result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
481 FIRSTSOCKET, smtp->bytecountp);
482
483 state(conn, SMTP_STOP);
484 return result;
485}
486
487/* for the POSTDATA response, which is received after the entire DATA
488 part has been sent off to the server */
489static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
490 int smtpcode,
491 smtpstate instate)
492{
493 CURLcode result = CURLE_OK;
494
495 (void)instate; /* no use for this yet */
496
497 if(smtpcode != 250)
498 result = CURLE_RECV_ERROR;
499
500 state(conn, SMTP_STOP);
501 return result;
502}
503
504static CURLcode smtp_statemach_act(struct connectdata *conn)
505{
506 CURLcode result;
507 curl_socket_t sock = conn->sock[FIRSTSOCKET];
508 struct SessionHandle *data=conn->data;
509 int smtpcode;
510 struct smtp_conn *smtpc = &conn->proto.smtpc;
511 struct pingpong *pp = &smtpc->pp;
512 size_t nread = 0;
513
514 if(pp->sendleft)
515 /* we have a piece of a command still left to send */
516 return Curl_pp_flushsend(pp);
517
518 /* we read a piece of response */
519 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
520 if(result)
521 return result;
522
523 if(smtpcode) {
524 /* we have now received a full SMTP server response */
525 switch(smtpc->state) {
526 case SMTP_SERVERGREET:
527 if(smtpcode/100 != 2) {
528 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
529 return CURLE_FTP_WEIRD_SERVER_REPLY;
530 }
531
532 result = smtp_state_ehlo(conn);
533 if(result)
534 return result;
535 break;
536
537 case SMTP_EHLO:
538 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
539 break;
540
541 case SMTP_HELO:
542 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
543 break;
544
545 case SMTP_MAIL:
546 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
547 break;
548
549 case SMTP_RCPT:
550 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
551 break;
552
553 case SMTP_STARTTLS:
554 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
555 break;
556
557 case SMTP_DATA:
558 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
559 break;
560
561 case SMTP_POSTDATA:
562 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
563 break;
564
565 case SMTP_QUIT:
566 /* fallthrough, just stop! */
567 default:
568 /* internal error */
569 state(conn, SMTP_STOP);
570 break;
571 }
572 }
573 return result;
574}
575
576/* called repeatedly until done from multi.c */
577static CURLcode smtp_multi_statemach(struct connectdata *conn,
578 bool *done)
579{
580 struct smtp_conn *smtpc = &conn->proto.smtpc;
581 CURLcode result = Curl_pp_multi_statemach(&smtpc->pp);
582
583 *done = (bool)(smtpc->state == SMTP_STOP);
584
585 return result;
586}
587
588static CURLcode smtp_easy_statemach(struct connectdata *conn)
589{
590 struct smtp_conn *smtpc = &conn->proto.smtpc;
591 struct pingpong *pp = &smtpc->pp;
592 CURLcode result = CURLE_OK;
593
594 while(smtpc->state != SMTP_STOP) {
595 result = Curl_pp_easy_statemach(pp);
596 if(result)
597 break;
598 }
599
600 return result;
601}
602
603/*
604 * Allocate and initialize the struct SMTP for the current SessionHandle. If
605 * need be.
606 */
607static CURLcode smtp_init(struct connectdata *conn)
608{
609 struct SessionHandle *data = conn->data;
610 struct FTP *smtp = data->state.proto.smtp;
611 if(!smtp) {
612 smtp = data->state.proto.smtp = calloc(sizeof(struct FTP), 1);
613 if(!smtp)
614 return CURLE_OUT_OF_MEMORY;
615 }
616
617 /* get some initial data into the smtp struct */
618 smtp->bytecountp = &data->req.bytecount;
619
620 /* No need to duplicate user+password, the connectdata struct won't change
621 during a session, but we re-init them here since on subsequent inits
622 since the conn struct may have changed or been replaced.
623 */
624 smtp->user = conn->user;
625 smtp->passwd = conn->passwd;
626
627 return CURLE_OK;
628}
629
630/*
631 * smtp_connect() should do everything that is to be considered a part of
632 * the connection phase.
633 *
634 * The variable 'done' points to will be TRUE if the protocol-layer connect
635 * phase is done when this function returns, or FALSE is not. When called as
636 * a part of the easy interface, it will always be TRUE.
637 */
638static CURLcode smtp_connect(struct connectdata *conn,
639 bool *done) /* see description above */
640{
641 CURLcode result;
642 struct smtp_conn *smtpc = &conn->proto.smtpc;
643 struct SessionHandle *data=conn->data;
644 struct pingpong *pp=&smtpc->pp;
645 const char *path = conn->data->state.path;
646 int len;
647
648#ifdef HAVE_GETHOSTNAME
649 char localhost[1024 + 1];
650#endif
651
652 *done = FALSE; /* default to not done yet */
653
654 /* If there already is a protocol-specific struct allocated for this
655 sessionhandle, deal with it */
656 Curl_reset_reqproto(conn);
657
658 result = smtp_init(conn);
659 if(CURLE_OK != result)
660 return result;
661
662 /* We always support persistant connections on smtp */
663 conn->bits.close = FALSE;
664
665 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
666 pp->statemach_act = smtp_statemach_act;
667 pp->endofresp = smtp_endofresp;
668 pp->conn = conn;
669
670#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
671 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
672 /* for SMTP over HTTP proxy */
673 struct HTTP http_proxy;
674 struct FTP *smtp_save;
675
676 /* BLOCKING */
677 /* We want "seamless" SMTP operations through HTTP proxy tunnel */
678
679 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
680 * conn->proto.http; we want SMTP through HTTP and we have to change the
681 * member temporarily for connecting to the HTTP proxy. After
682 * Curl_proxyCONNECT we have to set back the member to the original struct
683 * SMTP pointer
684 */
685 smtp_save = data->state.proto.smtp;
686 memset(&http_proxy, 0, sizeof(http_proxy));
687 data->state.proto.http = &http_proxy;
688
689 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
690 conn->host.name, conn->remote_port);
691
692 data->state.proto.smtp = smtp_save;
693
694 if(CURLE_OK != result)
695 return result;
696 }
697#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */
698
699 if(conn->protocol & PROT_SMTPS) {
700 /* BLOCKING */
701 /* SMTPS is simply smtp with SSL for the control channel */
702 /* now, perform the SSL initialization for this socket */
703 result = Curl_ssl_connect(conn, FIRSTSOCKET);
704 if(result)
705 return result;
706 }
707
708 Curl_pp_init(pp); /* init the response reader stuff */
709
710 pp->response_time = RESP_TIMEOUT; /* set default response time-out */
711 pp->statemach_act = smtp_statemach_act;
712 pp->endofresp = smtp_endofresp;
713 pp->conn = conn;
714
715 if(!*path) {
716#ifdef HAVE_GETHOSTNAME
717 if(!gethostname(localhost, sizeof localhost))
718 path = localhost;
719 else
720#endif
721 path = "localhost";
722 }
723
724 /* url decode the path and use it as domain with EHLO */
725 smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len);
726 if (!smtpc->domain)
727 return CURLE_OUT_OF_MEMORY;
728
729 /* When we connect, we start in the state where we await the server greeting
730 */
731 state(conn, SMTP_SERVERGREET);
732
733 if(data->state.used_interface == Curl_if_multi)
734 result = smtp_multi_statemach(conn, done);
735 else {
736 result = smtp_easy_statemach(conn);
737 if(!result)
738 *done = TRUE;
739 }
740
741 return result;
742}
743
744/***********************************************************************
745 *
746 * smtp_done()
747 *
748 * The DONE function. This does what needs to be done after a single DO has
749 * performed.
750 *
751 * Input argument is already checked for validity.
752 */
753static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
754 bool premature)
755{
756 struct SessionHandle *data = conn->data;
757 struct FTP *smtp = data->state.proto.smtp;
758 CURLcode result=CURLE_OK;
759 ssize_t bytes_written;
760 (void)premature;
761
762 if(!smtp)
763 /* When the easy handle is removed from the multi while libcurl is still
764 * trying to resolve the host name, it seems that the smtp struct is not
765 * yet initialized, but the removal action calls Curl_done() which calls
766 * this function. So we simply return success if no smtp pointer is set.
767 */
768 return CURLE_OK;
769
770 if(status) {
771 conn->bits.close = TRUE; /* marked for closure */
772 result = status; /* use the already set error code */
773 }
774 else
775 /* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
776
777 /* write to socket (send away data) */
778 result = Curl_write(conn,
779 conn->writesockfd, /* socket to send to */
780 SMTP_EOB, /* buffer pointer */
781 SMTP_EOB_LEN, /* buffer size */
782 &bytes_written); /* actually sent away */
783
784
785 if(status == CURLE_OK) {
786 struct smtp_conn *smtpc = &conn->proto.smtpc;
787 struct pingpong *pp= &smtpc->pp;
788 pp->response = Curl_tvnow(); /* timeout relative now */
789
790 state(conn, SMTP_POSTDATA);
791 /* run the state-machine
792
793 TODO: when the multi interface is used, this _really_ should be using
794 the smtp_multi_statemach function but we have no general support for
795 non-blocking DONE operations, not in the multi state machine and with
796 Curl_done() invokes on several places in the code!
797 */
798 result = smtp_easy_statemach(conn);
799 }
800
801 /* clear these for next connection */
802 smtp->transfer = FTPTRANSFER_BODY;
803
804 return result;
805}
806
807/***********************************************************************
808 *
809 * smtp_perform()
810 *
811 * This is the actual DO function for SMTP. Get a file/directory according to
812 * the options previously setup.
813 */
814
815static
816CURLcode smtp_perform(struct connectdata *conn,
817 bool *connected, /* connect status after PASV / PORT */
818 bool *dophase_done)
819{
820 /* this is SMTP and no proxy */
821 CURLcode result=CURLE_OK;
822
823 DEBUGF(infof(conn->data, "DO phase starts\n"));
824
825 if(conn->data->set.opt_no_body) {
826 /* requested no body means no transfer... */
827 struct FTP *smtp = conn->data->state.proto.smtp;
828 smtp->transfer = FTPTRANSFER_INFO;
829 }
830
831 *dophase_done = FALSE; /* not done yet */
832
833 /* start the first command in the DO phase */
834 result = smtp_mail(conn);
835 if(result)
836 return result;
837
838 /* run the state-machine */
839 if(conn->data->state.used_interface == Curl_if_multi)
840 result = smtp_multi_statemach(conn, dophase_done);
841 else {
842 result = smtp_easy_statemach(conn);
843 *dophase_done = TRUE; /* with the easy interface we are done here */
844 }
845 *connected = conn->bits.tcpconnect;
846
847 if(*dophase_done)
848 DEBUGF(infof(conn->data, "DO phase is complete\n"));
849
850 return result;
851}
852
853/***********************************************************************
854 *
855 * smtp_do()
856 *
857 * This function is registered as 'curl_do' function. It decodes the path
858 * parts etc as a wrapper to the actual DO function (smtp_perform).
859 *
860 * The input argument is already checked for validity.
861 */
862static CURLcode smtp_do(struct connectdata *conn, bool *done)
863{
864 CURLcode retcode = CURLE_OK;
865
866 *done = FALSE; /* default to false */
867
868 /*
869 Since connections can be re-used between SessionHandles, this might be a
870 connection already existing but on a fresh SessionHandle struct so we must
871 make sure we have a good 'struct SMTP' to play with. For new connections,
872 the struct SMTP is allocated and setup in the smtp_connect() function.
873 */
874 Curl_reset_reqproto(conn);
875 retcode = smtp_init(conn);
876 if(retcode)
877 return retcode;
878
879 retcode = smtp_regular_transfer(conn, done);
880
881 return retcode;
882}
883
884/***********************************************************************
885 *
886 * smtp_quit()
887 *
888 * This should be called before calling sclose(). We should then wait for the
889 * response from the server before returning. The calling code should then try
890 * to close the connection.
891 *
892 */
893static CURLcode smtp_quit(struct connectdata *conn)
894{
895 CURLcode result = CURLE_OK;
896
897 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "QUIT", NULL);
898 if(result)
899 return result;
900 state(conn, SMTP_QUIT);
901
902 result = smtp_easy_statemach(conn);
903
904 return result;
905}
906
907/***********************************************************************
908 *
909 * smtp_disconnect()
910 *
911 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
912 * resources. BLOCKING.
913 */
914static CURLcode smtp_disconnect(struct connectdata *conn)
915{
916 struct smtp_conn *smtpc= &conn->proto.smtpc;
917
918 /* We cannot send quit unconditionally. If this connection is stale or
919 bad in any way, sending quit and waiting around here will make the
920 disconnect wait in vain and cause more problems than we need to.
921 */
922
923 /* The SMTP session may or may not have been allocated/setup at this
924 point! */
925 if (smtpc->pp.conn)
926 (void)smtp_quit(conn); /* ignore errors on the LOGOUT */
927
928 Curl_pp_disconnect(&smtpc->pp);
929
930 return CURLE_OK;
931}
932
933/* call this when the DO phase has completed */
934static CURLcode smtp_dophase_done(struct connectdata *conn,
935 bool connected)
936{
937 CURLcode result = CURLE_OK;
938 struct FTP *smtp = conn->data->state.proto.smtp;
939 struct smtp_conn *smtpc= &conn->proto.smtpc;
940 (void)connected;
941
942 if(smtp->transfer != FTPTRANSFER_BODY)
943 /* no data to transfer */
944 result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
945
946 free(smtpc->domain);
947
948 return result;
949}
950
951/* called from multi.c while DOing */
952static CURLcode smtp_doing(struct connectdata *conn,
953 bool *dophase_done)
954{
955 CURLcode result;
956 result = smtp_multi_statemach(conn, dophase_done);
957
958 if(*dophase_done) {
959 result = smtp_dophase_done(conn, FALSE /* not connected */);
960
961 DEBUGF(infof(conn->data, "DO phase is complete\n"));
962 }
963 return result;
964}
965
966/***********************************************************************
967 *
968 * smtp_regular_transfer()
969 *
970 * The input argument is already checked for validity.
971 *
972 * Performs all commands done before a regular transfer between a local and a
973 * remote host.
974 */
975static
976CURLcode smtp_regular_transfer(struct connectdata *conn,
977 bool *dophase_done)
978{
979 CURLcode result=CURLE_OK;
980 bool connected=FALSE;
981 struct SessionHandle *data = conn->data;
982 data->req.size = -1; /* make sure this is unknown at this point */
983
984 Curl_pgrsSetUploadCounter(data, 0);
985 Curl_pgrsSetDownloadCounter(data, 0);
986 Curl_pgrsSetUploadSize(data, 0);
987 Curl_pgrsSetDownloadSize(data, 0);
988
989 result = smtp_perform(conn,
990 &connected, /* have we connected after PASV/PORT */
991 dophase_done); /* all commands in the DO-phase done? */
992
993 if(CURLE_OK == result) {
994
995 if(!*dophase_done)
996 /* the DO phase has not completed yet */
997 return CURLE_OK;
998
999 result = smtp_dophase_done(conn, connected);
1000 if(result)
1001 return result;
1002 }
1003
1004 return result;
1005}
1006
1007static CURLcode smtp_setup_connection(struct connectdata * conn)
1008{
1009 struct SessionHandle *data = conn->data;
1010
1011 if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
1012 /* Unless we have asked to tunnel smtp operations through the proxy, we
1013 switch and use HTTP operations only */
1014#ifndef CURL_DISABLE_HTTP
1015 if(conn->handler == &Curl_handler_smtp)
1016 conn->handler = &Curl_handler_smtp_proxy;
1017 else {
1018#ifdef USE_SSL
1019 conn->handler = &Curl_handler_smtps_proxy;
1020#else
1021 failf(data, "SMTPS not supported!");
1022 return CURLE_UNSUPPORTED_PROTOCOL;
1023#endif
1024 }
1025 /*
1026 * We explicitly mark this connection as persistent here as we're doing
1027 * SMTP over HTTP and thus we accidentally avoid setting this value
1028 * otherwise.
1029 */
1030 conn->bits.close = FALSE;
1031#else
1032 failf(data, "SMTP over http proxy requires HTTP support built-in!");
1033 return CURLE_UNSUPPORTED_PROTOCOL;
1034#endif
1035 }
1036
1037 data->state.path++; /* don't include the initial slash */
1038
1039 return CURLE_OK;
1040}
1041
1042CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
1043{
1044 /* When sending SMTP payload, we must detect CRLF.CRLF sequences in
1045 * the data and make sure it is sent as CRLF..CRLF instead, as
1046 * otherwise it will wrongly be detected as end of data by the server.
1047 */
1048 ssize_t i;
1049 ssize_t si;
1050 struct smtp_conn *smtpc = &conn->proto.smtpc;
1051 struct SessionHandle *data = conn->data;
1052
1053 if(data->state.scratch == NULL)
1054 data->state.scratch = malloc(2*BUFSIZE);
1055 if(data->state.scratch == NULL) {
1056 failf (data, "Failed to alloc scratch buffer!");
1057 return CURLE_OUT_OF_MEMORY;
1058 }
1059 /* This loop can be improved by some kind of Boyer-Moore style of
1060 approach but that is saved for later... */
1061 for(i = 0, si = 0; i < nread; i++, si++) {
1062 ssize_t left = nread - i;
1063
1064 if(left>= (ssize_t)(SMTP_EOB_LEN-smtpc->eob)) {
1065 if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1066 SMTP_EOB_LEN-smtpc->eob)) {
1067 /* It matched, copy the replacement data to the target buffer
1068 instead. Note that the replacement does not contain the
1069 trailing CRLF but we instead continue to match on that one
1070 to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
1071 */
1072 memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
1073 SMTP_EOB_REPL_LEN);
1074 si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
1075 it */
1076 i+=SMTP_EOB_LEN-smtpc->eob-1-2;
1077 smtpc->eob = 0; /* start over */
1078 continue;
1079 }
1080 }
1081 else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
1082 left)) {
1083 /* the last piece of the data matches the EOB so we can't send that
1084 until we know the rest of it */
1085 smtpc->eob += left;
1086 break;
1087 }
1088
1089 data->state.scratch[si] = data->req.upload_fromhere[i];
1090 } /* for() */
1091
1092 if(si != nread) {
1093 /* only use the new buffer if we replaced something */
1094 nread = si;
1095
1096 /* upload from the new (replaced) buffer instead */
1097 data->req.upload_fromhere = data->state.scratch;
1098
1099 /* set the new amount too */
1100 data->req.upload_present = nread;
1101 }
1102 return CURLE_OK;
1103}
1104
1105#endif /* CURL_DISABLE_SMTP */