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