source: EcnlProtoTool/trunk/curl-7.57.0/lib/smtp.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc
File size: 45.4 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2017, 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 https://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 * RFC1870 SMTP Service Extension for Message Size
22 * RFC2195 CRAM-MD5 authentication
23 * RFC2831 DIGEST-MD5 authentication
24 * RFC3207 SMTP over TLS
25 * RFC4422 Simple Authentication and Security Layer (SASL)
26 * RFC4616 PLAIN authentication
27 * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
28 * RFC4954 SMTP Authentication
29 * RFC5321 SMTP protocol
30 * RFC6749 OAuth 2.0 Authorization Framework
31 * Draft SMTP URL Interface <draft-earhart-url-smtp-00.txt>
32 * Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
33 *
34 ***************************************************************************/
35
36#include "curl_setup.h"
37
38#ifndef CURL_DISABLE_SMTP
39
40#ifdef HAVE_NETINET_IN_H
41#include <netinet/in.h>
42#endif
43#ifdef HAVE_ARPA_INET_H
44#include <arpa/inet.h>
45#endif
46#ifdef HAVE_UTSNAME_H
47#include <sys/utsname.h>
48#endif
49#ifdef HAVE_NETDB_H
50#include <netdb.h>
51#endif
52#ifdef __VMS
53#include <in.h>
54#include <inet.h>
55#endif
56
57#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
58#undef in_addr_t
59#define in_addr_t unsigned long
60#endif
61
62#include <curl/curl.h>
63#include "urldata.h"
64#include "sendf.h"
65#include "hostip.h"
66#include "progress.h"
67#include "transfer.h"
68#include "escape.h"
69#include "http.h" /* for HTTP proxy tunnel stuff */
70#include "mime.h"
71#include "socks.h"
72#include "smtp.h"
73#include "strtoofft.h"
74#include "strcase.h"
75#include "vtls/vtls.h"
76#include "connect.h"
77#include "strerror.h"
78#include "select.h"
79#include "multiif.h"
80#include "url.h"
81#include "curl_gethostname.h"
82#include "curl_sasl.h"
83#include "warnless.h"
84/* The last 3 #include files should be in this order */
85#include "curl_printf.h"
86#include "curl_memory.h"
87#include "memdebug.h"
88
89/* Local API functions */
90static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
91static CURLcode smtp_do(struct connectdata *conn, bool *done);
92static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
93 bool premature);
94static CURLcode smtp_connect(struct connectdata *conn, bool *done);
95static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
96static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
97static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
98 int numsocks);
99static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
100static CURLcode smtp_setup_connection(struct connectdata *conn);
101static CURLcode smtp_parse_url_options(struct connectdata *conn);
102static CURLcode smtp_parse_url_path(struct connectdata *conn);
103static CURLcode smtp_parse_custom_request(struct connectdata *conn);
104static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
105 const char *initresp);
106static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
107static void smtp_get_message(char *buffer, char **outptr);
108
109/*
110 * SMTP protocol handler.
111 */
112
113const struct Curl_handler Curl_handler_smtp = {
114 "SMTP", /* scheme */
115 smtp_setup_connection, /* setup_connection */
116 smtp_do, /* do_it */
117 smtp_done, /* done */
118 ZERO_NULL, /* do_more */
119 smtp_connect, /* connect_it */
120 smtp_multi_statemach, /* connecting */
121 smtp_doing, /* doing */
122 smtp_getsock, /* proto_getsock */
123 smtp_getsock, /* doing_getsock */
124 ZERO_NULL, /* domore_getsock */
125 ZERO_NULL, /* perform_getsock */
126 smtp_disconnect, /* disconnect */
127 ZERO_NULL, /* readwrite */
128 ZERO_NULL, /* connection_check */
129 PORT_SMTP, /* defport */
130 CURLPROTO_SMTP, /* protocol */
131 PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
132 PROTOPT_URLOPTIONS
133};
134
135#ifdef USE_SSL
136/*
137 * SMTPS protocol handler.
138 */
139
140const struct Curl_handler Curl_handler_smtps = {
141 "SMTPS", /* scheme */
142 smtp_setup_connection, /* setup_connection */
143 smtp_do, /* do_it */
144 smtp_done, /* done */
145 ZERO_NULL, /* do_more */
146 smtp_connect, /* connect_it */
147 smtp_multi_statemach, /* connecting */
148 smtp_doing, /* doing */
149 smtp_getsock, /* proto_getsock */
150 smtp_getsock, /* doing_getsock */
151 ZERO_NULL, /* domore_getsock */
152 ZERO_NULL, /* perform_getsock */
153 smtp_disconnect, /* disconnect */
154 ZERO_NULL, /* readwrite */
155 ZERO_NULL, /* connection_check */
156 PORT_SMTPS, /* defport */
157 CURLPROTO_SMTPS, /* protocol */
158 PROTOPT_CLOSEACTION | PROTOPT_SSL
159 | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
160};
161#endif
162
163/* SASL parameters for the smtp protocol */
164static const struct SASLproto saslsmtp = {
165 "smtp", /* The service name */
166 334, /* Code received when continuation is expected */
167 235, /* Code to receive upon authentication success */
168 512 - 8, /* Maximum initial response length (no max) */
169 smtp_perform_auth, /* Send authentication command */
170 smtp_continue_auth, /* Send authentication continuation */
171 smtp_get_message /* Get SASL response message */
172};
173
174#ifdef USE_SSL
175static void smtp_to_smtps(struct connectdata *conn)
176{
177 /* Change the connection handler */
178 conn->handler = &Curl_handler_smtps;
179
180 /* Set the connection's upgraded to TLS flag */
181 conn->tls_upgraded = TRUE;
182}
183#else
184#define smtp_to_smtps(x) Curl_nop_stmt
185#endif
186
187/***********************************************************************
188 *
189 * smtp_endofresp()
190 *
191 * Checks for an ending SMTP status code at the start of the given string, but
192 * also detects various capabilities from the EHLO response including the
193 * supported authentication mechanisms.
194 */
195static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
196 int *resp)
197{
198 struct smtp_conn *smtpc = &conn->proto.smtpc;
199 bool result = FALSE;
200
201 /* Nothing for us */
202 if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
203 return FALSE;
204
205 /* Do we have a command response? This should be the response code followed
206 by a space and optionally some text as per RFC-5321 and as outlined in
207 Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
208 only send the response code instead as per Section 4.2. */
209 if(line[3] == ' ' || len == 5) {
210 result = TRUE;
211 *resp = curlx_sltosi(strtol(line, NULL, 10));
212
213 /* Make sure real server never sends internal value */
214 if(*resp == 1)
215 *resp = 0;
216 }
217 /* Do we have a multiline (continuation) response? */
218 else if(line[3] == '-' &&
219 (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
220 result = TRUE;
221 *resp = 1; /* Internal response code */
222 }
223
224 return result;
225}
226
227/***********************************************************************
228 *
229 * smtp_get_message()
230 *
231 * Gets the authentication message from the response buffer.
232 */
233static void smtp_get_message(char *buffer, char **outptr)
234{
235 size_t len = 0;
236 char *message = NULL;
237
238 /* Find the start of the message */
239 for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
240 ;
241
242 /* Find the end of the message */
243 for(len = strlen(message); len--;)
244 if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
245 message[len] != '\t')
246 break;
247
248 /* Terminate the message */
249 if(++len) {
250 message[len] = '\0';
251 }
252
253 *outptr = message;
254}
255
256/***********************************************************************
257 *
258 * state()
259 *
260 * This is the ONLY way to change SMTP state!
261 */
262static void state(struct connectdata *conn, smtpstate newstate)
263{
264 struct smtp_conn *smtpc = &conn->proto.smtpc;
265#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
266 /* for debug purposes */
267 static const char * const names[] = {
268 "STOP",
269 "SERVERGREET",
270 "EHLO",
271 "HELO",
272 "STARTTLS",
273 "UPGRADETLS",
274 "AUTH",
275 "COMMAND",
276 "MAIL",
277 "RCPT",
278 "DATA",
279 "POSTDATA",
280 "QUIT",
281 /* LAST */
282 };
283
284 if(smtpc->state != newstate)
285 infof(conn->data, "SMTP %p state change from %s to %s\n",
286 (void *)smtpc, names[smtpc->state], names[newstate]);
287#endif
288
289 smtpc->state = newstate;
290}
291
292/***********************************************************************
293 *
294 * smtp_perform_ehlo()
295 *
296 * Sends the EHLO command to not only initialise communication with the ESMTP
297 * server but to also obtain a list of server side supported capabilities.
298 */
299static CURLcode smtp_perform_ehlo(struct connectdata *conn)
300{
301 CURLcode result = CURLE_OK;
302 struct smtp_conn *smtpc = &conn->proto.smtpc;
303
304 smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
305 smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
306 used for esmtp connections */
307 smtpc->tls_supported = FALSE; /* Clear the TLS capability */
308 smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
309
310 /* Send the EHLO command */
311 result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
312
313 if(!result)
314 state(conn, SMTP_EHLO);
315
316 return result;
317}
318
319/***********************************************************************
320 *
321 * smtp_perform_helo()
322 *
323 * Sends the HELO command to initialise communication with the SMTP server.
324 */
325static CURLcode smtp_perform_helo(struct connectdata *conn)
326{
327 CURLcode result = CURLE_OK;
328 struct smtp_conn *smtpc = &conn->proto.smtpc;
329
330 smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
331 in smtp connections */
332
333 /* Send the HELO command */
334 result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
335
336 if(!result)
337 state(conn, SMTP_HELO);
338
339 return result;
340}
341
342/***********************************************************************
343 *
344 * smtp_perform_starttls()
345 *
346 * Sends the STLS command to start the upgrade to TLS.
347 */
348static CURLcode smtp_perform_starttls(struct connectdata *conn)
349{
350 CURLcode result = CURLE_OK;
351
352 /* Send the STARTTLS command */
353 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
354
355 if(!result)
356 state(conn, SMTP_STARTTLS);
357
358 return result;
359}
360
361/***********************************************************************
362 *
363 * smtp_perform_upgrade_tls()
364 *
365 * Performs the upgrade to TLS.
366 */
367static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
368{
369 CURLcode result = CURLE_OK;
370 struct smtp_conn *smtpc = &conn->proto.smtpc;
371
372 /* Start the SSL connection */
373 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
374
375 if(!result) {
376 if(smtpc->state != SMTP_UPGRADETLS)
377 state(conn, SMTP_UPGRADETLS);
378
379 if(smtpc->ssldone) {
380 smtp_to_smtps(conn);
381 result = smtp_perform_ehlo(conn);
382 }
383 }
384
385 return result;
386}
387
388/***********************************************************************
389 *
390 * smtp_perform_auth()
391 *
392 * Sends an AUTH command allowing the client to login with the given SASL
393 * authentication mechanism.
394 */
395static CURLcode smtp_perform_auth(struct connectdata *conn,
396 const char *mech,
397 const char *initresp)
398{
399 CURLcode result = CURLE_OK;
400 struct smtp_conn *smtpc = &conn->proto.smtpc;
401
402 if(initresp) { /* AUTH <mech> ...<crlf> */
403 /* Send the AUTH command with the initial response */
404 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
405 }
406 else {
407 /* Send the AUTH command */
408 result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
409 }
410
411 return result;
412}
413
414/***********************************************************************
415 *
416 * smtp_continue_auth()
417 *
418 * Sends SASL continuation data or cancellation.
419 */
420static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
421{
422 struct smtp_conn *smtpc = &conn->proto.smtpc;
423
424 return Curl_pp_sendf(&smtpc->pp, "%s", resp);
425}
426
427/***********************************************************************
428 *
429 * smtp_perform_authentication()
430 *
431 * Initiates the authentication sequence, with the appropriate SASL
432 * authentication mechanism.
433 */
434static CURLcode smtp_perform_authentication(struct connectdata *conn)
435{
436 CURLcode result = CURLE_OK;
437 struct smtp_conn *smtpc = &conn->proto.smtpc;
438 saslprogress progress;
439
440 /* Check we have enough data to authenticate with, and the
441 server supports authentiation, and end the connect phase if not */
442 if(!smtpc->auth_supported ||
443 !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
444 state(conn, SMTP_STOP);
445 return result;
446 }
447
448 /* Calculate the SASL login details */
449 result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
450
451 if(!result) {
452 if(progress == SASL_INPROGRESS)
453 state(conn, SMTP_AUTH);
454 else {
455 /* Other mechanisms not supported */
456 infof(conn->data, "No known authentication mechanisms supported!\n");
457 result = CURLE_LOGIN_DENIED;
458 }
459 }
460
461 return result;
462}
463
464/***********************************************************************
465 *
466 * smtp_perform_command()
467 *
468 * Sends a SMTP based command.
469 */
470static CURLcode smtp_perform_command(struct connectdata *conn)
471{
472 CURLcode result = CURLE_OK;
473 struct Curl_easy *data = conn->data;
474 struct SMTP *smtp = data->req.protop;
475
476 /* Send the command */
477 if(smtp->rcpt)
478 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
479 smtp->custom && smtp->custom[0] != '\0' ?
480 smtp->custom : "VRFY",
481 smtp->rcpt->data);
482 else
483 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
484 smtp->custom && smtp->custom[0] != '\0' ?
485 smtp->custom : "HELP");
486
487 if(!result)
488 state(conn, SMTP_COMMAND);
489
490 return result;
491}
492
493/***********************************************************************
494 *
495 * smtp_perform_mail()
496 *
497 * Sends an MAIL command to initiate the upload of a message.
498 */
499static CURLcode smtp_perform_mail(struct connectdata *conn)
500{
501 char *from = NULL;
502 char *auth = NULL;
503 char *size = NULL;
504 CURLcode result = CURLE_OK;
505 struct Curl_easy *data = conn->data;
506
507 /* Calculate the FROM parameter */
508 if(!data->set.str[STRING_MAIL_FROM])
509 /* Null reverse-path, RFC-5321, sect. 3.6.3 */
510 from = strdup("<>");
511 else if(data->set.str[STRING_MAIL_FROM][0] == '<')
512 from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
513 else
514 from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
515
516 if(!from)
517 return CURLE_OUT_OF_MEMORY;
518
519 /* Calculate the optional AUTH parameter */
520 if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
521 if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
522 auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
523 else
524 /* Empty AUTH, RFC-2554, sect. 5 */
525 auth = strdup("<>");
526
527 if(!auth) {
528 free(from);
529
530 return CURLE_OUT_OF_MEMORY;
531 }
532 }
533
534 /* Prepare the mime data if some. */
535 if(data->set.mimepost.kind != MIMEKIND_NONE) {
536 /* Use the whole structure as data. */
537 data->set.mimepost.flags &= ~MIME_BODY_ONLY;
538
539 /* Add external headers and mime version. */
540 curl_mime_headers(&data->set.mimepost, data->set.headers, 0);
541 result = Curl_mime_prepare_headers(&data->set.mimepost, NULL,
542 NULL, MIMESTRATEGY_MAIL);
543
544 if(!result)
545 if(!Curl_checkheaders(conn, "Mime-Version"))
546 result = Curl_mime_add_header(&data->set.mimepost.curlheaders,
547 "Mime-Version: 1.0");
548
549 /* Make sure we will read the entire mime structure. */
550 if(!result)
551 result = Curl_mime_rewind(&data->set.mimepost);
552
553 if(result) {
554 free(from);
555 free(auth);
556 return result;
557 }
558
559 data->state.infilesize = Curl_mime_size(&data->set.mimepost);
560
561 /* Read from mime structure. */
562 data->state.fread_func = (curl_read_callback) Curl_mime_read;
563 data->state.in = (void *) &data->set.mimepost;
564 }
565
566 /* Calculate the optional SIZE parameter */
567 if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
568 size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
569
570 if(!size) {
571 free(from);
572 free(auth);
573
574 return CURLE_OUT_OF_MEMORY;
575 }
576 }
577
578 /* Send the MAIL command */
579 if(!auth && !size)
580 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
581 "MAIL FROM:%s", from);
582 else if(auth && !size)
583 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
584 "MAIL FROM:%s AUTH=%s", from, auth);
585 else if(auth && size)
586 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
587 "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
588 else
589 result = Curl_pp_sendf(&conn->proto.smtpc.pp,
590 "MAIL FROM:%s SIZE=%s", from, size);
591
592 free(from);
593 free(auth);
594 free(size);
595
596 if(!result)
597 state(conn, SMTP_MAIL);
598
599 return result;
600}
601
602/***********************************************************************
603 *
604 * smtp_perform_rcpt_to()
605 *
606 * Sends a RCPT TO command for a given recipient as part of the message upload
607 * process.
608 */
609static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
610{
611 CURLcode result = CURLE_OK;
612 struct Curl_easy *data = conn->data;
613 struct SMTP *smtp = data->req.protop;
614
615 /* Send the RCPT TO command */
616 if(smtp->rcpt->data[0] == '<')
617 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
618 smtp->rcpt->data);
619 else
620 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
621 smtp->rcpt->data);
622 if(!result)
623 state(conn, SMTP_RCPT);
624
625 return result;
626}
627
628/***********************************************************************
629 *
630 * smtp_perform_quit()
631 *
632 * Performs the quit action prior to sclose() being called.
633 */
634static CURLcode smtp_perform_quit(struct connectdata *conn)
635{
636 CURLcode result = CURLE_OK;
637
638 /* Send the QUIT command */
639 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
640
641 if(!result)
642 state(conn, SMTP_QUIT);
643
644 return result;
645}
646
647/* For the initial server greeting */
648static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
649 int smtpcode,
650 smtpstate instate)
651{
652 CURLcode result = CURLE_OK;
653 struct Curl_easy *data = conn->data;
654
655 (void)instate; /* no use for this yet */
656
657 if(smtpcode/100 != 2) {
658 failf(data, "Got unexpected smtp-server response: %d", smtpcode);
659 result = CURLE_WEIRD_SERVER_REPLY;
660 }
661 else
662 result = smtp_perform_ehlo(conn);
663
664 return result;
665}
666
667/* For STARTTLS responses */
668static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
669 int smtpcode,
670 smtpstate instate)
671{
672 CURLcode result = CURLE_OK;
673 struct Curl_easy *data = conn->data;
674
675 (void)instate; /* no use for this yet */
676
677 if(smtpcode != 220) {
678 if(data->set.use_ssl != CURLUSESSL_TRY) {
679 failf(data, "STARTTLS denied, code %d", smtpcode);
680 result = CURLE_USE_SSL_FAILED;
681 }
682 else
683 result = smtp_perform_authentication(conn);
684 }
685 else
686 result = smtp_perform_upgrade_tls(conn);
687
688 return result;
689}
690
691/* For EHLO responses */
692static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
693 smtpstate instate)
694{
695 CURLcode result = CURLE_OK;
696 struct Curl_easy *data = conn->data;
697 struct smtp_conn *smtpc = &conn->proto.smtpc;
698 const char *line = data->state.buffer;
699 size_t len = strlen(line);
700 size_t wordlen;
701
702 (void)instate; /* no use for this yet */
703
704 if(smtpcode/100 != 2 && smtpcode != 1) {
705 if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
706 result = smtp_perform_helo(conn);
707 else {
708 failf(data, "Remote access denied: %d", smtpcode);
709 result = CURLE_REMOTE_ACCESS_DENIED;
710 }
711 }
712 else {
713 line += 4;
714 len -= 4;
715
716 /* Does the server support the STARTTLS capability? */
717 if(len >= 8 && !memcmp(line, "STARTTLS", 8))
718 smtpc->tls_supported = TRUE;
719
720 /* Does the server support the SIZE capability? */
721 else if(len >= 4 && !memcmp(line, "SIZE", 4))
722 smtpc->size_supported = TRUE;
723
724 /* Does the server support authentication? */
725 else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
726 smtpc->auth_supported = TRUE;
727
728 /* Advance past the AUTH keyword */
729 line += 5;
730 len -= 5;
731
732 /* Loop through the data line */
733 for(;;) {
734 size_t llen;
735 unsigned int mechbit;
736
737 while(len &&
738 (*line == ' ' || *line == '\t' ||
739 *line == '\r' || *line == '\n')) {
740
741 line++;
742 len--;
743 }
744
745 if(!len)
746 break;
747
748 /* Extract the word */
749 for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
750 line[wordlen] != '\t' && line[wordlen] != '\r' &&
751 line[wordlen] != '\n';)
752 wordlen++;
753
754 /* Test the word for a matching authentication mechanism */
755 mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
756 if(mechbit && llen == wordlen)
757 smtpc->sasl.authmechs |= mechbit;
758
759 line += wordlen;
760 len -= wordlen;
761 }
762 }
763
764 if(smtpcode != 1) {
765 if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
766 /* We don't have a SSL/TLS connection yet, but SSL is requested */
767 if(smtpc->tls_supported)
768 /* Switch to TLS connection now */
769 result = smtp_perform_starttls(conn);
770 else if(data->set.use_ssl == CURLUSESSL_TRY)
771 /* Fallback and carry on with authentication */
772 result = smtp_perform_authentication(conn);
773 else {
774 failf(data, "STARTTLS not supported.");
775 result = CURLE_USE_SSL_FAILED;
776 }
777 }
778 else
779 result = smtp_perform_authentication(conn);
780 }
781 }
782
783 return result;
784}
785
786/* For HELO responses */
787static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
788 smtpstate instate)
789{
790 CURLcode result = CURLE_OK;
791 struct Curl_easy *data = conn->data;
792
793 (void)instate; /* no use for this yet */
794
795 if(smtpcode/100 != 2) {
796 failf(data, "Remote access denied: %d", smtpcode);
797 result = CURLE_REMOTE_ACCESS_DENIED;
798 }
799 else
800 /* End of connect phase */
801 state(conn, SMTP_STOP);
802
803 return result;
804}
805
806/* For SASL authentication responses */
807static CURLcode smtp_state_auth_resp(struct connectdata *conn,
808 int smtpcode,
809 smtpstate instate)
810{
811 CURLcode result = CURLE_OK;
812 struct Curl_easy *data = conn->data;
813 struct smtp_conn *smtpc = &conn->proto.smtpc;
814 saslprogress progress;
815
816 (void)instate; /* no use for this yet */
817
818 result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
819 if(!result)
820 switch(progress) {
821 case SASL_DONE:
822 state(conn, SMTP_STOP); /* Authenticated */
823 break;
824 case SASL_IDLE: /* No mechanism left after cancellation */
825 failf(data, "Authentication cancelled");
826 result = CURLE_LOGIN_DENIED;
827 break;
828 default:
829 break;
830 }
831
832 return result;
833}
834
835/* For command responses */
836static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
837 smtpstate instate)
838{
839 CURLcode result = CURLE_OK;
840 struct Curl_easy *data = conn->data;
841 struct SMTP *smtp = data->req.protop;
842 char *line = data->state.buffer;
843 size_t len = strlen(line);
844
845 (void)instate; /* no use for this yet */
846
847 if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
848 (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
849 failf(data, "Command failed: %d", smtpcode);
850 result = CURLE_RECV_ERROR;
851 }
852 else {
853 /* Temporarily add the LF character back and send as body to the client */
854 if(!data->set.opt_no_body) {
855 line[len] = '\n';
856 result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
857 line[len] = '\0';
858 }
859
860 if(smtpcode != 1) {
861 if(smtp->rcpt) {
862 smtp->rcpt = smtp->rcpt->next;
863
864 if(smtp->rcpt) {
865 /* Send the next command */
866 result = smtp_perform_command(conn);
867 }
868 else
869 /* End of DO phase */
870 state(conn, SMTP_STOP);
871 }
872 else
873 /* End of DO phase */
874 state(conn, SMTP_STOP);
875 }
876 }
877
878 return result;
879}
880
881/* For MAIL responses */
882static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
883 smtpstate instate)
884{
885 CURLcode result = CURLE_OK;
886 struct Curl_easy *data = conn->data;
887
888 (void)instate; /* no use for this yet */
889
890 if(smtpcode/100 != 2) {
891 failf(data, "MAIL failed: %d", smtpcode);
892 result = CURLE_SEND_ERROR;
893 }
894 else
895 /* Start the RCPT TO command */
896 result = smtp_perform_rcpt_to(conn);
897
898 return result;
899}
900
901/* For RCPT responses */
902static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
903 smtpstate instate)
904{
905 CURLcode result = CURLE_OK;
906 struct Curl_easy *data = conn->data;
907 struct SMTP *smtp = data->req.protop;
908
909 (void)instate; /* no use for this yet */
910
911 if(smtpcode/100 != 2) {
912 failf(data, "RCPT failed: %d", smtpcode);
913 result = CURLE_SEND_ERROR;
914 }
915 else {
916 smtp->rcpt = smtp->rcpt->next;
917
918 if(smtp->rcpt)
919 /* Send the next RCPT TO command */
920 result = smtp_perform_rcpt_to(conn);
921 else {
922 /* Send the DATA command */
923 result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
924
925 if(!result)
926 state(conn, SMTP_DATA);
927 }
928 }
929
930 return result;
931}
932
933/* For DATA response */
934static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
935 smtpstate instate)
936{
937 CURLcode result = CURLE_OK;
938 struct Curl_easy *data = conn->data;
939
940 (void)instate; /* no use for this yet */
941
942 if(smtpcode != 354) {
943 failf(data, "DATA failed: %d", smtpcode);
944 result = CURLE_SEND_ERROR;
945 }
946 else {
947 /* Set the progress upload size */
948 Curl_pgrsSetUploadSize(data, data->state.infilesize);
949
950 /* SMTP upload */
951 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
952
953 /* End of DO phase */
954 state(conn, SMTP_STOP);
955 }
956
957 return result;
958}
959
960/* For POSTDATA responses, which are received after the entire DATA
961 part has been sent to the server */
962static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
963 int smtpcode,
964 smtpstate instate)
965{
966 CURLcode result = CURLE_OK;
967
968 (void)instate; /* no use for this yet */
969
970 if(smtpcode != 250)
971 result = CURLE_RECV_ERROR;
972
973 /* End of DONE phase */
974 state(conn, SMTP_STOP);
975
976 return result;
977}
978
979static CURLcode smtp_statemach_act(struct connectdata *conn)
980{
981 CURLcode result = CURLE_OK;
982 curl_socket_t sock = conn->sock[FIRSTSOCKET];
983 struct Curl_easy *data = conn->data;
984 int smtpcode;
985 struct smtp_conn *smtpc = &conn->proto.smtpc;
986 struct pingpong *pp = &smtpc->pp;
987 size_t nread = 0;
988
989 /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
990 if(smtpc->state == SMTP_UPGRADETLS)
991 return smtp_perform_upgrade_tls(conn);
992
993 /* Flush any data that needs to be sent */
994 if(pp->sendleft)
995 return Curl_pp_flushsend(pp);
996
997 do {
998 /* Read the response from the server */
999 result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
1000 if(result)
1001 return result;
1002
1003 /* Store the latest response for later retrieval if necessary */
1004 if(smtpc->state != SMTP_QUIT && smtpcode != 1)
1005 data->info.httpcode = smtpcode;
1006
1007 if(!smtpcode)
1008 break;
1009
1010 /* We have now received a full SMTP server response */
1011 switch(smtpc->state) {
1012 case SMTP_SERVERGREET:
1013 result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
1014 break;
1015
1016 case SMTP_EHLO:
1017 result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
1018 break;
1019
1020 case SMTP_HELO:
1021 result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
1022 break;
1023
1024 case SMTP_STARTTLS:
1025 result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
1026 break;
1027
1028 case SMTP_AUTH:
1029 result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
1030 break;
1031
1032 case SMTP_COMMAND:
1033 result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
1034 break;
1035
1036 case SMTP_MAIL:
1037 result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
1038 break;
1039
1040 case SMTP_RCPT:
1041 result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
1042 break;
1043
1044 case SMTP_DATA:
1045 result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
1046 break;
1047
1048 case SMTP_POSTDATA:
1049 result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
1050 break;
1051
1052 case SMTP_QUIT:
1053 /* fallthrough, just stop! */
1054 default:
1055 /* internal error */
1056 state(conn, SMTP_STOP);
1057 break;
1058 }
1059 } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
1060
1061 return result;
1062}
1063
1064/* Called repeatedly until done from multi.c */
1065static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1066{
1067 CURLcode result = CURLE_OK;
1068 struct smtp_conn *smtpc = &conn->proto.smtpc;
1069
1070 if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
1071 result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
1072 if(result || !smtpc->ssldone)
1073 return result;
1074 }
1075
1076 result = Curl_pp_statemach(&smtpc->pp, FALSE);
1077 *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
1078
1079 return result;
1080}
1081
1082static CURLcode smtp_block_statemach(struct connectdata *conn)
1083{
1084 CURLcode result = CURLE_OK;
1085 struct smtp_conn *smtpc = &conn->proto.smtpc;
1086
1087 while(smtpc->state != SMTP_STOP && !result)
1088 result = Curl_pp_statemach(&smtpc->pp, TRUE);
1089
1090 return result;
1091}
1092
1093/* Allocate and initialize the SMTP struct for the current Curl_easy if
1094 required */
1095static CURLcode smtp_init(struct connectdata *conn)
1096{
1097 CURLcode result = CURLE_OK;
1098 struct Curl_easy *data = conn->data;
1099 struct SMTP *smtp;
1100
1101 smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
1102 if(!smtp)
1103 result = CURLE_OUT_OF_MEMORY;
1104
1105 return result;
1106}
1107
1108/* For the SMTP "protocol connect" and "doing" phases only */
1109static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
1110 int numsocks)
1111{
1112 return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
1113}
1114
1115/***********************************************************************
1116 *
1117 * smtp_connect()
1118 *
1119 * This function should do everything that is to be considered a part of
1120 * the connection phase.
1121 *
1122 * The variable pointed to by 'done' will be TRUE if the protocol-layer
1123 * connect phase is done when this function returns, or FALSE if not.
1124 */
1125static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1126{
1127 CURLcode result = CURLE_OK;
1128 struct smtp_conn *smtpc = &conn->proto.smtpc;
1129 struct pingpong *pp = &smtpc->pp;
1130
1131 *done = FALSE; /* default to not done yet */
1132
1133 /* We always support persistent connections in SMTP */
1134 connkeep(conn, "SMTP default");
1135
1136 /* Set the default response time-out */
1137 pp->response_time = RESP_TIMEOUT;
1138 pp->statemach_act = smtp_statemach_act;
1139 pp->endofresp = smtp_endofresp;
1140 pp->conn = conn;
1141
1142 /* Initialize the SASL storage */
1143 Curl_sasl_init(&smtpc->sasl, &saslsmtp);
1144
1145 /* Initialise the pingpong layer */
1146 Curl_pp_init(pp);
1147
1148 /* Parse the URL options */
1149 result = smtp_parse_url_options(conn);
1150 if(result)
1151 return result;
1152
1153 /* Parse the URL path */
1154 result = smtp_parse_url_path(conn);
1155 if(result)
1156 return result;
1157
1158 /* Start off waiting for the server greeting response */
1159 state(conn, SMTP_SERVERGREET);
1160
1161 result = smtp_multi_statemach(conn, done);
1162
1163 return result;
1164}
1165
1166/***********************************************************************
1167 *
1168 * smtp_done()
1169 *
1170 * The DONE function. This does what needs to be done after a single DO has
1171 * performed.
1172 *
1173 * Input argument is already checked for validity.
1174 */
1175static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1176 bool premature)
1177{
1178 CURLcode result = CURLE_OK;
1179 struct Curl_easy *data = conn->data;
1180 struct SMTP *smtp = data->req.protop;
1181 struct pingpong *pp = &conn->proto.smtpc.pp;
1182 char *eob;
1183 ssize_t len;
1184 ssize_t bytes_written;
1185
1186 (void)premature;
1187
1188 if(!smtp || !pp->conn)
1189 return CURLE_OK;
1190
1191 /* Cleanup our per-request based variables */
1192 Curl_safefree(smtp->custom);
1193
1194 if(status) {
1195 connclose(conn, "SMTP done with bad status"); /* marked for closure */
1196 result = status; /* use the already set error code */
1197 }
1198 else if(!data->set.connect_only && data->set.mail_rcpt &&
1199 (data->set.upload || data->set.mimepost.kind)) {
1200 /* Calculate the EOB taking into account any terminating CRLF from the
1201 previous line of the email or the CRLF of the DATA command when there
1202 is "no mail data". RFC-5321, sect. 4.1.1.4.
1203
1204 Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1205 fail when using a different pointer following a previous write, that
1206 returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1207 bytes written doesn't equal len. */
1208 if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1209 eob = strdup(SMTP_EOB + 2);
1210 len = SMTP_EOB_LEN - 2;
1211 }
1212 else {
1213 eob = strdup(SMTP_EOB);
1214 len = SMTP_EOB_LEN;
1215 }
1216
1217 if(!eob)
1218 return CURLE_OUT_OF_MEMORY;
1219
1220 /* Send the end of block data */
1221 result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1222 if(result) {
1223 free(eob);
1224 return result;
1225 }
1226
1227 if(bytes_written != len) {
1228 /* The whole chunk was not sent so keep it around and adjust the
1229 pingpong structure accordingly */
1230 pp->sendthis = eob;
1231 pp->sendsize = len;
1232 pp->sendleft = len - bytes_written;
1233 }
1234 else {
1235 /* Successfully sent so adjust the response timeout relative to now */
1236 pp->response = Curl_now();
1237
1238 free(eob);
1239 }
1240
1241 state(conn, SMTP_POSTDATA);
1242
1243 /* Run the state-machine
1244
1245 TODO: when the multi interface is used, this _really_ should be using
1246 the smtp_multi_statemach function but we have no general support for
1247 non-blocking DONE operations!
1248 */
1249 result = smtp_block_statemach(conn);
1250 }
1251
1252 /* Clear the transfer mode for the next request */
1253 smtp->transfer = FTPTRANSFER_BODY;
1254
1255 return result;
1256}
1257
1258/***********************************************************************
1259 *
1260 * smtp_perform()
1261 *
1262 * This is the actual DO function for SMTP. Transfer a mail, send a command
1263 * or get some data according to the options previously setup.
1264 */
1265static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1266 bool *dophase_done)
1267{
1268 /* This is SMTP and no proxy */
1269 CURLcode result = CURLE_OK;
1270 struct Curl_easy *data = conn->data;
1271 struct SMTP *smtp = data->req.protop;
1272
1273 DEBUGF(infof(conn->data, "DO phase starts\n"));
1274
1275 if(data->set.opt_no_body) {
1276 /* Requested no body means no transfer */
1277 smtp->transfer = FTPTRANSFER_INFO;
1278 }
1279
1280 *dophase_done = FALSE; /* not done yet */
1281
1282 /* Store the first recipient (or NULL if not specified) */
1283 smtp->rcpt = data->set.mail_rcpt;
1284
1285 /* Start the first command in the DO phase */
1286 if((data->set.upload || data->set.mimepost.kind) && data->set.mail_rcpt)
1287 /* MAIL transfer */
1288 result = smtp_perform_mail(conn);
1289 else
1290 /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
1291 result = smtp_perform_command(conn);
1292
1293 if(result)
1294 return result;
1295
1296 /* Run the state-machine */
1297 result = smtp_multi_statemach(conn, dophase_done);
1298
1299 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
1300
1301 if(*dophase_done)
1302 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1303
1304 return result;
1305}
1306
1307/***********************************************************************
1308 *
1309 * smtp_do()
1310 *
1311 * This function is registered as 'curl_do' function. It decodes the path
1312 * parts etc as a wrapper to the actual DO function (smtp_perform).
1313 *
1314 * The input argument is already checked for validity.
1315 */
1316static CURLcode smtp_do(struct connectdata *conn, bool *done)
1317{
1318 CURLcode result = CURLE_OK;
1319
1320 *done = FALSE; /* default to false */
1321
1322 /* Parse the custom request */
1323 result = smtp_parse_custom_request(conn);
1324 if(result)
1325 return result;
1326
1327 result = smtp_regular_transfer(conn, done);
1328
1329 return result;
1330}
1331
1332/***********************************************************************
1333 *
1334 * smtp_disconnect()
1335 *
1336 * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
1337 * resources. BLOCKING.
1338 */
1339static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
1340{
1341 struct smtp_conn *smtpc = &conn->proto.smtpc;
1342
1343 /* We cannot send quit unconditionally. If this connection is stale or
1344 bad in any way, sending quit and waiting around here will make the
1345 disconnect wait in vain and cause more problems than we need to. */
1346
1347 /* The SMTP session may or may not have been allocated/setup at this
1348 point! */
1349 if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
1350 if(!smtp_perform_quit(conn))
1351 (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
1352
1353 /* Disconnect from the server */
1354 Curl_pp_disconnect(&smtpc->pp);
1355
1356 /* Cleanup the SASL module */
1357 Curl_sasl_cleanup(conn, smtpc->sasl.authused);
1358
1359 /* Cleanup our connection based variables */
1360 Curl_safefree(smtpc->domain);
1361
1362 return CURLE_OK;
1363}
1364
1365/* Call this when the DO phase has completed */
1366static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
1367{
1368 struct SMTP *smtp = conn->data->req.protop;
1369
1370 (void)connected;
1371
1372 if(smtp->transfer != FTPTRANSFER_BODY)
1373 /* no data to transfer */
1374 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1375
1376 return CURLE_OK;
1377}
1378
1379/* Called from multi.c while DOing */
1380static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
1381{
1382 CURLcode result = smtp_multi_statemach(conn, dophase_done);
1383
1384 if(result)
1385 DEBUGF(infof(conn->data, "DO phase failed\n"));
1386 else if(*dophase_done) {
1387 result = smtp_dophase_done(conn, FALSE /* not connected */);
1388
1389 DEBUGF(infof(conn->data, "DO phase is complete\n"));
1390 }
1391
1392 return result;
1393}
1394
1395/***********************************************************************
1396 *
1397 * smtp_regular_transfer()
1398 *
1399 * The input argument is already checked for validity.
1400 *
1401 * Performs all commands done before a regular transfer between a local and a
1402 * remote host.
1403 */
1404static CURLcode smtp_regular_transfer(struct connectdata *conn,
1405 bool *dophase_done)
1406{
1407 CURLcode result = CURLE_OK;
1408 bool connected = FALSE;
1409 struct Curl_easy *data = conn->data;
1410
1411 /* Make sure size is unknown at this point */
1412 data->req.size = -1;
1413
1414 /* Set the progress data */
1415 Curl_pgrsSetUploadCounter(data, 0);
1416 Curl_pgrsSetDownloadCounter(data, 0);
1417 Curl_pgrsSetUploadSize(data, -1);
1418 Curl_pgrsSetDownloadSize(data, -1);
1419
1420 /* Carry out the perform */
1421 result = smtp_perform(conn, &connected, dophase_done);
1422
1423 /* Perform post DO phase operations if necessary */
1424 if(!result && *dophase_done)
1425 result = smtp_dophase_done(conn, connected);
1426
1427 return result;
1428}
1429
1430static CURLcode smtp_setup_connection(struct connectdata *conn)
1431{
1432 struct Curl_easy *data = conn->data;
1433 CURLcode result;
1434
1435 /* Clear the TLS upgraded flag */
1436 conn->tls_upgraded = FALSE;
1437
1438 /* Initialise the SMTP layer */
1439 result = smtp_init(conn);
1440 if(result)
1441 return result;
1442
1443 data->state.path++; /* don't include the initial slash */
1444
1445 return CURLE_OK;
1446}
1447
1448/***********************************************************************
1449 *
1450 * smtp_parse_url_options()
1451 *
1452 * Parse the URL login options.
1453 */
1454static CURLcode smtp_parse_url_options(struct connectdata *conn)
1455{
1456 CURLcode result = CURLE_OK;
1457 struct smtp_conn *smtpc = &conn->proto.smtpc;
1458 const char *ptr = conn->options;
1459
1460 smtpc->sasl.resetprefs = TRUE;
1461
1462 while(!result && ptr && *ptr) {
1463 const char *key = ptr;
1464 const char *value;
1465
1466 while(*ptr && *ptr != '=')
1467 ptr++;
1468
1469 value = ptr + 1;
1470
1471 while(*ptr && *ptr != ';')
1472 ptr++;
1473
1474 if(strncasecompare(key, "AUTH=", 5))
1475 result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
1476 value, ptr - value);
1477 else
1478 result = CURLE_URL_MALFORMAT;
1479
1480 if(*ptr == ';')
1481 ptr++;
1482 }
1483
1484 return result;
1485}
1486
1487/***********************************************************************
1488 *
1489 * smtp_parse_url_path()
1490 *
1491 * Parse the URL path into separate path components.
1492 */
1493static CURLcode smtp_parse_url_path(struct connectdata *conn)
1494{
1495 /* The SMTP struct is already initialised in smtp_connect() */
1496 struct Curl_easy *data = conn->data;
1497 struct smtp_conn *smtpc = &conn->proto.smtpc;
1498 const char *path = data->state.path;
1499 char localhost[HOSTNAME_MAX + 1];
1500
1501 /* Calculate the path if necessary */
1502 if(!*path) {
1503 if(!Curl_gethostname(localhost, sizeof(localhost)))
1504 path = localhost;
1505 else
1506 path = "localhost";
1507 }
1508
1509 /* URL decode the path and use it as the domain in our EHLO */
1510 return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
1511}
1512
1513/***********************************************************************
1514 *
1515 * smtp_parse_custom_request()
1516 *
1517 * Parse the custom request.
1518 */
1519static CURLcode smtp_parse_custom_request(struct connectdata *conn)
1520{
1521 CURLcode result = CURLE_OK;
1522 struct Curl_easy *data = conn->data;
1523 struct SMTP *smtp = data->req.protop;
1524 const char *custom = data->set.str[STRING_CUSTOMREQUEST];
1525
1526 /* URL decode the custom request */
1527 if(custom)
1528 result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
1529
1530 return result;
1531}
1532
1533CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
1534{
1535 /* When sending a SMTP payload we must detect CRLF. sequences making sure
1536 they are sent as CRLF.. instead, as a . on the beginning of a line will
1537 be deleted by the server when not part of an EOB terminator and a
1538 genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
1539 data by the server
1540 */
1541 ssize_t i;
1542 ssize_t si;
1543 struct Curl_easy *data = conn->data;
1544 struct SMTP *smtp = data->req.protop;
1545 char *scratch = data->state.scratch;
1546 char *newscratch = NULL;
1547 char *oldscratch = NULL;
1548 size_t eob_sent;
1549
1550 /* Do we need to allocate a scratch buffer? */
1551 if(!scratch || data->set.crlf) {
1552 oldscratch = scratch;
1553
1554 scratch = newscratch = malloc(2 * data->set.buffer_size);
1555 if(!newscratch) {
1556 failf(data, "Failed to alloc scratch buffer!");
1557
1558 return CURLE_OUT_OF_MEMORY;
1559 }
1560 }
1561
1562 /* Have we already sent part of the EOB? */
1563 eob_sent = smtp->eob;
1564
1565 /* This loop can be improved by some kind of Boyer-Moore style of
1566 approach but that is saved for later... */
1567 for(i = 0, si = 0; i < nread; i++) {
1568 if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
1569 smtp->eob++;
1570
1571 /* Is the EOB potentially the terminating CRLF? */
1572 if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
1573 smtp->trailing_crlf = TRUE;
1574 else
1575 smtp->trailing_crlf = FALSE;
1576 }
1577 else if(smtp->eob) {
1578 /* A previous substring matched so output that first */
1579 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1580 si += smtp->eob - eob_sent;
1581
1582 /* Then compare the first byte */
1583 if(SMTP_EOB[0] == data->req.upload_fromhere[i])
1584 smtp->eob = 1;
1585 else
1586 smtp->eob = 0;
1587
1588 eob_sent = 0;
1589
1590 /* Reset the trailing CRLF flag as there was more data */
1591 smtp->trailing_crlf = FALSE;
1592 }
1593
1594 /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
1595 if(SMTP_EOB_FIND_LEN == smtp->eob) {
1596 /* Copy the replacement data to the target buffer */
1597 memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
1598 SMTP_EOB_REPL_LEN - eob_sent);
1599 si += SMTP_EOB_REPL_LEN - eob_sent;
1600 smtp->eob = 0;
1601 eob_sent = 0;
1602 }
1603 else if(!smtp->eob)
1604 scratch[si++] = data->req.upload_fromhere[i];
1605 }
1606
1607 if(smtp->eob - eob_sent) {
1608 /* A substring matched before processing ended so output that now */
1609 memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
1610 si += smtp->eob - eob_sent;
1611 }
1612
1613 /* Only use the new buffer if we replaced something */
1614 if(si != nread) {
1615 /* Upload from the new (replaced) buffer instead */
1616 data->req.upload_fromhere = scratch;
1617
1618 /* Save the buffer so it can be freed later */
1619 data->state.scratch = scratch;
1620
1621 /* Free the old scratch buffer */
1622 free(oldscratch);
1623
1624 /* Set the new amount too */
1625 data->req.upload_present = si;
1626 }
1627 else
1628 free(newscratch);
1629
1630 return CURLE_OK;
1631}
1632
1633#endif /* CURL_DISABLE_SMTP */
Note: See TracBrowser for help on using the repository browser.