smtp.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  * RFC1870 SMTP Service Extension for Message Size
00022  * RFC2195 CRAM-MD5 authentication
00023  * RFC2831 DIGEST-MD5 authentication
00024  * RFC3207 SMTP over TLS
00025  * RFC4422 Simple Authentication and Security Layer (SASL)
00026  * RFC4616 PLAIN authentication
00027  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
00028  * RFC4954 SMTP Authentication
00029  * RFC5321 SMTP protocol
00030  * RFC6749 OAuth 2.0 Authorization Framework
00031  * Draft   SMTP URL Interface   <draft-earhart-url-smtp-00.txt>
00032  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
00033  *
00034  ***************************************************************************/
00035 
00036 #include "curl_setup.h"
00037 
00038 #ifndef CURL_DISABLE_SMTP
00039 
00040 #ifdef HAVE_NETINET_IN_H
00041 #include <netinet/in.h>
00042 #endif
00043 #ifdef HAVE_ARPA_INET_H
00044 #include <arpa/inet.h>
00045 #endif
00046 #ifdef HAVE_UTSNAME_H
00047 #include <sys/utsname.h>
00048 #endif
00049 #ifdef HAVE_NETDB_H
00050 #include <netdb.h>
00051 #endif
00052 #ifdef __VMS
00053 #include <in.h>
00054 #include <inet.h>
00055 #endif
00056 
00057 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
00058 #undef in_addr_t
00059 #define in_addr_t unsigned long
00060 #endif
00061 
00062 #include <curl/curl.h>
00063 #include "urldata.h"
00064 #include "sendf.h"
00065 #include "hostip.h"
00066 #include "progress.h"
00067 #include "transfer.h"
00068 #include "escape.h"
00069 #include "http.h" /* for HTTP proxy tunnel stuff */
00070 #include "socks.h"
00071 #include "smtp.h"
00072 #include "strtoofft.h"
00073 #include "strcase.h"
00074 #include "vtls/vtls.h"
00075 #include "connect.h"
00076 #include "strerror.h"
00077 #include "select.h"
00078 #include "multiif.h"
00079 #include "url.h"
00080 #include "curl_gethostname.h"
00081 #include "curl_sasl.h"
00082 #include "warnless.h"
00083 /* The last 3 #include files should be in this order */
00084 #include "curl_printf.h"
00085 #include "curl_memory.h"
00086 #include "memdebug.h"
00087 
00088 /* Local API functions */
00089 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
00090 static CURLcode smtp_do(struct connectdata *conn, bool *done);
00091 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
00092                           bool premature);
00093 static CURLcode smtp_connect(struct connectdata *conn, bool *done);
00094 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
00095 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
00096 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
00097                         int numsocks);
00098 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
00099 static CURLcode smtp_setup_connection(struct connectdata *conn);
00100 static CURLcode smtp_parse_url_options(struct connectdata *conn);
00101 static CURLcode smtp_parse_url_path(struct connectdata *conn);
00102 static CURLcode smtp_parse_custom_request(struct connectdata *conn);
00103 static CURLcode smtp_perform_auth(struct connectdata *conn, const char *mech,
00104                                   const char *initresp);
00105 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp);
00106 static void smtp_get_message(char *buffer, char **outptr);
00107 
00108 /*
00109  * SMTP protocol handler.
00110  */
00111 
00112 const struct Curl_handler Curl_handler_smtp = {
00113   "SMTP",                           /* scheme */
00114   smtp_setup_connection,            /* setup_connection */
00115   smtp_do,                          /* do_it */
00116   smtp_done,                        /* done */
00117   ZERO_NULL,                        /* do_more */
00118   smtp_connect,                     /* connect_it */
00119   smtp_multi_statemach,             /* connecting */
00120   smtp_doing,                       /* doing */
00121   smtp_getsock,                     /* proto_getsock */
00122   smtp_getsock,                     /* doing_getsock */
00123   ZERO_NULL,                        /* domore_getsock */
00124   ZERO_NULL,                        /* perform_getsock */
00125   smtp_disconnect,                  /* disconnect */
00126   ZERO_NULL,                        /* readwrite */
00127   PORT_SMTP,                        /* defport */
00128   CURLPROTO_SMTP,                   /* protocol */
00129   PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
00130 };
00131 
00132 #ifdef USE_SSL
00133 /*
00134  * SMTPS protocol handler.
00135  */
00136 
00137 const struct Curl_handler Curl_handler_smtps = {
00138   "SMTPS",                          /* scheme */
00139   smtp_setup_connection,            /* setup_connection */
00140   smtp_do,                          /* do_it */
00141   smtp_done,                        /* done */
00142   ZERO_NULL,                        /* do_more */
00143   smtp_connect,                     /* connect_it */
00144   smtp_multi_statemach,             /* connecting */
00145   smtp_doing,                       /* doing */
00146   smtp_getsock,                     /* proto_getsock */
00147   smtp_getsock,                     /* doing_getsock */
00148   ZERO_NULL,                        /* domore_getsock */
00149   ZERO_NULL,                        /* perform_getsock */
00150   smtp_disconnect,                  /* disconnect */
00151   ZERO_NULL,                        /* readwrite */
00152   PORT_SMTPS,                       /* defport */
00153   CURLPROTO_SMTPS,                  /* protocol */
00154   PROTOPT_CLOSEACTION | PROTOPT_SSL
00155   | PROTOPT_NOURLQUERY              /* flags */
00156 };
00157 #endif
00158 
00159 #ifndef CURL_DISABLE_HTTP
00160 /*
00161  * HTTP-proxyed SMTP protocol handler.
00162  */
00163 
00164 static const struct Curl_handler Curl_handler_smtp_proxy = {
00165   "SMTP",                               /* scheme */
00166   Curl_http_setup_conn,                 /* setup_connection */
00167   Curl_http,                            /* do_it */
00168   Curl_http_done,                       /* done */
00169   ZERO_NULL,                            /* do_more */
00170   ZERO_NULL,                            /* connect_it */
00171   ZERO_NULL,                            /* connecting */
00172   ZERO_NULL,                            /* doing */
00173   ZERO_NULL,                            /* proto_getsock */
00174   ZERO_NULL,                            /* doing_getsock */
00175   ZERO_NULL,                            /* domore_getsock */
00176   ZERO_NULL,                            /* perform_getsock */
00177   ZERO_NULL,                            /* disconnect */
00178   ZERO_NULL,                            /* readwrite */
00179   PORT_SMTP,                            /* defport */
00180   CURLPROTO_HTTP,                       /* protocol */
00181   PROTOPT_NONE                          /* flags */
00182 };
00183 
00184 #ifdef USE_SSL
00185 /*
00186  * HTTP-proxyed SMTPS protocol handler.
00187  */
00188 
00189 static const struct Curl_handler Curl_handler_smtps_proxy = {
00190   "SMTPS",                              /* scheme */
00191   Curl_http_setup_conn,                 /* setup_connection */
00192   Curl_http,                            /* do_it */
00193   Curl_http_done,                       /* done */
00194   ZERO_NULL,                            /* do_more */
00195   ZERO_NULL,                            /* connect_it */
00196   ZERO_NULL,                            /* connecting */
00197   ZERO_NULL,                            /* doing */
00198   ZERO_NULL,                            /* proto_getsock */
00199   ZERO_NULL,                            /* doing_getsock */
00200   ZERO_NULL,                            /* domore_getsock */
00201   ZERO_NULL,                            /* perform_getsock */
00202   ZERO_NULL,                            /* disconnect */
00203   ZERO_NULL,                            /* readwrite */
00204   PORT_SMTPS,                           /* defport */
00205   CURLPROTO_HTTP,                       /* protocol */
00206   PROTOPT_NONE                          /* flags */
00207 };
00208 #endif
00209 #endif
00210 
00211 /* SASL parameters for the smtp protocol */
00212 static const struct SASLproto saslsmtp = {
00213   "smtp",                     /* The service name */
00214   334,                        /* Code received when continuation is expected */
00215   235,                        /* Code to receive upon authentication success */
00216   512 - 8,                    /* Maximum initial response length (no max) */
00217   smtp_perform_auth,          /* Send authentication command */
00218   smtp_continue_auth,         /* Send authentication continuation */
00219   smtp_get_message            /* Get SASL response message */
00220 };
00221 
00222 #ifdef USE_SSL
00223 static void smtp_to_smtps(struct connectdata *conn)
00224 {
00225   /* Change the connection handler */
00226   conn->handler = &Curl_handler_smtps;
00227 
00228   /* Set the connection's upgraded to TLS flag */
00229   conn->tls_upgraded = TRUE;
00230 }
00231 #else
00232 #define smtp_to_smtps(x) Curl_nop_stmt
00233 #endif
00234 
00235 /***********************************************************************
00236  *
00237  * smtp_endofresp()
00238  *
00239  * Checks for an ending SMTP status code at the start of the given string, but
00240  * also detects various capabilities from the EHLO response including the
00241  * supported authentication mechanisms.
00242  */
00243 static bool smtp_endofresp(struct connectdata *conn, char *line, size_t len,
00244                            int *resp)
00245 {
00246   struct smtp_conn *smtpc = &conn->proto.smtpc;
00247   bool result = FALSE;
00248 
00249   /* Nothing for us */
00250   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
00251     return FALSE;
00252 
00253   /* Do we have a command response? This should be the response code followed
00254      by a space and optionally some text as per RFC-5321 and as outlined in
00255      Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
00256      only send the response code instead as per Section 4.2. */
00257   if(line[3] == ' ' || len == 5) {
00258     result = TRUE;
00259     *resp = curlx_sltosi(strtol(line, NULL, 10));
00260 
00261     /* Make sure real server never sends internal value */
00262     if(*resp == 1)
00263       *resp = 0;
00264   }
00265   /* Do we have a multiline (continuation) response? */
00266   else if(line[3] == '-' &&
00267           (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) {
00268     result = TRUE;
00269     *resp = 1;  /* Internal response code */
00270   }
00271 
00272   return result;
00273 }
00274 
00275 /***********************************************************************
00276  *
00277  * smtp_get_message()
00278  *
00279  * Gets the authentication message from the response buffer.
00280  */
00281 static void smtp_get_message(char *buffer, char **outptr)
00282 {
00283   size_t len = 0;
00284   char *message = NULL;
00285 
00286   /* Find the start of the message */
00287   for(message = buffer + 4; *message == ' ' || *message == '\t'; message++)
00288     ;
00289 
00290   /* Find the end of the message */
00291   for(len = strlen(message); len--;)
00292     if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
00293        message[len] != '\t')
00294       break;
00295 
00296   /* Terminate the message */
00297   if(++len) {
00298     message[len] = '\0';
00299   }
00300 
00301   *outptr = message;
00302 }
00303 
00304 /***********************************************************************
00305  *
00306  * state()
00307  *
00308  * This is the ONLY way to change SMTP state!
00309  */
00310 static void state(struct connectdata *conn, smtpstate newstate)
00311 {
00312   struct smtp_conn *smtpc = &conn->proto.smtpc;
00313 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00314   /* for debug purposes */
00315   static const char * const names[] = {
00316     "STOP",
00317     "SERVERGREET",
00318     "EHLO",
00319     "HELO",
00320     "STARTTLS",
00321     "UPGRADETLS",
00322     "AUTH",
00323     "COMMAND",
00324     "MAIL",
00325     "RCPT",
00326     "DATA",
00327     "POSTDATA",
00328     "QUIT",
00329     /* LAST */
00330   };
00331 
00332   if(smtpc->state != newstate)
00333     infof(conn->data, "SMTP %p state change from %s to %s\n",
00334           (void *)smtpc, names[smtpc->state], names[newstate]);
00335 #endif
00336 
00337   smtpc->state = newstate;
00338 }
00339 
00340 /***********************************************************************
00341  *
00342  * smtp_perform_ehlo()
00343  *
00344  * Sends the EHLO command to not only initialise communication with the ESMTP
00345  * server but to also obtain a list of server side supported capabilities.
00346  */
00347 static CURLcode smtp_perform_ehlo(struct connectdata *conn)
00348 {
00349   CURLcode result = CURLE_OK;
00350   struct smtp_conn *smtpc = &conn->proto.smtpc;
00351 
00352   smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
00353   smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
00354                                              used for esmtp connections */
00355   smtpc->tls_supported = FALSE;           /* Clear the TLS capability */
00356   smtpc->auth_supported = FALSE;          /* Clear the AUTH capability */
00357 
00358   /* Send the EHLO command */
00359   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
00360 
00361   if(!result)
00362     state(conn, SMTP_EHLO);
00363 
00364   return result;
00365 }
00366 
00367 /***********************************************************************
00368  *
00369  * smtp_perform_helo()
00370  *
00371  * Sends the HELO command to initialise communication with the SMTP server.
00372  */
00373 static CURLcode smtp_perform_helo(struct connectdata *conn)
00374 {
00375   CURLcode result = CURLE_OK;
00376   struct smtp_conn *smtpc = &conn->proto.smtpc;
00377 
00378   smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
00379                                             in smtp connections */
00380 
00381   /* Send the HELO command */
00382   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
00383 
00384   if(!result)
00385     state(conn, SMTP_HELO);
00386 
00387   return result;
00388 }
00389 
00390 /***********************************************************************
00391  *
00392  * smtp_perform_starttls()
00393  *
00394  * Sends the STLS command to start the upgrade to TLS.
00395  */
00396 static CURLcode smtp_perform_starttls(struct connectdata *conn)
00397 {
00398   CURLcode result = CURLE_OK;
00399 
00400   /* Send the STARTTLS command */
00401   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "STARTTLS");
00402 
00403   if(!result)
00404     state(conn, SMTP_STARTTLS);
00405 
00406   return result;
00407 }
00408 
00409 /***********************************************************************
00410  *
00411  * smtp_perform_upgrade_tls()
00412  *
00413  * Performs the upgrade to TLS.
00414  */
00415 static CURLcode smtp_perform_upgrade_tls(struct connectdata *conn)
00416 {
00417   CURLcode result = CURLE_OK;
00418   struct smtp_conn *smtpc = &conn->proto.smtpc;
00419 
00420   /* Start the SSL connection */
00421   result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
00422 
00423   if(!result) {
00424     if(smtpc->state != SMTP_UPGRADETLS)
00425       state(conn, SMTP_UPGRADETLS);
00426 
00427     if(smtpc->ssldone) {
00428       smtp_to_smtps(conn);
00429       result = smtp_perform_ehlo(conn);
00430     }
00431   }
00432 
00433   return result;
00434 }
00435 
00436 /***********************************************************************
00437  *
00438  * smtp_perform_auth()
00439  *
00440  * Sends an AUTH command allowing the client to login with the given SASL
00441  * authentication mechanism.
00442  */
00443 static CURLcode smtp_perform_auth(struct connectdata *conn,
00444                                   const char *mech,
00445                                   const char *initresp)
00446 {
00447   CURLcode result = CURLE_OK;
00448   struct smtp_conn *smtpc = &conn->proto.smtpc;
00449 
00450   if(initresp) {                                  /* AUTH <mech> ...<crlf> */
00451     /* Send the AUTH command with the initial response */
00452     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
00453   }
00454   else {
00455     /* Send the AUTH command */
00456     result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
00457   }
00458 
00459   return result;
00460 }
00461 
00462 /***********************************************************************
00463  *
00464  * smtp_continue_auth()
00465  *
00466  * Sends SASL continuation data or cancellation.
00467  */
00468 static CURLcode smtp_continue_auth(struct connectdata *conn, const char *resp)
00469 {
00470   struct smtp_conn *smtpc = &conn->proto.smtpc;
00471 
00472   return Curl_pp_sendf(&smtpc->pp, "%s", resp);
00473 }
00474 
00475 /***********************************************************************
00476  *
00477  * smtp_perform_authentication()
00478  *
00479  * Initiates the authentication sequence, with the appropriate SASL
00480  * authentication mechanism.
00481  */
00482 static CURLcode smtp_perform_authentication(struct connectdata *conn)
00483 {
00484   CURLcode result = CURLE_OK;
00485   struct smtp_conn *smtpc = &conn->proto.smtpc;
00486   saslprogress progress;
00487 
00488   /* Check we have enough data to authenticate with, and the
00489      server supports authentiation, and end the connect phase if not */
00490   if(!smtpc->auth_supported ||
00491      !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
00492     state(conn, SMTP_STOP);
00493     return result;
00494   }
00495 
00496   /* Calculate the SASL login details */
00497   result = Curl_sasl_start(&smtpc->sasl, conn, FALSE, &progress);
00498 
00499   if(!result) {
00500     if(progress == SASL_INPROGRESS)
00501       state(conn, SMTP_AUTH);
00502     else {
00503       /* Other mechanisms not supported */
00504       infof(conn->data, "No known authentication mechanisms supported!\n");
00505       result = CURLE_LOGIN_DENIED;
00506     }
00507   }
00508 
00509   return result;
00510 }
00511 
00512 /***********************************************************************
00513  *
00514  * smtp_perform_command()
00515  *
00516  * Sends a SMTP based command.
00517  */
00518 static CURLcode smtp_perform_command(struct connectdata *conn)
00519 {
00520   CURLcode result = CURLE_OK;
00521   struct Curl_easy *data = conn->data;
00522   struct SMTP *smtp = data->req.protop;
00523 
00524   /* Send the command */
00525   if(smtp->rcpt)
00526     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
00527                            smtp->custom && smtp->custom[0] != '\0' ?
00528                            smtp->custom : "VRFY",
00529                            smtp->rcpt->data);
00530   else
00531     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
00532                            smtp->custom && smtp->custom[0] != '\0' ?
00533                            smtp->custom : "HELP");
00534 
00535   if(!result)
00536     state(conn, SMTP_COMMAND);
00537 
00538   return result;
00539 }
00540 
00541 /***********************************************************************
00542  *
00543  * smtp_perform_mail()
00544  *
00545  * Sends an MAIL command to initiate the upload of a message.
00546  */
00547 static CURLcode smtp_perform_mail(struct connectdata *conn)
00548 {
00549   char *from = NULL;
00550   char *auth = NULL;
00551   char *size = NULL;
00552   CURLcode result = CURLE_OK;
00553   struct Curl_easy *data = conn->data;
00554 
00555   /* Calculate the FROM parameter */
00556   if(!data->set.str[STRING_MAIL_FROM])
00557     /* Null reverse-path, RFC-5321, sect. 3.6.3 */
00558     from = strdup("<>");
00559   else if(data->set.str[STRING_MAIL_FROM][0] == '<')
00560     from = aprintf("%s", data->set.str[STRING_MAIL_FROM]);
00561   else
00562     from = aprintf("<%s>", data->set.str[STRING_MAIL_FROM]);
00563 
00564   if(!from)
00565     return CURLE_OUT_OF_MEMORY;
00566 
00567   /* Calculate the optional AUTH parameter */
00568   if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
00569     if(data->set.str[STRING_MAIL_AUTH][0] != '\0')
00570       auth = aprintf("%s", data->set.str[STRING_MAIL_AUTH]);
00571     else
00572       /* Empty AUTH, RFC-2554, sect. 5 */
00573       auth = strdup("<>");
00574 
00575     if(!auth) {
00576       free(from);
00577 
00578       return CURLE_OUT_OF_MEMORY;
00579     }
00580   }
00581 
00582   /* Calculate the optional SIZE parameter */
00583   if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
00584     size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
00585 
00586     if(!size) {
00587       free(from);
00588       free(auth);
00589 
00590       return CURLE_OUT_OF_MEMORY;
00591     }
00592   }
00593 
00594   /* Send the MAIL command */
00595   if(!auth && !size)
00596     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
00597                            "MAIL FROM:%s", from);
00598   else if(auth && !size)
00599     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
00600                            "MAIL FROM:%s AUTH=%s", from, auth);
00601   else if(auth && size)
00602     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
00603                            "MAIL FROM:%s AUTH=%s SIZE=%s", from, auth, size);
00604   else
00605     result = Curl_pp_sendf(&conn->proto.smtpc.pp,
00606                            "MAIL FROM:%s SIZE=%s", from, size);
00607 
00608   free(from);
00609   free(auth);
00610   free(size);
00611 
00612   if(!result)
00613     state(conn, SMTP_MAIL);
00614 
00615   return result;
00616 }
00617 
00618 /***********************************************************************
00619  *
00620  * smtp_perform_rcpt_to()
00621  *
00622  * Sends a RCPT TO command for a given recipient as part of the message upload
00623  * process.
00624  */
00625 static CURLcode smtp_perform_rcpt_to(struct connectdata *conn)
00626 {
00627   CURLcode result = CURLE_OK;
00628   struct Curl_easy *data = conn->data;
00629   struct SMTP *smtp = data->req.protop;
00630 
00631   /* Send the RCPT TO command */
00632   if(smtp->rcpt->data[0] == '<')
00633     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:%s",
00634                            smtp->rcpt->data);
00635   else
00636     result = Curl_pp_sendf(&conn->proto.smtpc.pp, "RCPT TO:<%s>",
00637                            smtp->rcpt->data);
00638   if(!result)
00639     state(conn, SMTP_RCPT);
00640 
00641   return result;
00642 }
00643 
00644 /***********************************************************************
00645  *
00646  * smtp_perform_quit()
00647  *
00648  * Performs the quit action prior to sclose() being called.
00649  */
00650 static CURLcode smtp_perform_quit(struct connectdata *conn)
00651 {
00652   CURLcode result = CURLE_OK;
00653 
00654   /* Send the QUIT command */
00655   result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "QUIT");
00656 
00657   if(!result)
00658     state(conn, SMTP_QUIT);
00659 
00660   return result;
00661 }
00662 
00663 /* For the initial server greeting */
00664 static CURLcode smtp_state_servergreet_resp(struct connectdata *conn,
00665                                             int smtpcode,
00666                                             smtpstate instate)
00667 {
00668   CURLcode result = CURLE_OK;
00669   struct Curl_easy *data = conn->data;
00670 
00671   (void)instate; /* no use for this yet */
00672 
00673   if(smtpcode/100 != 2) {
00674     failf(data, "Got unexpected smtp-server response: %d", smtpcode);
00675     result = CURLE_WEIRD_SERVER_REPLY;
00676   }
00677   else
00678     result = smtp_perform_ehlo(conn);
00679 
00680   return result;
00681 }
00682 
00683 /* For STARTTLS responses */
00684 static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
00685                                          int smtpcode,
00686                                          smtpstate instate)
00687 {
00688   CURLcode result = CURLE_OK;
00689   struct Curl_easy *data = conn->data;
00690 
00691   (void)instate; /* no use for this yet */
00692 
00693   if(smtpcode != 220) {
00694     if(data->set.use_ssl != CURLUSESSL_TRY) {
00695       failf(data, "STARTTLS denied. %c", smtpcode);
00696       result = CURLE_USE_SSL_FAILED;
00697     }
00698     else
00699       result = smtp_perform_authentication(conn);
00700   }
00701   else
00702     result = smtp_perform_upgrade_tls(conn);
00703 
00704   return result;
00705 }
00706 
00707 /* For EHLO responses */
00708 static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
00709                                      smtpstate instate)
00710 {
00711   CURLcode result = CURLE_OK;
00712   struct Curl_easy *data = conn->data;
00713   struct smtp_conn *smtpc = &conn->proto.smtpc;
00714   const char *line = data->state.buffer;
00715   size_t len = strlen(line);
00716   size_t wordlen;
00717 
00718   (void)instate; /* no use for this yet */
00719 
00720   if(smtpcode/100 != 2 && smtpcode != 1) {
00721     if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
00722       result = smtp_perform_helo(conn);
00723     else {
00724       failf(data, "Remote access denied: %d", smtpcode);
00725       result = CURLE_REMOTE_ACCESS_DENIED;
00726     }
00727   }
00728   else {
00729     line += 4;
00730     len -= 4;
00731 
00732     /* Does the server support the STARTTLS capability? */
00733     if(len >= 8 && !memcmp(line, "STARTTLS", 8))
00734       smtpc->tls_supported = TRUE;
00735 
00736     /* Does the server support the SIZE capability? */
00737     else if(len >= 4 && !memcmp(line, "SIZE", 4))
00738       smtpc->size_supported = TRUE;
00739 
00740     /* Does the server support authentication? */
00741     else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
00742       smtpc->auth_supported = TRUE;
00743 
00744       /* Advance past the AUTH keyword */
00745       line += 5;
00746       len -= 5;
00747 
00748       /* Loop through the data line */
00749       for(;;) {
00750         size_t llen;
00751         unsigned int mechbit;
00752 
00753         while(len &&
00754               (*line == ' ' || *line == '\t' ||
00755                *line == '\r' || *line == '\n')) {
00756 
00757           line++;
00758           len--;
00759         }
00760 
00761         if(!len)
00762           break;
00763 
00764         /* Extract the word */
00765         for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
00766               line[wordlen] != '\t' && line[wordlen] != '\r' &&
00767               line[wordlen] != '\n';)
00768           wordlen++;
00769 
00770         /* Test the word for a matching authentication mechanism */
00771         mechbit = Curl_sasl_decode_mech(line, wordlen, &llen);
00772         if(mechbit && llen == wordlen)
00773           smtpc->sasl.authmechs |= mechbit;
00774 
00775         line += wordlen;
00776         len -= wordlen;
00777       }
00778     }
00779 
00780     if(smtpcode != 1) {
00781       if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
00782         /* We don't have a SSL/TLS connection yet, but SSL is requested */
00783         if(smtpc->tls_supported)
00784           /* Switch to TLS connection now */
00785           result = smtp_perform_starttls(conn);
00786         else if(data->set.use_ssl == CURLUSESSL_TRY)
00787           /* Fallback and carry on with authentication */
00788           result = smtp_perform_authentication(conn);
00789         else {
00790           failf(data, "STARTTLS not supported.");
00791           result = CURLE_USE_SSL_FAILED;
00792         }
00793       }
00794       else
00795         result = smtp_perform_authentication(conn);
00796     }
00797   }
00798 
00799   return result;
00800 }
00801 
00802 /* For HELO responses */
00803 static CURLcode smtp_state_helo_resp(struct connectdata *conn, int smtpcode,
00804                                      smtpstate instate)
00805 {
00806   CURLcode result = CURLE_OK;
00807   struct Curl_easy *data = conn->data;
00808 
00809   (void)instate; /* no use for this yet */
00810 
00811   if(smtpcode/100 != 2) {
00812     failf(data, "Remote access denied: %d", smtpcode);
00813     result = CURLE_REMOTE_ACCESS_DENIED;
00814   }
00815   else
00816     /* End of connect phase */
00817     state(conn, SMTP_STOP);
00818 
00819   return result;
00820 }
00821 
00822 /* For SASL authentication responses */
00823 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
00824                                      int smtpcode,
00825                                      smtpstate instate)
00826 {
00827   CURLcode result = CURLE_OK;
00828   struct Curl_easy *data = conn->data;
00829   struct smtp_conn *smtpc = &conn->proto.smtpc;
00830   saslprogress progress;
00831 
00832   (void)instate; /* no use for this yet */
00833 
00834   result = Curl_sasl_continue(&smtpc->sasl, conn, smtpcode, &progress);
00835   if(!result)
00836     switch(progress) {
00837     case SASL_DONE:
00838       state(conn, SMTP_STOP);  /* Authenticated */
00839       break;
00840     case SASL_IDLE:            /* No mechanism left after cancellation */
00841       failf(data, "Authentication cancelled");
00842       result = CURLE_LOGIN_DENIED;
00843       break;
00844     default:
00845       break;
00846     }
00847 
00848   return result;
00849 }
00850 
00851 /* For command responses */
00852 static CURLcode smtp_state_command_resp(struct connectdata *conn, int smtpcode,
00853                                         smtpstate instate)
00854 {
00855   CURLcode result = CURLE_OK;
00856   struct Curl_easy *data = conn->data;
00857   struct SMTP *smtp = data->req.protop;
00858   char *line = data->state.buffer;
00859   size_t len = strlen(line);
00860 
00861   (void)instate; /* no use for this yet */
00862 
00863   if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) ||
00864      (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) {
00865     failf(data, "Command failed: %d", smtpcode);
00866     result = CURLE_RECV_ERROR;
00867   }
00868   else {
00869     /* Temporarily add the LF character back and send as body to the client */
00870     if(!data->set.opt_no_body) {
00871       line[len] = '\n';
00872       result = Curl_client_write(conn, CLIENTWRITE_BODY, line, len + 1);
00873       line[len] = '\0';
00874     }
00875 
00876     if(smtpcode != 1) {
00877       if(smtp->rcpt) {
00878         smtp->rcpt = smtp->rcpt->next;
00879 
00880         if(smtp->rcpt) {
00881           /* Send the next command */
00882           result = smtp_perform_command(conn);
00883         }
00884         else
00885           /* End of DO phase */
00886           state(conn, SMTP_STOP);
00887       }
00888       else
00889         /* End of DO phase */
00890         state(conn, SMTP_STOP);
00891     }
00892   }
00893 
00894   return result;
00895 }
00896 
00897 /* For MAIL responses */
00898 static CURLcode smtp_state_mail_resp(struct connectdata *conn, int smtpcode,
00899                                      smtpstate instate)
00900 {
00901   CURLcode result = CURLE_OK;
00902   struct Curl_easy *data = conn->data;
00903 
00904   (void)instate; /* no use for this yet */
00905 
00906   if(smtpcode/100 != 2) {
00907     failf(data, "MAIL failed: %d", smtpcode);
00908     result = CURLE_SEND_ERROR;
00909   }
00910   else
00911     /* Start the RCPT TO command */
00912     result = smtp_perform_rcpt_to(conn);
00913 
00914   return result;
00915 }
00916 
00917 /* For RCPT responses */
00918 static CURLcode smtp_state_rcpt_resp(struct connectdata *conn, int smtpcode,
00919                                      smtpstate instate)
00920 {
00921   CURLcode result = CURLE_OK;
00922   struct Curl_easy *data = conn->data;
00923   struct SMTP *smtp = data->req.protop;
00924 
00925   (void)instate; /* no use for this yet */
00926 
00927   if(smtpcode/100 != 2) {
00928     failf(data, "RCPT failed: %d", smtpcode);
00929     result = CURLE_SEND_ERROR;
00930   }
00931   else {
00932     smtp->rcpt = smtp->rcpt->next;
00933 
00934     if(smtp->rcpt)
00935       /* Send the next RCPT TO command */
00936       result = smtp_perform_rcpt_to(conn);
00937     else {
00938       /* Send the DATA command */
00939       result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "DATA");
00940 
00941       if(!result)
00942         state(conn, SMTP_DATA);
00943     }
00944   }
00945 
00946   return result;
00947 }
00948 
00949 /* For DATA response */
00950 static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
00951                                      smtpstate instate)
00952 {
00953   CURLcode result = CURLE_OK;
00954   struct Curl_easy *data = conn->data;
00955 
00956   (void)instate; /* no use for this yet */
00957 
00958   if(smtpcode != 354) {
00959     failf(data, "DATA failed: %d", smtpcode);
00960     result = CURLE_SEND_ERROR;
00961   }
00962   else {
00963     /* Set the progress upload size */
00964     Curl_pgrsSetUploadSize(data, data->state.infilesize);
00965 
00966     /* SMTP upload */
00967     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
00968 
00969     /* End of DO phase */
00970     state(conn, SMTP_STOP);
00971   }
00972 
00973   return result;
00974 }
00975 
00976 /* For POSTDATA responses, which are received after the entire DATA
00977    part has been sent to the server */
00978 static CURLcode smtp_state_postdata_resp(struct connectdata *conn,
00979                                          int smtpcode,
00980                                          smtpstate instate)
00981 {
00982   CURLcode result = CURLE_OK;
00983 
00984   (void)instate; /* no use for this yet */
00985 
00986   if(smtpcode != 250)
00987     result = CURLE_RECV_ERROR;
00988 
00989   /* End of DONE phase */
00990   state(conn, SMTP_STOP);
00991 
00992   return result;
00993 }
00994 
00995 static CURLcode smtp_statemach_act(struct connectdata *conn)
00996 {
00997   CURLcode result = CURLE_OK;
00998   curl_socket_t sock = conn->sock[FIRSTSOCKET];
00999   struct Curl_easy *data = conn->data;
01000   int smtpcode;
01001   struct smtp_conn *smtpc = &conn->proto.smtpc;
01002   struct pingpong *pp = &smtpc->pp;
01003   size_t nread = 0;
01004 
01005   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
01006   if(smtpc->state == SMTP_UPGRADETLS)
01007     return smtp_perform_upgrade_tls(conn);
01008 
01009   /* Flush any data that needs to be sent */
01010   if(pp->sendleft)
01011     return Curl_pp_flushsend(pp);
01012 
01013   do {
01014     /* Read the response from the server */
01015     result = Curl_pp_readresp(sock, pp, &smtpcode, &nread);
01016     if(result)
01017       return result;
01018 
01019     /* Store the latest response for later retrieval if necessary */
01020     if(smtpc->state != SMTP_QUIT && smtpcode != 1)
01021       data->info.httpcode = smtpcode;
01022 
01023     if(!smtpcode)
01024       break;
01025 
01026     /* We have now received a full SMTP server response */
01027     switch(smtpc->state) {
01028     case SMTP_SERVERGREET:
01029       result = smtp_state_servergreet_resp(conn, smtpcode, smtpc->state);
01030       break;
01031 
01032     case SMTP_EHLO:
01033       result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state);
01034       break;
01035 
01036     case SMTP_HELO:
01037       result = smtp_state_helo_resp(conn, smtpcode, smtpc->state);
01038       break;
01039 
01040     case SMTP_STARTTLS:
01041       result = smtp_state_starttls_resp(conn, smtpcode, smtpc->state);
01042       break;
01043 
01044     case SMTP_AUTH:
01045       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
01046       break;
01047 
01048     case SMTP_COMMAND:
01049       result = smtp_state_command_resp(conn, smtpcode, smtpc->state);
01050       break;
01051 
01052     case SMTP_MAIL:
01053       result = smtp_state_mail_resp(conn, smtpcode, smtpc->state);
01054       break;
01055 
01056     case SMTP_RCPT:
01057       result = smtp_state_rcpt_resp(conn, smtpcode, smtpc->state);
01058       break;
01059 
01060     case SMTP_DATA:
01061       result = smtp_state_data_resp(conn, smtpcode, smtpc->state);
01062       break;
01063 
01064     case SMTP_POSTDATA:
01065       result = smtp_state_postdata_resp(conn, smtpcode, smtpc->state);
01066       break;
01067 
01068     case SMTP_QUIT:
01069       /* fallthrough, just stop! */
01070     default:
01071       /* internal error */
01072       state(conn, SMTP_STOP);
01073       break;
01074     }
01075   } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
01076 
01077   return result;
01078 }
01079 
01080 /* Called repeatedly until done from multi.c */
01081 static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done)
01082 {
01083   CURLcode result = CURLE_OK;
01084   struct smtp_conn *smtpc = &conn->proto.smtpc;
01085 
01086   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
01087     result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &smtpc->ssldone);
01088     if(result || !smtpc->ssldone)
01089       return result;
01090   }
01091 
01092   result = Curl_pp_statemach(&smtpc->pp, FALSE);
01093   *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE;
01094 
01095   return result;
01096 }
01097 
01098 static CURLcode smtp_block_statemach(struct connectdata *conn)
01099 {
01100   CURLcode result = CURLE_OK;
01101   struct smtp_conn *smtpc = &conn->proto.smtpc;
01102 
01103   while(smtpc->state != SMTP_STOP && !result)
01104     result = Curl_pp_statemach(&smtpc->pp, TRUE);
01105 
01106   return result;
01107 }
01108 
01109 /* Allocate and initialize the SMTP struct for the current Curl_easy if
01110    required */
01111 static CURLcode smtp_init(struct connectdata *conn)
01112 {
01113   CURLcode result = CURLE_OK;
01114   struct Curl_easy *data = conn->data;
01115   struct SMTP *smtp;
01116 
01117   smtp = data->req.protop = calloc(sizeof(struct SMTP), 1);
01118   if(!smtp)
01119     result = CURLE_OUT_OF_MEMORY;
01120 
01121   return result;
01122 }
01123 
01124 /* For the SMTP "protocol connect" and "doing" phases only */
01125 static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
01126                         int numsocks)
01127 {
01128   return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
01129 }
01130 
01131 /***********************************************************************
01132  *
01133  * smtp_connect()
01134  *
01135  * This function should do everything that is to be considered a part of
01136  * the connection phase.
01137  *
01138  * The variable pointed to by 'done' will be TRUE if the protocol-layer
01139  * connect phase is done when this function returns, or FALSE if not.
01140  */
01141 static CURLcode smtp_connect(struct connectdata *conn, bool *done)
01142 {
01143   CURLcode result = CURLE_OK;
01144   struct smtp_conn *smtpc = &conn->proto.smtpc;
01145   struct pingpong *pp = &smtpc->pp;
01146 
01147   *done = FALSE; /* default to not done yet */
01148 
01149   /* We always support persistent connections in SMTP */
01150   connkeep(conn, "SMTP default");
01151 
01152   /* Set the default response time-out */
01153   pp->response_time = RESP_TIMEOUT;
01154   pp->statemach_act = smtp_statemach_act;
01155   pp->endofresp = smtp_endofresp;
01156   pp->conn = conn;
01157 
01158   /* Initialize the SASL storage */
01159   Curl_sasl_init(&smtpc->sasl, &saslsmtp);
01160 
01161   /* Initialise the pingpong layer */
01162   Curl_pp_init(pp);
01163 
01164   /* Parse the URL options */
01165   result = smtp_parse_url_options(conn);
01166   if(result)
01167     return result;
01168 
01169   /* Parse the URL path */
01170   result = smtp_parse_url_path(conn);
01171   if(result)
01172     return result;
01173 
01174   /* Start off waiting for the server greeting response */
01175   state(conn, SMTP_SERVERGREET);
01176 
01177   result = smtp_multi_statemach(conn, done);
01178 
01179   return result;
01180 }
01181 
01182 /***********************************************************************
01183  *
01184  * smtp_done()
01185  *
01186  * The DONE function. This does what needs to be done after a single DO has
01187  * performed.
01188  *
01189  * Input argument is already checked for validity.
01190  */
01191 static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
01192                           bool premature)
01193 {
01194   CURLcode result = CURLE_OK;
01195   struct Curl_easy *data = conn->data;
01196   struct SMTP *smtp = data->req.protop;
01197   struct pingpong *pp = &conn->proto.smtpc.pp;
01198   char *eob;
01199   ssize_t len;
01200   ssize_t bytes_written;
01201 
01202   (void)premature;
01203 
01204   if(!smtp || !pp->conn)
01205     return CURLE_OK;
01206 
01207   if(status) {
01208     connclose(conn, "SMTP done with bad status"); /* marked for closure */
01209     result = status;         /* use the already set error code */
01210   }
01211   else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
01212     /* Calculate the EOB taking into account any terminating CRLF from the
01213        previous line of the email or the CRLF of the DATA command when there
01214        is "no mail data". RFC-5321, sect. 4.1.1.4.
01215 
01216        Note: As some SSL backends, such as OpenSSL, will cause Curl_write() to
01217        fail when using a different pointer following a previous write, that
01218        returned CURLE_AGAIN, we duplicate the EOB now rather than when the
01219        bytes written doesn't equal len. */
01220     if(smtp->trailing_crlf || !conn->data->state.infilesize) {
01221       eob = strdup(SMTP_EOB + 2);
01222       len = SMTP_EOB_LEN - 2;
01223     }
01224     else {
01225       eob = strdup(SMTP_EOB);
01226       len = SMTP_EOB_LEN;
01227     }
01228 
01229     if(!eob)
01230       return CURLE_OUT_OF_MEMORY;
01231 
01232     /* Send the end of block data */
01233     result = Curl_write(conn, conn->writesockfd, eob, len, &bytes_written);
01234     if(result) {
01235       free(eob);
01236       return result;
01237     }
01238 
01239     if(bytes_written != len) {
01240       /* The whole chunk was not sent so keep it around and adjust the
01241          pingpong structure accordingly */
01242       pp->sendthis = eob;
01243       pp->sendsize = len;
01244       pp->sendleft = len - bytes_written;
01245     }
01246     else {
01247       /* Successfully sent so adjust the response timeout relative to now */
01248       pp->response = Curl_tvnow();
01249 
01250       free(eob);
01251     }
01252 
01253     state(conn, SMTP_POSTDATA);
01254 
01255     /* Run the state-machine
01256 
01257        TODO: when the multi interface is used, this _really_ should be using
01258        the smtp_multi_statemach function but we have no general support for
01259        non-blocking DONE operations!
01260     */
01261     result = smtp_block_statemach(conn);
01262   }
01263 
01264   /* Cleanup our per-request based variables */
01265   Curl_safefree(smtp->custom);
01266 
01267   /* Clear the transfer mode for the next request */
01268   smtp->transfer = FTPTRANSFER_BODY;
01269 
01270   return result;
01271 }
01272 
01273 /***********************************************************************
01274  *
01275  * smtp_perform()
01276  *
01277  * This is the actual DO function for SMTP. Transfer a mail, send a command
01278  * or get some data according to the options previously setup.
01279  */
01280 static CURLcode smtp_perform(struct connectdata *conn, bool *connected,
01281                              bool *dophase_done)
01282 {
01283   /* This is SMTP and no proxy */
01284   CURLcode result = CURLE_OK;
01285   struct Curl_easy *data = conn->data;
01286   struct SMTP *smtp = data->req.protop;
01287 
01288   DEBUGF(infof(conn->data, "DO phase starts\n"));
01289 
01290   if(data->set.opt_no_body) {
01291     /* Requested no body means no transfer */
01292     smtp->transfer = FTPTRANSFER_INFO;
01293   }
01294 
01295   *dophase_done = FALSE; /* not done yet */
01296 
01297   /* Store the first recipient (or NULL if not specified) */
01298   smtp->rcpt = data->set.mail_rcpt;
01299 
01300   /* Start the first command in the DO phase */
01301   if(data->set.upload && data->set.mail_rcpt)
01302     /* MAIL transfer */
01303     result = smtp_perform_mail(conn);
01304   else
01305     /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
01306     result = smtp_perform_command(conn);
01307 
01308   if(result)
01309     return result;
01310 
01311   /* Run the state-machine */
01312   result = smtp_multi_statemach(conn, dophase_done);
01313 
01314   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
01315 
01316   if(*dophase_done)
01317     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01318 
01319   return result;
01320 }
01321 
01322 /***********************************************************************
01323  *
01324  * smtp_do()
01325  *
01326  * This function is registered as 'curl_do' function. It decodes the path
01327  * parts etc as a wrapper to the actual DO function (smtp_perform).
01328  *
01329  * The input argument is already checked for validity.
01330  */
01331 static CURLcode smtp_do(struct connectdata *conn, bool *done)
01332 {
01333   CURLcode result = CURLE_OK;
01334 
01335   *done = FALSE; /* default to false */
01336 
01337   /* Parse the custom request */
01338   result = smtp_parse_custom_request(conn);
01339   if(result)
01340     return result;
01341 
01342   result = smtp_regular_transfer(conn, done);
01343 
01344   return result;
01345 }
01346 
01347 /***********************************************************************
01348  *
01349  * smtp_disconnect()
01350  *
01351  * Disconnect from an SMTP server. Cleanup protocol-specific per-connection
01352  * resources. BLOCKING.
01353  */
01354 static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
01355 {
01356   struct smtp_conn *smtpc = &conn->proto.smtpc;
01357 
01358   /* We cannot send quit unconditionally. If this connection is stale or
01359      bad in any way, sending quit and waiting around here will make the
01360      disconnect wait in vain and cause more problems than we need to. */
01361 
01362   /* The SMTP session may or may not have been allocated/setup at this
01363      point! */
01364   if(!dead_connection && smtpc->pp.conn && smtpc->pp.conn->bits.protoconnstart)
01365     if(!smtp_perform_quit(conn))
01366       (void)smtp_block_statemach(conn); /* ignore errors on QUIT */
01367 
01368   /* Disconnect from the server */
01369   Curl_pp_disconnect(&smtpc->pp);
01370 
01371   /* Cleanup the SASL module */
01372   Curl_sasl_cleanup(conn, smtpc->sasl.authused);
01373 
01374   /* Cleanup our connection based variables */
01375   Curl_safefree(smtpc->domain);
01376 
01377   return CURLE_OK;
01378 }
01379 
01380 /* Call this when the DO phase has completed */
01381 static CURLcode smtp_dophase_done(struct connectdata *conn, bool connected)
01382 {
01383   struct SMTP *smtp = conn->data->req.protop;
01384 
01385   (void)connected;
01386 
01387   if(smtp->transfer != FTPTRANSFER_BODY)
01388     /* no data to transfer */
01389     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
01390 
01391   return CURLE_OK;
01392 }
01393 
01394 /* Called from multi.c while DOing */
01395 static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done)
01396 {
01397   CURLcode result = smtp_multi_statemach(conn, dophase_done);
01398 
01399   if(result)
01400     DEBUGF(infof(conn->data, "DO phase failed\n"));
01401   else if(*dophase_done) {
01402     result = smtp_dophase_done(conn, FALSE /* not connected */);
01403 
01404     DEBUGF(infof(conn->data, "DO phase is complete\n"));
01405   }
01406 
01407   return result;
01408 }
01409 
01410 /***********************************************************************
01411  *
01412  * smtp_regular_transfer()
01413  *
01414  * The input argument is already checked for validity.
01415  *
01416  * Performs all commands done before a regular transfer between a local and a
01417  * remote host.
01418  */
01419 static CURLcode smtp_regular_transfer(struct connectdata *conn,
01420                                       bool *dophase_done)
01421 {
01422   CURLcode result = CURLE_OK;
01423   bool connected = FALSE;
01424   struct Curl_easy *data = conn->data;
01425 
01426   /* Make sure size is unknown at this point */
01427   data->req.size = -1;
01428 
01429   /* Set the progress data */
01430   Curl_pgrsSetUploadCounter(data, 0);
01431   Curl_pgrsSetDownloadCounter(data, 0);
01432   Curl_pgrsSetUploadSize(data, -1);
01433   Curl_pgrsSetDownloadSize(data, -1);
01434 
01435   /* Carry out the perform */
01436   result = smtp_perform(conn, &connected, dophase_done);
01437 
01438   /* Perform post DO phase operations if necessary */
01439   if(!result && *dophase_done)
01440     result = smtp_dophase_done(conn, connected);
01441 
01442   return result;
01443 }
01444 
01445 static CURLcode smtp_setup_connection(struct connectdata *conn)
01446 {
01447   struct Curl_easy *data = conn->data;
01448   CURLcode result;
01449 
01450   /* Clear the TLS upgraded flag */
01451   conn->tls_upgraded = FALSE;
01452 
01453   /* Set up the proxy if necessary */
01454   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
01455     /* Unless we have asked to tunnel SMTP operations through the proxy, we
01456        switch and use HTTP operations only */
01457 #ifndef CURL_DISABLE_HTTP
01458     if(conn->handler == &Curl_handler_smtp)
01459       conn->handler = &Curl_handler_smtp_proxy;
01460     else {
01461 #ifdef USE_SSL
01462       conn->handler = &Curl_handler_smtps_proxy;
01463 #else
01464       failf(data, "SMTPS not supported!");
01465       return CURLE_UNSUPPORTED_PROTOCOL;
01466 #endif
01467     }
01468     /* set it up as a HTTP connection instead */
01469     return conn->handler->setup_connection(conn);
01470 
01471 #else
01472     failf(data, "SMTP over http proxy requires HTTP support built-in!");
01473     return CURLE_UNSUPPORTED_PROTOCOL;
01474 #endif
01475   }
01476 
01477   /* Initialise the SMTP layer */
01478   result = smtp_init(conn);
01479   if(result)
01480     return result;
01481 
01482   data->state.path++;   /* don't include the initial slash */
01483 
01484   return CURLE_OK;
01485 }
01486 
01487 /***********************************************************************
01488  *
01489  * smtp_parse_url_options()
01490  *
01491  * Parse the URL login options.
01492  */
01493 static CURLcode smtp_parse_url_options(struct connectdata *conn)
01494 {
01495   CURLcode result = CURLE_OK;
01496   struct smtp_conn *smtpc = &conn->proto.smtpc;
01497   const char *ptr = conn->options;
01498 
01499   smtpc->sasl.resetprefs = TRUE;
01500 
01501   while(!result && ptr && *ptr) {
01502     const char *key = ptr;
01503     const char *value;
01504 
01505     while(*ptr && *ptr != '=')
01506       ptr++;
01507 
01508     value = ptr + 1;
01509 
01510     while(*ptr && *ptr != ';')
01511       ptr++;
01512 
01513     if(strncasecompare(key, "AUTH=", 5))
01514       result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
01515                                                value, ptr - value);
01516     else
01517       result = CURLE_URL_MALFORMAT;
01518 
01519     if(*ptr == ';')
01520       ptr++;
01521   }
01522 
01523   return result;
01524 }
01525 
01526 /***********************************************************************
01527  *
01528  * smtp_parse_url_path()
01529  *
01530  * Parse the URL path into separate path components.
01531  */
01532 static CURLcode smtp_parse_url_path(struct connectdata *conn)
01533 {
01534   /* The SMTP struct is already initialised in smtp_connect() */
01535   struct Curl_easy *data = conn->data;
01536   struct smtp_conn *smtpc = &conn->proto.smtpc;
01537   const char *path = data->state.path;
01538   char localhost[HOSTNAME_MAX + 1];
01539 
01540   /* Calculate the path if necessary */
01541   if(!*path) {
01542     if(!Curl_gethostname(localhost, sizeof(localhost)))
01543       path = localhost;
01544     else
01545       path = "localhost";
01546   }
01547 
01548   /* URL decode the path and use it as the domain in our EHLO */
01549   return Curl_urldecode(conn->data, path, 0, &smtpc->domain, NULL, TRUE);
01550 }
01551 
01552 /***********************************************************************
01553  *
01554  * smtp_parse_custom_request()
01555  *
01556  * Parse the custom request.
01557  */
01558 static CURLcode smtp_parse_custom_request(struct connectdata *conn)
01559 {
01560   CURLcode result = CURLE_OK;
01561   struct Curl_easy *data = conn->data;
01562   struct SMTP *smtp = data->req.protop;
01563   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
01564 
01565   /* URL decode the custom request */
01566   if(custom)
01567     result = Curl_urldecode(data, custom, 0, &smtp->custom, NULL, TRUE);
01568 
01569   return result;
01570 }
01571 
01572 CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
01573 {
01574   /* When sending a SMTP payload we must detect CRLF. sequences making sure
01575      they are sent as CRLF.. instead, as a . on the beginning of a line will
01576      be deleted by the server when not part of an EOB terminator and a
01577      genuine CRLF.CRLF which isn't escaped will wrongly be detected as end of
01578      data by the server
01579   */
01580   ssize_t i;
01581   ssize_t si;
01582   struct Curl_easy *data = conn->data;
01583   struct SMTP *smtp = data->req.protop;
01584   char *scratch = data->state.scratch;
01585   char *newscratch = NULL;
01586   char *oldscratch = NULL;
01587   size_t eob_sent;
01588 
01589   /* Do we need to allocate a scratch buffer? */
01590   if(!scratch || data->set.crlf) {
01591     oldscratch = scratch;
01592 
01593     scratch = newscratch = malloc(2 * BUFSIZE);
01594     if(!newscratch) {
01595       failf(data, "Failed to alloc scratch buffer!");
01596 
01597       return CURLE_OUT_OF_MEMORY;
01598     }
01599   }
01600 
01601   /* Have we already sent part of the EOB? */
01602   eob_sent = smtp->eob;
01603 
01604   /* This loop can be improved by some kind of Boyer-Moore style of
01605      approach but that is saved for later... */
01606   for(i = 0, si = 0; i < nread; i++) {
01607     if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
01608       smtp->eob++;
01609 
01610       /* Is the EOB potentially the terminating CRLF? */
01611       if(2 == smtp->eob || SMTP_EOB_LEN == smtp->eob)
01612         smtp->trailing_crlf = TRUE;
01613       else
01614         smtp->trailing_crlf = FALSE;
01615     }
01616     else if(smtp->eob) {
01617       /* A previous substring matched so output that first */
01618       memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
01619       si += smtp->eob - eob_sent;
01620 
01621       /* Then compare the first byte */
01622       if(SMTP_EOB[0] == data->req.upload_fromhere[i])
01623         smtp->eob = 1;
01624       else
01625         smtp->eob = 0;
01626 
01627       eob_sent = 0;
01628 
01629       /* Reset the trailing CRLF flag as there was more data */
01630       smtp->trailing_crlf = FALSE;
01631     }
01632 
01633     /* Do we have a match for CRLF. as per RFC-5321, sect. 4.5.2 */
01634     if(SMTP_EOB_FIND_LEN == smtp->eob) {
01635       /* Copy the replacement data to the target buffer */
01636       memcpy(&scratch[si], &SMTP_EOB_REPL[eob_sent],
01637              SMTP_EOB_REPL_LEN - eob_sent);
01638       si += SMTP_EOB_REPL_LEN - eob_sent;
01639       smtp->eob = 0;
01640       eob_sent = 0;
01641     }
01642     else if(!smtp->eob)
01643       scratch[si++] = data->req.upload_fromhere[i];
01644   }
01645 
01646   if(smtp->eob - eob_sent) {
01647     /* A substring matched before processing ended so output that now */
01648     memcpy(&scratch[si], &SMTP_EOB[eob_sent], smtp->eob - eob_sent);
01649     si += smtp->eob - eob_sent;
01650   }
01651 
01652   /* Only use the new buffer if we replaced something */
01653   if(si != nread) {
01654     /* Upload from the new (replaced) buffer instead */
01655     data->req.upload_fromhere = scratch;
01656 
01657     /* Save the buffer so it can be freed later */
01658     data->state.scratch = scratch;
01659 
01660     /* Free the old scratch buffer */
01661     free(oldscratch);
01662 
01663     /* Set the new amount too */
01664     data->req.upload_present = si;
01665   }
01666   else
01667     free(newscratch);
01668 
01669   return CURLE_OK;
01670 }
01671 
01672 #endif /* CURL_DISABLE_SMTP */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06