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