lib: reorder protocol functions to avoid forward declarations (email)

For protocols: imap, pop3, smtp.

Move protocol hander table to the end of sources, rearrange static
functions is reverse dependency order as necessary.

Closes #20275
This commit is contained in:
Viktor Szakats 2025-12-21 16:33:06 +01:00
parent 07926b5982
commit 2ded1e3c6e
No known key found for this signature in database
GPG Key ID: B5ABD165E2AEF201
3 changed files with 1294 additions and 1400 deletions

1041
lib/imap.c

File diff suppressed because it is too large Load Diff

View File

@ -135,111 +135,6 @@ struct pop3_conn {
BIT(tls_supported); /* StartTLS capability supported by server */
};
/* Local API functions */
static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
static CURLcode pop3_do(struct Curl_easy *data, bool *done);
static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
bool premature);
static CURLcode pop3_connect(struct Curl_easy *data, bool *done);
static CURLcode pop3_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead);
static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done);
static CURLcode pop3_pollset(struct Curl_easy *data,
struct easy_pollset *ps);
static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done);
static CURLcode pop3_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static CURLcode pop3_parse_url_options(struct connectdata *conn);
static CURLcode pop3_parse_url_path(struct Curl_easy *data);
static CURLcode pop3_parse_custom_request(struct Curl_easy *data);
static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech,
const struct bufref *initresp);
static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp);
static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
/* This function scans the body after the end-of-body and writes everything
* until the end is found */
static CURLcode pop3_write(struct Curl_easy *data,
const char *str, size_t nread, bool is_eos);
/*
* POP3 protocol handler.
*/
const struct Curl_handler Curl_handler_pop3 = {
"pop3", /* scheme */
pop3_setup_connection, /* setup_connection */
pop3_do, /* do_it */
pop3_done, /* done */
ZERO_NULL, /* do_more */
pop3_connect, /* connect_it */
pop3_multi_statemach, /* connecting */
pop3_doing, /* doing */
pop3_pollset, /* proto_pollset */
pop3_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
pop3_disconnect, /* disconnect */
pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_POP3, /* defport */
CURLPROTO_POP3, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE
};
#ifdef USE_SSL
/*
* POP3S protocol handler.
*/
const struct Curl_handler Curl_handler_pop3s = {
"pop3s", /* scheme */
pop3_setup_connection, /* setup_connection */
pop3_do, /* do_it */
pop3_done, /* done */
ZERO_NULL, /* do_more */
pop3_connect, /* connect_it */
pop3_multi_statemach, /* connecting */
pop3_doing, /* doing */
pop3_pollset, /* proto_pollset */
pop3_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
pop3_disconnect, /* disconnect */
pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_POP3S, /* defport */
CURLPROTO_POP3S, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE
};
#endif
/* SASL parameters for the pop3 protocol */
static const struct SASLproto saslpop3 = {
"pop", /* The service name */
pop3_perform_auth, /* Send authentication command */
pop3_continue_auth, /* Send authentication continuation */
pop3_cancel_auth, /* Send authentication cancellation */
pop3_get_message, /* Get SASL response message */
255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
'*', /* Code received when continuation is expected */
'+', /* Code to receive upon authentication success */
SASL_AUTH_DEFAULT, /* Default mechanisms */
SASL_FLAG_BASE64 /* Configuration flags */
};
struct pop3_cmd {
const char *name;
unsigned short nlen;
@ -268,6 +163,105 @@ static const struct pop3_cmd pop3cmds[] = {
{ "XTND", 4, TRUE, TRUE },
};
/***********************************************************************
*
* pop3_parse_url_options()
*
* Parse the URL login options.
*/
static CURLcode pop3_parse_url_options(struct connectdata *conn)
{
struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
const char *ptr = conn->options;
if(!pop3c)
return CURLE_FAILED_INIT;
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
while(*ptr && *ptr != '=')
ptr++;
value = ptr + 1;
while(*ptr && *ptr != ';')
ptr++;
if(curl_strnequal(key, "AUTH=", 5)) {
result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
value, ptr - value);
if(result && curl_strnequal(value, "+APOP", ptr - value)) {
pop3c->preftype = POP3_TYPE_APOP;
pop3c->sasl.prefmech = SASL_AUTH_NONE;
result = CURLE_OK;
}
}
else
result = CURLE_URL_MALFORMAT;
if(*ptr == ';')
ptr++;
}
if(pop3c->preftype != POP3_TYPE_APOP)
switch(pop3c->sasl.prefmech) {
case SASL_AUTH_NONE:
pop3c->preftype = POP3_TYPE_NONE;
break;
case SASL_AUTH_DEFAULT:
pop3c->preftype = POP3_TYPE_ANY;
break;
default:
pop3c->preftype = POP3_TYPE_SASL;
break;
}
return result;
}
/***********************************************************************
*
* pop3_parse_url_path()
*
* Parse the URL path into separate path components.
*/
static CURLcode pop3_parse_url_path(struct Curl_easy *data)
{
/* The POP3 struct is already initialised in pop3_connect() */
struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *path = &data->state.up.path[1]; /* skip leading path */
if(!pop3)
return CURLE_FAILED_INIT;
/* URL decode the path for the message ID */
return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
}
/***********************************************************************
*
* pop3_parse_custom_request()
*
* Parse the custom request.
*/
static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
if(!pop3)
return CURLE_FAILED_INIT;
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
return result;
}
/* Return iff a command is defined as "multi-line" (RFC 1939),
* has a response terminated by a last line with a '.'.
*/
@ -1070,6 +1064,146 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
return result;
}
/***********************************************************************
*
* pop3_write()
*
* This function scans the body after the end-of-body and writes everything
* until the end is found.
*/
static CURLcode pop3_write(struct Curl_easy *data, const char *str,
size_t nread, bool is_eos)
{
/* This code could be made into a special function in the handler struct */
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
struct connectdata *conn = data->conn;
struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
bool strip_dot = FALSE;
size_t last = 0;
size_t i;
(void)is_eos;
if(!pop3c)
return CURLE_FAILED_INIT;
/* Search through the buffer looking for the end-of-body marker which is
5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
the eob so the server will have prefixed it with an extra dot which we
need to strip out. Additionally the marker could of course be spread out
over 5 different data chunks. */
for(i = 0; i < nread; i++) {
size_t prev = pop3c->eob;
switch(str[i]) {
case 0x0d:
if(pop3c->eob == 0) {
pop3c->eob++;
if(i) {
/* Write out the body part that did not match */
result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
i - last);
if(result)
return result;
last = i;
}
}
else if(pop3c->eob == 3)
pop3c->eob++;
else
/* If the character match was not at position 0 or 3 then restart the
pattern matching */
pop3c->eob = 1;
break;
case 0x0a:
if(pop3c->eob == 1 || pop3c->eob == 4)
pop3c->eob++;
else
/* If the character match was not at position 1 or 4 then start the
search again */
pop3c->eob = 0;
break;
case 0x2e:
if(pop3c->eob == 2)
pop3c->eob++;
else if(pop3c->eob == 3) {
/* We have an extra dot after the CRLF which we need to strip off */
strip_dot = TRUE;
pop3c->eob = 0;
}
else
/* If the character match was not at position 2 then start the search
again */
pop3c->eob = 0;
break;
default:
pop3c->eob = 0;
break;
}
/* Did we have a partial match which has subsequently failed? */
if(prev && prev >= pop3c->eob) {
/* Strip can only be non-zero for the first mismatch after CRLF and
then both prev and strip are equal and nothing will be output below */
while(prev && pop3c->strip) {
prev--;
pop3c->strip--;
}
if(prev) {
/* If the partial match was the CRLF and dot then only write the CRLF
as the server would have inserted the dot */
if(strip_dot && prev - 1 > 0) {
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev - 1);
}
else if(!strip_dot) {
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev);
}
else {
result = CURLE_OK;
}
if(result)
return result;
last = i;
strip_dot = FALSE;
}
}
}
if(pop3c->eob == POP3_EOB_LEN) {
/* We have a full match so the transfer is done, however we must transfer
the CRLF at the start of the EOB as this is considered to be part of the
message as per RFC-1939, sect. 3 */
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
k->keepon &= ~KEEP_RECV;
pop3c->eob = 0;
return result;
}
if(pop3c->eob)
/* While EOB is matching nothing should be output */
return CURLE_OK;
if(nread - last) {
result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
nread - last);
}
return result;
}
/* For command responses */
static CURLcode pop3_state_command_resp(struct Curl_easy *data,
int pop3code,
@ -1264,6 +1398,20 @@ static CURLcode pop3_pollset(struct Curl_easy *data,
return pop3c ? Curl_pp_pollset(data, &pop3c->pp, ps) : CURLE_OK;
}
/* SASL parameters for the pop3 protocol */
static const struct SASLproto saslpop3 = {
"pop", /* The service name */
pop3_perform_auth, /* Send authentication command */
pop3_continue_auth, /* Send authentication continuation */
pop3_cancel_auth, /* Send authentication cancellation */
pop3_get_message, /* Get SASL response message */
255 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
'*', /* Code received when continuation is expected */
'+', /* Code to receive upon authentication success */
SASL_AUTH_DEFAULT, /* Default mechanisms */
SASL_FLAG_BASE64 /* Configuration flags */
};
/***********************************************************************
*
* pop3_connect()
@ -1382,6 +1530,46 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
return result;
}
/* Call this when the DO phase has completed */
static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
{
(void)data;
(void)connected;
return CURLE_OK;
}
/***********************************************************************
*
* pop3_regular_transfer()
*
* The input argument is already checked for validity.
*
* Performs all commands done before a regular transfer between a local and a
* remote host.
*/
static CURLcode pop3_regular_transfer(struct Curl_easy *data,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
bool connected = FALSE;
/* Make sure size is unknown at this point */
data->req.size = -1;
/* Set the progress data */
Curl_pgrsReset(data);
/* Carry out the perform */
result = pop3_perform(data, &connected, dophase_done);
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
result = pop3_dophase_done(data, connected);
return result;
}
/***********************************************************************
*
* pop3_do()
@ -1446,15 +1634,6 @@ static CURLcode pop3_disconnect(struct Curl_easy *data,
return CURLE_OK;
}
/* Call this when the DO phase has completed */
static CURLcode pop3_dophase_done(struct Curl_easy *data, bool connected)
{
(void)data;
(void)connected;
return CURLE_OK;
}
/* Called from multi.c while DOing */
static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
{
@ -1471,37 +1650,6 @@ static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
return result;
}
/***********************************************************************
*
* pop3_regular_transfer()
*
* The input argument is already checked for validity.
*
* Performs all commands done before a regular transfer between a local and a
* remote host.
*/
static CURLcode pop3_regular_transfer(struct Curl_easy *data,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
bool connected = FALSE;
/* Make sure size is unknown at this point */
data->req.size = -1;
/* Set the progress data */
Curl_pgrsReset(data);
/* Carry out the perform */
result = pop3_perform(data, &connected, dophase_done);
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
result = pop3_dophase_done(data, connected);
return result;
}
static void pop3_easy_dtor(void *key, size_t klen, void *entry)
{
struct POP3 *pop3 = entry;
@ -1542,243 +1690,64 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data,
return CURLE_OK;
}
/***********************************************************************
*
* pop3_parse_url_options()
*
* Parse the URL login options.
/*
* POP3 protocol handler.
*/
static CURLcode pop3_parse_url_options(struct connectdata *conn)
{
struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
const char *ptr = conn->options;
const struct Curl_handler Curl_handler_pop3 = {
"pop3", /* scheme */
pop3_setup_connection, /* setup_connection */
pop3_do, /* do_it */
pop3_done, /* done */
ZERO_NULL, /* do_more */
pop3_connect, /* connect_it */
pop3_multi_statemach, /* connecting */
pop3_doing, /* doing */
pop3_pollset, /* proto_pollset */
pop3_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
pop3_disconnect, /* disconnect */
pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_POP3, /* defport */
CURLPROTO_POP3, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE
};
if(!pop3c)
return CURLE_FAILED_INIT;
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
while(*ptr && *ptr != '=')
ptr++;
value = ptr + 1;
while(*ptr && *ptr != ';')
ptr++;
if(curl_strnequal(key, "AUTH=", 5)) {
result = Curl_sasl_parse_url_auth_option(&pop3c->sasl,
value, ptr - value);
if(result && curl_strnequal(value, "+APOP", ptr - value)) {
pop3c->preftype = POP3_TYPE_APOP;
pop3c->sasl.prefmech = SASL_AUTH_NONE;
result = CURLE_OK;
}
}
else
result = CURLE_URL_MALFORMAT;
if(*ptr == ';')
ptr++;
}
if(pop3c->preftype != POP3_TYPE_APOP)
switch(pop3c->sasl.prefmech) {
case SASL_AUTH_NONE:
pop3c->preftype = POP3_TYPE_NONE;
break;
case SASL_AUTH_DEFAULT:
pop3c->preftype = POP3_TYPE_ANY;
break;
default:
pop3c->preftype = POP3_TYPE_SASL;
break;
}
return result;
}
/***********************************************************************
*
* pop3_parse_url_path()
*
* Parse the URL path into separate path components.
#ifdef USE_SSL
/*
* POP3S protocol handler.
*/
static CURLcode pop3_parse_url_path(struct Curl_easy *data)
{
/* The POP3 struct is already initialised in pop3_connect() */
struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *path = &data->state.up.path[1]; /* skip leading path */
if(!pop3)
return CURLE_FAILED_INIT;
/* URL decode the path for the message ID */
return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
}
/***********************************************************************
*
* pop3_parse_custom_request()
*
* Parse the custom request.
*/
static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
if(!pop3)
return CURLE_FAILED_INIT;
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
return result;
}
/***********************************************************************
*
* pop3_write()
*
* This function scans the body after the end-of-body and writes everything
* until the end is found.
*/
static CURLcode pop3_write(struct Curl_easy *data, const char *str,
size_t nread, bool is_eos)
{
/* This code could be made into a special function in the handler struct */
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
struct connectdata *conn = data->conn;
struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
bool strip_dot = FALSE;
size_t last = 0;
size_t i;
(void)is_eos;
if(!pop3c)
return CURLE_FAILED_INIT;
/* Search through the buffer looking for the end-of-body marker which is
5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
the eob so the server will have prefixed it with an extra dot which we
need to strip out. Additionally the marker could of course be spread out
over 5 different data chunks. */
for(i = 0; i < nread; i++) {
size_t prev = pop3c->eob;
switch(str[i]) {
case 0x0d:
if(pop3c->eob == 0) {
pop3c->eob++;
if(i) {
/* Write out the body part that did not match */
result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
i - last);
if(result)
return result;
last = i;
}
}
else if(pop3c->eob == 3)
pop3c->eob++;
else
/* If the character match was not at position 0 or 3 then restart the
pattern matching */
pop3c->eob = 1;
break;
case 0x0a:
if(pop3c->eob == 1 || pop3c->eob == 4)
pop3c->eob++;
else
/* If the character match was not at position 1 or 4 then start the
search again */
pop3c->eob = 0;
break;
case 0x2e:
if(pop3c->eob == 2)
pop3c->eob++;
else if(pop3c->eob == 3) {
/* We have an extra dot after the CRLF which we need to strip off */
strip_dot = TRUE;
pop3c->eob = 0;
}
else
/* If the character match was not at position 2 then start the search
again */
pop3c->eob = 0;
break;
default:
pop3c->eob = 0;
break;
}
/* Did we have a partial match which has subsequently failed? */
if(prev && prev >= pop3c->eob) {
/* Strip can only be non-zero for the first mismatch after CRLF and
then both prev and strip are equal and nothing will be output below */
while(prev && pop3c->strip) {
prev--;
pop3c->strip--;
}
if(prev) {
/* If the partial match was the CRLF and dot then only write the CRLF
as the server would have inserted the dot */
if(strip_dot && prev - 1 > 0) {
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev - 1);
}
else if(!strip_dot) {
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev);
}
else {
result = CURLE_OK;
}
if(result)
return result;
last = i;
strip_dot = FALSE;
}
}
}
if(pop3c->eob == POP3_EOB_LEN) {
/* We have a full match so the transfer is done, however we must transfer
the CRLF at the start of the EOB as this is considered to be part of the
message as per RFC-1939, sect. 3 */
result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
k->keepon &= ~KEEP_RECV;
pop3c->eob = 0;
return result;
}
if(pop3c->eob)
/* While EOB is matching nothing should be output */
return CURLE_OK;
if(nread - last) {
result = Curl_client_write(data, CLIENTWRITE_BODY, &str[last],
nread - last);
}
return result;
}
const struct Curl_handler Curl_handler_pop3s = {
"pop3s", /* scheme */
pop3_setup_connection, /* setup_connection */
pop3_do, /* do_it */
pop3_done, /* done */
ZERO_NULL, /* do_more */
pop3_connect, /* connect_it */
pop3_multi_statemach, /* connecting */
pop3_doing, /* doing */
pop3_pollset, /* proto_pollset */
pop3_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
pop3_disconnect, /* disconnect */
pop3_write, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_POP3S, /* defport */
CURLPROTO_POP3S, /* protocol */
CURLPROTO_POP3, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE
};
#endif
#endif /* CURL_DISABLE_POP3 */

View File

@ -136,116 +136,344 @@ struct SMTP {
BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */
};
/* Local API functions */
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
struct smtp_conn *smtpc,
struct SMTP *smtp,
bool *done);
static CURLcode smtp_do(struct Curl_easy *data, bool *done);
static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
bool premature);
static CURLcode smtp_connect(struct Curl_easy *data, bool *done);
static CURLcode smtp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead);
static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done);
static CURLcode smtp_pollset(struct Curl_easy *data,
struct easy_pollset *ps);
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
static CURLcode smtp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
/***********************************************************************
*
* smtp_parse_url_options()
*
* Parse the URL login options.
*/
static CURLcode smtp_parse_url_options(struct connectdata *conn,
struct smtp_conn *smtpc);
struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
const char *ptr = conn->options;
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
while(*ptr && *ptr != '=')
ptr++;
value = ptr + 1;
while(*ptr && *ptr != ';')
ptr++;
if(curl_strnequal(key, "AUTH=", 5))
result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
value, ptr - value);
else
result = CURLE_URL_MALFORMAT;
if(*ptr == ';')
ptr++;
}
return result;
}
/***********************************************************************
*
* smtp_parse_url_path()
*
* Parse the URL path into separate path components.
*/
static CURLcode smtp_parse_url_path(struct Curl_easy *data,
struct smtp_conn *smtpc);
struct smtp_conn *smtpc)
{
/* The SMTP struct is already initialised in smtp_connect() */
const char *path = &data->state.up.path[1]; /* skip leading path */
char localhost[HOSTNAME_MAX + 1];
/* Calculate the path if necessary */
if(!*path) {
if(!Curl_gethostname(localhost, sizeof(localhost)))
path = localhost;
else
path = "localhost";
}
/* URL decode the path and use it as the domain in our EHLO */
return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
}
/***********************************************************************
*
* smtp_parse_custom_request()
*
* Parse the custom request.
*/
static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
struct SMTP *smtp);
static CURLcode smtp_parse_address(const char *fqma,
char **address, struct hostname *host,
const char **suffix);
static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
const struct bufref *initresp);
static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp);
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
static CURLcode cr_eob_add(struct Curl_easy *data);
struct SMTP *smtp)
{
CURLcode result = CURLE_OK;
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
/*
* SMTP protocol handler.
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
return result;
}
/***********************************************************************
*
* smtp_parse_address()
*
* Parse the fully qualified mailbox address into a local address part and the
* hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
* necessary.
*
* Parameters:
*
* conn [in] - The connection handle.
* fqma [in] - The fully qualified mailbox address (which may or
* may not contain UTF-8 characters).
* address [in/out] - A new allocated buffer which holds the local
* address part of the mailbox. This buffer must be
* free'ed by the caller.
* host [in/out] - The hostname structure that holds the original,
* and optionally encoded, hostname.
* Curl_free_idnconverted_hostname() must be called
* once the caller has finished with the structure.
*
* Returns CURLE_OK on success.
*
* Notes:
*
* Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
* that conversion then we shall return success. This allow the caller to send
* the data to the server as a U-label (as per RFC-6531 sect. 3.2).
*
* If an mailbox '@' separator cannot be located then the mailbox is considered
* to be either a local mailbox or an invalid mailbox (depending on what the
* calling function deems it to be) then the input will simply be returned in
* the address part with the hostname being NULL.
*/
static CURLcode smtp_parse_address(const char *fqma, char **address,
struct hostname *host, const char **suffix)
{
CURLcode result = CURLE_OK;
size_t length;
char *addressend;
const struct Curl_handler Curl_handler_smtp = {
"smtp", /* scheme */
smtp_setup_connection, /* setup_connection */
smtp_do, /* do_it */
smtp_done, /* done */
ZERO_NULL, /* do_more */
smtp_connect, /* connect_it */
smtp_multi_statemach, /* connecting */
smtp_doing, /* doing */
smtp_pollset, /* proto_pollset */
smtp_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_SMTP, /* defport */
CURLPROTO_SMTP, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE
/* Duplicate the fully qualified email address so we can manipulate it,
ensuring it does not contain the delimiters if specified */
char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma);
if(!dup)
return CURLE_OUT_OF_MEMORY;
if(fqma[0] != '<') {
length = strlen(dup);
if(length) {
if(dup[length - 1] == '>')
dup[length - 1] = '\0';
}
}
else {
addressend = strrchr(dup, '>');
if(addressend) {
*addressend = '\0';
*suffix = addressend + 1;
}
}
/* Extract the hostname from the address (if we can) */
host->name = strpbrk(dup, "@");
if(host->name) {
*host->name = '\0';
host->name = host->name + 1;
/* Attempt to convert the hostname to IDN ACE */
(void)Curl_idnconvert_hostname(host);
/* If Curl_idnconvert_hostname() fails then we shall attempt to continue
and send the hostname using UTF-8 rather than as 7-bit ACE (which is
our preference) */
}
/* Extract the local address from the mailbox */
*address = dup;
return result;
}
struct cr_eob_ctx {
struct Curl_creader super;
struct bufq buf;
size_t n_eob; /* how many EOB bytes we matched so far */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
BIT(read_eos); /* we read an EOS from the next reader */
BIT(processed_eos); /* we read and processed an EOS */
BIT(eos); /* we have returned an EOS */
};
#ifdef USE_SSL
/*
* SMTPS protocol handler.
*/
static CURLcode cr_eob_init(struct Curl_easy *data,
struct Curl_creader *reader)
{
struct cr_eob_ctx *ctx = reader->ctx;
(void)data;
/* The first char we read is the first on a line, as if we had
* read CRLF just before */
ctx->n_eob = 2;
Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
return CURLE_OK;
}
const struct Curl_handler Curl_handler_smtps = {
"smtps", /* scheme */
smtp_setup_connection, /* setup_connection */
smtp_do, /* do_it */
smtp_done, /* done */
ZERO_NULL, /* do_more */
smtp_connect, /* connect_it */
smtp_multi_statemach, /* connecting */
smtp_doing, /* doing */
smtp_pollset, /* proto_pollset */
smtp_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_SMTPS, /* defport */
CURLPROTO_SMTPS, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE
};
#endif
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
{
struct cr_eob_ctx *ctx = reader->ctx;
(void)data;
Curl_bufq_free(&ctx->buf);
}
/* SASL parameters for the smtp protocol */
static const struct SASLproto saslsmtp = {
"smtp", /* The service name */
smtp_perform_auth, /* Send authentication command */
smtp_continue_auth, /* Send authentication continuation */
smtp_cancel_auth, /* Cancel authentication */
smtp_get_message, /* Get SASL response message */
512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
334, /* Code received when continuation is expected */
235, /* Code to receive upon authentication success */
SASL_AUTH_DEFAULT, /* Default mechanisms */
SASL_FLAG_BASE64 /* Configuration flags */
/* this is the 5-bytes End-Of-Body marker for SMTP */
#define SMTP_EOB "\r\n.\r\n"
#define SMTP_EOB_FIND_LEN 3
/* client reader doing SMTP End-Of-Body escaping. */
static CURLcode cr_eob_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen,
size_t *pnread, bool *peos)
{
struct cr_eob_ctx *ctx = reader->ctx;
CURLcode result = CURLE_OK;
size_t nread, i, start, n;
bool eos;
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* Get more and convert it when needed */
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
blen, result, nread, eos);
if(result)
return result;
ctx->read_eos = eos;
if(nread) {
if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
/* not in the middle of a match, no EOB start found, just pass */
*pnread = nread;
*peos = FALSE;
return CURLE_OK;
}
/* scan for EOB (continuation) and convert */
for(i = start = 0; i < nread; ++i) {
if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
/* matched the EOB prefix and seeing additional char, add '.' */
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
if(result)
return result;
result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
if(result)
return result;
ctx->n_eob = 0;
start = i;
if(data->state.infilesize > 0)
data->state.infilesize++;
}
if(buf[i] != SMTP_EOB[ctx->n_eob])
ctx->n_eob = 0;
if(buf[i] == SMTP_EOB[ctx->n_eob]) {
/* matching another char of the EOB */
++ctx->n_eob;
}
}
/* add any remainder to buf */
if(start < nread) {
result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
if(result)
return result;
}
}
}
*peos = FALSE;
if(ctx->read_eos && !ctx->processed_eos) {
/* if we last matched a CRLF or if the data was empty, add ".\r\n"
* to end the body. If we sent something and it did not end with "\r\n",
* add "\r\n.\r\n" to end the body */
const char *eob = SMTP_EOB;
CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
switch(ctx->n_eob) {
case 2:
/* seen a CRLF at the end, just add the remainder */
eob = &SMTP_EOB[2];
break;
case 3:
/* ended with '\r\n.', we should escape the last '.' */
eob = "." SMTP_EOB;
break;
default:
break;
}
result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
if(result)
return result;
ctx->processed_eos = TRUE;
}
if(!Curl_bufq_is_empty(&ctx->buf)) {
result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
}
else
*pnread = 0;
if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* no more data, read all, done. */
CURL_TRC_SMTP(data, "mail body complete, returning EOS");
ctx->eos = TRUE;
}
*peos = ctx->eos;
DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
blen, result, *pnread, *peos));
return result;
}
static curl_off_t cr_eob_total_length(struct Curl_easy *data,
struct Curl_creader *reader)
{
/* this reader changes length depending on input */
(void)data;
(void)reader;
return -1;
}
static const struct Curl_crtype cr_eob = {
"cr-smtp-eob",
cr_eob_init,
cr_eob_read,
cr_eob_close,
Curl_creader_def_needs_rewind,
cr_eob_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_eob_ctx)
};
static CURLcode cr_eob_add(struct Curl_easy *data)
{
struct Curl_creader *reader = NULL;
CURLcode result;
result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE);
if(!result)
result = Curl_creader_add(data, reader);
if(result && reader)
Curl_creader_free(data, reader);
return result;
}
/***********************************************************************
*
* smtp_endofresp()
@ -1413,6 +1641,20 @@ static CURLcode smtp_pollset(struct Curl_easy *data,
return smtpc ? Curl_pp_pollset(data, &smtpc->pp, ps) : CURLE_OK;
}
/* SASL parameters for the smtp protocol */
static const struct SASLproto saslsmtp = {
"smtp", /* The service name */
smtp_perform_auth, /* Send authentication command */
smtp_continue_auth, /* Send authentication continuation */
smtp_cancel_auth, /* Cancel authentication */
smtp_get_message, /* Get SASL response message */
512 - 8, /* Max line len - strlen("AUTH ") - 1 space - crlf */
334, /* Code received when continuation is expected */
235, /* Code to receive upon authentication success */
SASL_AUTH_DEFAULT, /* Default mechanisms */
SASL_FLAG_BASE64 /* Configuration flags */
};
/***********************************************************************
*
* smtp_connect()
@ -1568,6 +1810,55 @@ out:
return result;
}
/* Call this when the DO phase has completed */
static CURLcode smtp_dophase_done(struct Curl_easy *data,
struct SMTP *smtp,
bool connected)
{
(void)connected;
if(smtp->transfer != PPTRANSFER_BODY)
/* no data to transfer */
Curl_xfer_setup_nop(data);
return CURLE_OK;
}
/***********************************************************************
*
* smtp_regular_transfer()
*
* The input argument is already checked for validity.
*
* Performs all commands done before a regular transfer between a local and a
* remote host.
*/
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
struct smtp_conn *smtpc,
struct SMTP *smtp,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
bool connected = FALSE;
/* Make sure size is unknown at this point */
data->req.size = -1;
/* Set the progress data */
Curl_pgrsReset(data);
/* Carry out the perform */
result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
result = smtp_dophase_done(data, smtp, connected);
CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
result, *dophase_done);
return result;
}
/***********************************************************************
*
* smtp_do()
@ -1631,20 +1922,6 @@ static CURLcode smtp_disconnect(struct Curl_easy *data,
return CURLE_OK;
}
/* Call this when the DO phase has completed */
static CURLcode smtp_dophase_done(struct Curl_easy *data,
struct SMTP *smtp,
bool connected)
{
(void)connected;
if(smtp->transfer != PPTRANSFER_BODY)
/* no data to transfer */
Curl_xfer_setup_nop(data);
return CURLE_OK;
}
/* Called from multi.c while DOing */
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
{
@ -1666,41 +1943,6 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
return result;
}
/***********************************************************************
*
* smtp_regular_transfer()
*
* The input argument is already checked for validity.
*
* Performs all commands done before a regular transfer between a local and a
* remote host.
*/
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
struct smtp_conn *smtpc,
struct SMTP *smtp,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
bool connected = FALSE;
/* Make sure size is unknown at this point */
data->req.size = -1;
/* Set the progress data */
Curl_pgrsReset(data);
/* Carry out the perform */
result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
result = smtp_dophase_done(data, smtp, connected);
CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
result, *dophase_done);
return result;
}
static void smtp_easy_dtor(void *key, size_t klen, void *entry)
{
struct SMTP *smtp = entry;
@ -1743,342 +1985,64 @@ out:
return result;
}
/***********************************************************************
*
* smtp_parse_url_options()
*
* Parse the URL login options.
/*
* SMTP protocol handler.
*/
static CURLcode smtp_parse_url_options(struct connectdata *conn,
struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
const char *ptr = conn->options;
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
while(*ptr && *ptr != '=')
ptr++;
value = ptr + 1;
while(*ptr && *ptr != ';')
ptr++;
if(curl_strnequal(key, "AUTH=", 5))
result = Curl_sasl_parse_url_auth_option(&smtpc->sasl,
value, ptr - value);
else
result = CURLE_URL_MALFORMAT;
if(*ptr == ';')
ptr++;
}
return result;
}
/***********************************************************************
*
* smtp_parse_url_path()
*
* Parse the URL path into separate path components.
*/
static CURLcode smtp_parse_url_path(struct Curl_easy *data,
struct smtp_conn *smtpc)
{
/* The SMTP struct is already initialised in smtp_connect() */
const char *path = &data->state.up.path[1]; /* skip leading path */
char localhost[HOSTNAME_MAX + 1];
/* Calculate the path if necessary */
if(!*path) {
if(!Curl_gethostname(localhost, sizeof(localhost)))
path = localhost;
else
path = "localhost";
}
/* URL decode the path and use it as the domain in our EHLO */
return Curl_urldecode(path, 0, &smtpc->domain, NULL, REJECT_CTRL);
}
/***********************************************************************
*
* smtp_parse_custom_request()
*
* Parse the custom request.
*/
static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
struct SMTP *smtp)
{
CURLcode result = CURLE_OK;
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(custom, 0, &smtp->custom, NULL, REJECT_CTRL);
return result;
}
/***********************************************************************
*
* smtp_parse_address()
*
* Parse the fully qualified mailbox address into a local address part and the
* hostname, converting the hostname to an IDN A-label, as per RFC-5890, if
* necessary.
*
* Parameters:
*
* conn [in] - The connection handle.
* fqma [in] - The fully qualified mailbox address (which may or
* may not contain UTF-8 characters).
* address [in/out] - A new allocated buffer which holds the local
* address part of the mailbox. This buffer must be
* free'ed by the caller.
* host [in/out] - The hostname structure that holds the original,
* and optionally encoded, hostname.
* Curl_free_idnconverted_hostname() must be called
* once the caller has finished with the structure.
*
* Returns CURLE_OK on success.
*
* Notes:
*
* Should a UTF-8 hostname require conversion to IDN ACE and we cannot honor
* that conversion then we shall return success. This allow the caller to send
* the data to the server as a U-label (as per RFC-6531 sect. 3.2).
*
* If an mailbox '@' separator cannot be located then the mailbox is considered
* to be either a local mailbox or an invalid mailbox (depending on what the
* calling function deems it to be) then the input will simply be returned in
* the address part with the hostname being NULL.
*/
static CURLcode smtp_parse_address(const char *fqma, char **address,
struct hostname *host, const char **suffix)
{
CURLcode result = CURLE_OK;
size_t length;
char *addressend;
/* Duplicate the fully qualified email address so we can manipulate it,
ensuring it does not contain the delimiters if specified */
char *dup = curlx_strdup(fqma[0] == '<' ? fqma + 1 : fqma);
if(!dup)
return CURLE_OUT_OF_MEMORY;
if(fqma[0] != '<') {
length = strlen(dup);
if(length) {
if(dup[length - 1] == '>')
dup[length - 1] = '\0';
}
}
else {
addressend = strrchr(dup, '>');
if(addressend) {
*addressend = '\0';
*suffix = addressend + 1;
}
}
/* Extract the hostname from the address (if we can) */
host->name = strpbrk(dup, "@");
if(host->name) {
*host->name = '\0';
host->name = host->name + 1;
/* Attempt to convert the hostname to IDN ACE */
(void)Curl_idnconvert_hostname(host);
/* If Curl_idnconvert_hostname() fails then we shall attempt to continue
and send the hostname using UTF-8 rather than as 7-bit ACE (which is
our preference) */
}
/* Extract the local address from the mailbox */
*address = dup;
return result;
}
struct cr_eob_ctx {
struct Curl_creader super;
struct bufq buf;
size_t n_eob; /* how many EOB bytes we matched so far */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
BIT(read_eos); /* we read an EOS from the next reader */
BIT(processed_eos); /* we read and processed an EOS */
BIT(eos); /* we have returned an EOS */
const struct Curl_handler Curl_handler_smtp = {
"smtp", /* scheme */
smtp_setup_connection, /* setup_connection */
smtp_do, /* do_it */
smtp_done, /* done */
ZERO_NULL, /* do_more */
smtp_connect, /* connect_it */
smtp_multi_statemach, /* connecting */
smtp_doing, /* doing */
smtp_pollset, /* proto_pollset */
smtp_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_SMTP, /* defport */
CURLPROTO_SMTP, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */
PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE
};
static CURLcode cr_eob_init(struct Curl_easy *data,
struct Curl_creader *reader)
{
struct cr_eob_ctx *ctx = reader->ctx;
(void)data;
/* The first char we read is the first on a line, as if we had
* read CRLF just before */
ctx->n_eob = 2;
Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
return CURLE_OK;
}
static void cr_eob_close(struct Curl_easy *data, struct Curl_creader *reader)
{
struct cr_eob_ctx *ctx = reader->ctx;
(void)data;
Curl_bufq_free(&ctx->buf);
}
/* this is the 5-bytes End-Of-Body marker for SMTP */
#define SMTP_EOB "\r\n.\r\n"
#define SMTP_EOB_FIND_LEN 3
/* client reader doing SMTP End-Of-Body escaping. */
static CURLcode cr_eob_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen,
size_t *pnread, bool *peos)
{
struct cr_eob_ctx *ctx = reader->ctx;
CURLcode result = CURLE_OK;
size_t nread, i, start, n;
bool eos;
if(!ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* Get more and convert it when needed */
result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
CURL_TRC_SMTP(data, "cr_eob_read, next_read(len=%zu) -> %d, %zu eos=%d",
blen, result, nread, eos);
if(result)
return result;
ctx->read_eos = eos;
if(nread) {
if(!ctx->n_eob && !memchr(buf, SMTP_EOB[0], nread)) {
/* not in the middle of a match, no EOB start found, just pass */
*pnread = nread;
*peos = FALSE;
return CURLE_OK;
}
/* scan for EOB (continuation) and convert */
for(i = start = 0; i < nread; ++i) {
if(ctx->n_eob >= SMTP_EOB_FIND_LEN) {
/* matched the EOB prefix and seeing additional char, add '.' */
result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
if(result)
return result;
result = Curl_bufq_cwrite(&ctx->buf, ".", 1, &n);
if(result)
return result;
ctx->n_eob = 0;
start = i;
if(data->state.infilesize > 0)
data->state.infilesize++;
}
if(buf[i] != SMTP_EOB[ctx->n_eob])
ctx->n_eob = 0;
if(buf[i] == SMTP_EOB[ctx->n_eob]) {
/* matching another char of the EOB */
++ctx->n_eob;
}
}
/* add any remainder to buf */
if(start < nread) {
result = Curl_bufq_cwrite(&ctx->buf, buf + start, nread - start, &n);
if(result)
return result;
}
}
}
*peos = FALSE;
if(ctx->read_eos && !ctx->processed_eos) {
/* if we last matched a CRLF or if the data was empty, add ".\r\n"
* to end the body. If we sent something and it did not end with "\r\n",
* add "\r\n.\r\n" to end the body */
const char *eob = SMTP_EOB;
CURL_TRC_SMTP(data, "auto-ending mail body with '\\r\\n.\\r\\n'");
switch(ctx->n_eob) {
case 2:
/* seen a CRLF at the end, just add the remainder */
eob = &SMTP_EOB[2];
break;
case 3:
/* ended with '\r\n.', we should escape the last '.' */
eob = "." SMTP_EOB;
break;
default:
break;
}
result = Curl_bufq_cwrite(&ctx->buf, eob, strlen(eob), &n);
if(result)
return result;
ctx->processed_eos = TRUE;
}
if(!Curl_bufq_is_empty(&ctx->buf)) {
result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
}
else
*pnread = 0;
if(ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
/* no more data, read all, done. */
CURL_TRC_SMTP(data, "mail body complete, returning EOS");
ctx->eos = TRUE;
}
*peos = ctx->eos;
DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d",
blen, result, *pnread, *peos));
return result;
}
static curl_off_t cr_eob_total_length(struct Curl_easy *data,
struct Curl_creader *reader)
{
/* this reader changes length depending on input */
(void)data;
(void)reader;
return -1;
}
static const struct Curl_crtype cr_eob = {
"cr-smtp-eob",
cr_eob_init,
cr_eob_read,
cr_eob_close,
Curl_creader_def_needs_rewind,
cr_eob_total_length,
Curl_creader_def_resume_from,
Curl_creader_def_cntrl,
Curl_creader_def_is_paused,
Curl_creader_def_done,
sizeof(struct cr_eob_ctx)
#ifdef USE_SSL
/*
* SMTPS protocol handler.
*/
const struct Curl_handler Curl_handler_smtps = {
"smtps", /* scheme */
smtp_setup_connection, /* setup_connection */
smtp_do, /* do_it */
smtp_done, /* done */
ZERO_NULL, /* do_more */
smtp_connect, /* connect_it */
smtp_multi_statemach, /* connecting */
smtp_doing, /* doing */
smtp_pollset, /* proto_pollset */
smtp_pollset, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
smtp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_SMTPS, /* defport */
CURLPROTO_SMTPS, /* protocol */
CURLPROTO_SMTP, /* family */
PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE
};
static CURLcode cr_eob_add(struct Curl_easy *data)
{
struct Curl_creader *reader = NULL;
CURLcode result;
result = Curl_creader_create(&reader, data, &cr_eob, CURL_CR_CONTENT_ENCODE);
if(!result)
result = Curl_creader_add(data, reader);
if(result && reader)
Curl_creader_free(data, reader);
return result;
}
#endif
#endif /* CURL_DISABLE_SMTP */