smtp.c
Go to the documentation of this file.
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 */
90 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
91 static CURLcode smtp_do(struct connectdata *conn, bool *done);
92 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
93  bool premature);
94 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
95 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
96 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
97 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
98  int numsocks);
99 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
100 static CURLcode smtp_setup_connection(struct connectdata *conn);
101 static CURLcode smtp_parse_url_options(struct connectdata *conn);
102 static CURLcode smtp_parse_url_path(struct connectdata *conn);
103 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
104 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
105  const char *initresp);
106 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
107 static void smtp_get_message(char *buffer, char **outptr);
108 
109 /*
110  * SMTP protocol handler.
111  */
112 
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 */
133 };
134 
135 #ifdef USE_SSL
136 /*
137  * SMTPS protocol handler.
138  */
139 
140 const 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 */
159  | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */
160 };
161 #endif
162 
163 /* SASL parameters for the smtp protocol */
164 static 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
175 static 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  */
195 static 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  */
233 static 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  */
262 static 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  */
300 {
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  */
326 {
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  */
349 {
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  */
368 {
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  */
396  const char *mech,
397  const char *initresp)
398 {
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  */
420 static 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  */
435 {
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  */
471 {
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  */
500 {
501  char *from = NULL;
502  char *auth = NULL;
503  char *size = NULL;
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"))
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. */
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  */
610 {
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  */
635 {
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 */
649  int smtpcode,
650  smtpstate instate)
651 {
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 */
669  int smtpcode,
670  smtpstate instate)
671 {
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 */
692 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
693  smtpstate instate)
694 {
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);
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 */
787 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
788  smtpstate instate)
789 {
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);
798  }
799  else
800  /* End of connect phase */
801  state(conn, SMTP_STOP);
802 
803  return result;
804 }
805 
806 /* For SASL authentication responses */
808  int smtpcode,
809  smtpstate instate)
810 {
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 */
836 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
837  smtpstate instate)
838 {
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 */
882 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
883  smtpstate instate)
884 {
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 */
902 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
903  smtpstate instate)
904 {
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 */
934 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
935  smtpstate instate)
936 {
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 */
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 */
963  int smtpcode,
964  smtpstate instate)
965 {
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 
980 {
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 */
1065 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
1066 {
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 
1083 {
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 */
1095 static CURLcode smtp_init(struct connectdata *conn)
1096 {
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 */
1109 static 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  */
1125 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
1126 {
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 */
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  */
1175 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
1176  bool premature)
1177 {
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  if(status) {
1192  connclose(conn, "SMTP done with bad status"); /* marked for closure */
1193  result = status; /* use the already set error code */
1194  }
1195  else if(!data->set.connect_only && data->set.mail_rcpt &&
1196  (data->set.upload || data->set.mimepost.kind)) {
1197  /* Calculate the EOB taking into account any terminating CRLF from the
1198  previous line of the email or the CRLF of the DATA command when there
1199  is "no mail data". RFC-5321, sect. 4.1.1.4.
1200 
1201  Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
1202  fail when using a different pointer following a previous write, that
1203  returned CURLE_AGAIN, we duplicate the EOB now rather than when the
1204  bytes written doesn't equal len. */
1205  if(smtp->trailing_crlf || !conn->data->state.infilesize) {
1206  eob = strdup(SMTP_EOB + 2);
1207  len = SMTP_EOB_LEN - 2;
1208  }
1209  else {
1210  eob = strdup(SMTP_EOB);
1211  len = SMTP_EOB_LEN;
1212  }
1213 
1214  if(!eob)
1215  return CURLE_OUT_OF_MEMORY;
1216 
1217  /* Send the end of block data */
1218  result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
1219  if(result) {
1220  free(eob);
1221  return result;
1222  }
1223 
1224  if(bytes_written != len) {
1225  /* The whole chunk was not sent so keep it around and adjust the
1226  pingpong structure accordingly */
1227  pp->sendthis = eob;
1228  pp->sendsize = len;
1229  pp->sendleft = len - bytes_written;
1230  }
1231  else {
1232  /* Successfully sent so adjust the response timeout relative to now */
1233  pp->response = Curl_tvnow();
1234 
1235  free(eob);
1236  }
1237 
1238  state(conn, SMTP_POSTDATA);
1239 
1240  /* Run the state-machine
1241 
1242  TODO: when the multi interface is used, this _really_ should be using
1243  the smtp_multi_statemach function but we have no general support for
1244  non-blocking DONE operations!
1245  */
1246  result = smtp_block_statemach(conn);
1247  }
1248 
1249  /* Cleanup our per-request based variables */
1250  Curl_safefree(smtp->custom);
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  */
1265 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
1266  bool *dophase_done)
1267 {
1268  /* This is SMTP and no proxy */
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  */
1316 static CURLcode smtp_do(struct connectdata *conn, bool *done)
1317 {
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  */
1339 static 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 */
1366 static 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 */
1380 static 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  */
1405  bool *dophase_done)
1406 {
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 
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  */
1455 {
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  */
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  */
1520 {
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 
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 */
#define free(ptr)
Definition: curl_memory.h:130
bool crlf
Definition: urldata.h:1566
#define SMTP_EOB_REPL_LEN
Definition: smtp.h:87
struct ssl_connect_data ssl[2]
Definition: urldata.h:887
curl_mimepart mimepost
Definition: urldata.h:1563
#define CLIENTWRITE_BODY
Definition: sendf.h:50
unsigned int authmechs
Definition: curl_sasl.h:104
char * upload_fromhere
Definition: urldata.h:603
struct ConnectBits bits
Definition: urldata.h:893
static CURLcode smtp_perform_starttls(struct connectdata *conn)
Definition: smtp.c:348
CURLcode Curl_urldecode(struct Curl_easy *data, const char *string, size_t length, char **ostring, size_t *olen, bool reject_ctrl)
Definition: escape.c:146
CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, struct curl_slist *headers, int take_ownership)
Definition: mime.c:1352
size_t sendleft
Definition: pingpong.h:59
size_t eob
Definition: smtp.h:58
static CURLcode smtp_state_postdata_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:962
static CURLcode smtp_state_starttls_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:668
static CURLcode smtp_perform(struct connectdata *conn, bool *connected, bool *dophase_done)
Definition: smtp.c:1265
struct UserDefined set
Definition: urldata.h:1762
void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size)
Definition: progress.c:304
#define connclose(x, y)
Definition: connect.h:141
#define SMTP_EOB_REPL
Definition: smtp.h:86
curl_off_t size
Definition: urldata.h:519
Definition: smtp.h:46
bool opt_no_body
Definition: urldata.h:1631
static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
Definition: smtp.c:1065
long response_time
Definition: pingpong.h:63
int curlx_sltosi(long slnum)
Definition: warnless.c:264
Definition: smtp.h:65
#define FIRSTSOCKET
Definition: urldata.h:487
#define PROTOPT_NOURLQUERY
Definition: urldata.h:711
CURLcode Curl_pp_disconnect(struct pingpong *pp)
Definition: pingpong.c:500
static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:934
void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
Definition: curl_sasl.c:81
const struct Curl_handler Curl_handler_smtps
static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
Definition: smtp.c:1366
static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:787
static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks)
Definition: smtp.c:1109
#define failf
Definition: sendf.h:48
char * data
Definition: curl.h:2336
char * domain
Definition: smtp.h:69
bool resetprefs
Definition: curl_sasl.h:107
#define strdup(ptr)
Definition: curl_memory.h:122
bool protoconnstart
Definition: urldata.h:395
const struct Curl_handler * handler
Definition: urldata.h:904
UNITTEST_START char * ptr
Definition: unit1330.c:38
#define CURLPROTO_SMTPS
Definition: curl.h:861
CURLcode
Definition: curl.h:454
Definition: smtp.h:32
unsigned int flags
Definition: mime.h:115
const struct Curl_handler Curl_handler_smtp
Definition: smtp.c:113
curl_pp_transfer transfer
Definition: smtp.h:55
CURLcode Curl_pp_flushsend(struct pingpong *pp)
Definition: pingpong.c:476
#define MIME_BODY_ONLY
Definition: mime.h:31
enum mimekind kind
Definition: mime.h:102
CURLcode Curl_mime_prepare_headers(curl_mimepart *part, const char *contenttype, const char *disposition, enum mimestrategy strategy)
Definition: mime.c:1577
bool tls_upgraded
Definition: urldata.h:891
static const struct SASLproto saslsmtp
Definition: smtp.c:164
#define Curl_ssl_connect_nonblocking(x, y, z)
Definition: vtls.h:267
void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
Definition: curl_sasl.c:178
struct curl_slist * headers
Definition: urldata.h:1560
#define malloc(size)
Definition: curl_memory.h:124
Definition: smtp.h:42
struct SASL sasl
Definition: smtp.h:70
bool connect_only
Definition: urldata.h:1653
char * sendthis
Definition: pingpong.h:57
static CURLcode smtp_parse_url_options(struct connectdata *conn)
Definition: smtp.c:1454
UNITTEST_START int result
Definition: unit1304.c:49
char buffer[]
Definition: unit1308.c:48
curl_usessl use_ssl
Definition: urldata.h:1643
CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn, bool force_ir, saslprogress *progress)
Definition: curl_sasl.c:254
unsigned int i
Definition: unit1303.c:79
#define SASL_AUTH_NONE
Definition: curl_sasl.h:42
static srvr_sockaddr_union_t from
Definition: tftpd.c:197
bool ssldone
Definition: smtp.h:68
ssize_t upload_present
Definition: urldata.h:597
int httpcode
Definition: urldata.h:1052
size_t len
Definition: curl_sasl.c:55
#define PORT_SMTP
Definition: urldata.h:45
#define PROTOPT_SSL
Definition: urldata.h:700
static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
Definition: smtp.c:1380
CURLcode(* statemach_act)(struct connectdata *conn)
Definition: pingpong.h:72
static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:836
memcpy(filename, filename1, strlen(filename1))
Definition: urldata.h:1179
size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
Definition: mime.c:1428
#define ZERO_NULL
Definition: curlx.c:131
void Curl_setup_transfer(struct connectdata *conn, int sockindex, curl_off_t size, bool getheader, curl_off_t *bytecountp, int writesockindex, curl_off_t *writecountp)
Definition: transfer.c:1989
static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
Definition: smtp.c:367
#define FALSE
char * buffer
Definition: urldata.h:1253
struct SingleRequest req
Definition: urldata.h:1761
struct curl_slist * mail_rcpt
Definition: urldata.h:1670
unsigned int flags
Definition: urldata.h:696
CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt,...)
Definition: mime.c:1507
bool(* endofresp)(struct connectdata *conn, char *ptr, size_t len, int *code)
Definition: pingpong.h:74
CURLcode Curl_pp_statemach(struct pingpong *pp, bool block)
Definition: pingpong.c:81
curl_off_t infilesize
Definition: urldata.h:1345
static CURLcode smtp_setup_connection(struct connectdata *conn)
Definition: smtp.c:1430
CURLcode Curl_mime_rewind(curl_mimepart *part)
Definition: mime.c:1437
struct curl_slist * next
Definition: curl.h:2337
CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn, int code, saslprogress *progress)
Definition: curl_sasl.c:408
saslprogress
Definition: curl_sasl.h:79
#define ISDIGIT(x)
bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
Definition: curl_sasl.c:236
unsigned int Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len)
Definition: curl_sasl.c:117
static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len, int *resp)
Definition: smtp.c:195
#define strncasecompare(a, b, c)
Definition: strcase.h:36
Definition: smtp.h:40
Definition: smtp.h:54
size_t sendsize
Definition: pingpong.h:60
bool tls_supported
Definition: smtp.h:71
void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
Definition: progress.c:334
static CURLcode smtp_parse_custom_request(struct connectdata *conn)
Definition: smtp.c:1519
void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
Definition: progress.c:322
#define Curl_tvnow()
Definition: timeval.h:57
size_t(* curl_read_callback)(char *buffer, size_t size, size_t nitems, void *instream)
Definition: curl.h:355
static CURLcode smtp_perform_authentication(struct connectdata *conn)
Definition: smtp.c:434
static CURLcode smtp_perform_command(struct connectdata *conn)
Definition: smtp.c:470
CURLcode Curl_write(struct connectdata *conn, curl_socket_t sockfd, const void *mem, size_t len, ssize_t *written)
Definition: sendf.c:318
struct pingpong pp
Definition: smtp.h:66
char * options
Definition: urldata.h:867
char * path
Definition: urldata.h:1329
static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:692
Definition: curl.h:455
#define SMTP_EOB
Definition: smtp.h:81
void * protop
Definition: urldata.h:614
CURLcode Curl_client_write(struct connectdata *conn, int type, char *ptr, size_t len)
Definition: sendf.c:624
static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:902
#define Curl_safefree(ptr)
Definition: memdebug.h:170
curl_socket_t writesockfd
Definition: urldata.h:912
bool trailing_crlf
Definition: smtp.h:60
CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
Definition: smtp.c:1533
int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks, int numsocks)
Definition: pingpong.c:456
bool Curl_pp_moredata(struct pingpong *pp)
Definition: pingpong.c:507
static CURLcode smtp_statemach_act(struct connectdata *conn)
Definition: smtp.c:979
struct UrlState state
Definition: urldata.h:1769
static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
Definition: smtp.c:609
curl_off_t Curl_mime_size(curl_mimepart *part)
Definition: mime.c:1484
#define aprintf
Definition: curl_printf.h:46
#define SMTP_EOB_LEN
Definition: smtp.h:82
static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:882
static void state(struct connectdata *conn, smtpstate newstate)
Definition: smtp.c:262
Definition: smtp.h:44
#define RESP_TIMEOUT
Definition: urldata.h:80
struct PureInfo info
Definition: urldata.h:1772
#define CURLPROTO_SMTP
Definition: curl.h:860
char * Curl_checkheaders(const struct connectdata *conn, const char *thisheader)
Definition: transfer.c:92
#define ssize_t
Definition: config-win32.h:382
static CURLcode smtp_state_auth_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:807
char * custom
Definition: smtp.h:56
curl_socket_t sock[2]
Definition: urldata.h:876
void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
Definition: progress.c:286
#define connkeep(x, y)
Definition: connect.h:142
static CURLcode smtp_perform_ehlo(struct connectdata *conn)
Definition: smtp.c:299
struct curl_slist * rcpt
Definition: smtp.h:57
smtpstate state
Definition: smtp.h:67
char * scratch
Definition: urldata.h:1270
void * in
Definition: urldata.h:1356
static CURLcode smtp_connect(struct connectdata *conn, bool *done)
Definition: smtp.c:1125
void Curl_pp_init(struct pingpong *pp)
Definition: pingpong.c:140
static CURLcode smtp_done(struct connectdata *conn, CURLcode status, bool premature)
Definition: smtp.c:1175
struct smtp_conn smtpc
Definition: urldata.h:1004
struct connectdata * conn
Definition: pingpong.h:66
static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
Definition: smtp.c:420
CURLcode Curl_pp_sendf(struct pingpong *pp, const char *fmt,...)
Definition: pingpong.c:248
#define infof
Definition: sendf.h:44
UNITTEST_START int * value
Definition: unit1602.c:51
curl_read_callback fread_func
Definition: urldata.h:1355
struct curl_slist * curlheaders
Definition: mime.h:109
char * str[STRING_LAST]
Definition: urldata.h:1663
#define PROTOPT_URLOPTIONS
Definition: urldata.h:717
CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl, const char *value, size_t len)
Definition: curl_sasl.c:145
Definition: smtp.h:43
bool auth_supported
Definition: smtp.h:74
#define SMTP_EOB_FIND_LEN
Definition: smtp.h:83
static CURLcode smtp_state_servergreet_resp(struct connectdata *conn, int smtpcode, smtpstate instate)
Definition: smtp.c:648
union connectdata::@34 proto
static CURLcode smtp_do(struct connectdata *conn, bool *done)
Definition: smtp.c:1316
CURLcode Curl_pp_readresp(curl_socket_t sockfd, struct pingpong *pp, int *code, size_t *size)
Definition: pingpong.c:267
static CURLcode smtp_perform_quit(struct connectdata *conn)
Definition: smtp.c:634
size_t size
Definition: unit1302.c:52
bool size_supported
Definition: smtp.h:72
#define TRUE
#define HOSTNAME_MAX
static CURLcode smtp_init(struct connectdata *conn)
Definition: smtp.c:1095
static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech, const char *initresp)
Definition: smtp.c:395
static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done)
Definition: smtp.c:1404
#define PROTOPT_CLOSEACTION
Definition: urldata.h:702
int curl_socket_t
Definition: curl.h:130
smtpstate
Definition: smtp.h:31
static CURLcode smtp_perform_mail(struct connectdata *conn)
Definition: smtp.c:499
static CURLcode smtp_block_statemach(struct connectdata *conn)
Definition: smtp.c:1082
long buffer_size
Definition: urldata.h:1591
int Curl_gethostname(char *name, GETHOSTNAME_TYPE_ARG2 namelen)
bool upload
Definition: urldata.h:1632
#define PORT_SMTPS
Definition: urldata.h:46
int key
Definition: unit1602.c:56
struct curltime response
Definition: pingpong.h:61
#define smtp_to_smtps(x)
Definition: smtp.c:184
Definition: debug.c:29
bool tcpconnect[2]
Definition: urldata.h:393
static void smtp_get_message(char *buffer, char **outptr)
Definition: smtp.c:233
#define calloc(nbelem, size)
Definition: curl_memory.h:126
Definition: smtp.h:35
static CURLcode smtp_parse_url_path(struct connectdata *conn)
Definition: smtp.c:1493
Definition: smtp.h:36
static CURLcode smtp_perform_helo(struct connectdata *conn)
Definition: smtp.c:325
const char * path
Definition: util.c:192
static CURLcode smtp_disconnect(struct connectdata *conn, bool dead)
Definition: smtp.c:1339
#define CURL_FORMAT_CURL_OFF_T
Definition: system.h:373
#define DEBUGF(x)
struct Curl_easy * data
Definition: urldata.h:791
unsigned int authused
Definition: curl_sasl.h:106


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:16