diff --git a/CMakeLists.txt b/CMakeLists.txt index a30ded9d3..15a0f9765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,11 +346,6 @@ message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV") set(LWS_WITH_LIBUV 1) endif() -if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV) -message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV") - set(LWS_WITH_LIBUV 1) -endif() - if (LWS_WITH_PLUGINS OR LWS_WITH_CGI) # sshd plugin set(LWS_WITH_GENCRYPTO 1) @@ -361,11 +356,6 @@ if (LWS_WITH_GENERIC_SESSIONS) set(LWS_WITH_SMTP 1) endif() -if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV) -message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV") - set(LWS_WITH_LIBUV 1) -endif() - if (LWS_WITH_ESP32) set(LWS_WITH_SHARED OFF) set(LWS_WITH_MBEDTLS ON) @@ -929,6 +919,7 @@ if (LWS_WITH_NETWORK) lib/core-net/wsi-timeout.c lib/core-net/adopt.c lib/roles/pipe/ops-pipe.c + lib/abstract/abstract.c ) if (LWS_WITH_STATS) @@ -982,6 +973,7 @@ endif() if (LWS_ROLE_RAW) list(APPEND SOURCES + lib/abstract/transports/raw-skt.c lib/roles/raw-skt/ops-raw-skt.c lib/roles/raw-file/ops-raw-file.c) endif() @@ -1295,7 +1287,7 @@ endif() if (LWS_WITH_SMTP) list(APPEND SOURCES - lib/misc/smtp.c) + lib/abstract/smtp/smtp.c) endif() if (LWS_WITH_RANGES) diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 31948abae..e977afc4a 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -529,6 +529,9 @@ struct lws; #include #include +#include +#include + #if defined(LWS_WITH_TLS) #if defined(LWS_WITH_MBEDTLS) diff --git a/include/libwebsockets/abstract/smtp.h b/include/libwebsockets/abstract/smtp.h new file mode 100644 index 000000000..85b912731 --- /dev/null +++ b/include/libwebsockets/abstract/smtp.h @@ -0,0 +1,164 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * included from libwebsockets.h + */ + +/** \defgroup smtp SMTP related functions + * ##SMTP related functions + * \ingroup lwsapi + * + * These apis let you communicate with a local SMTP server to send email from + * lws. It handles all the SMTP sequencing and protocol actions. + * + * Your system should have postfix, sendmail or another MTA listening on port + * 25 and able to send email using the "mail" commandline app. Usually distro + * MTAs are configured for this by default. + * + * It runs via its own libuv events if initialized (which requires giving it + * a libuv loop to attach to). + * + * It operates using three callbacks, on_next() queries if there is a new email + * to send, on_get_body() asks for the body of the email, and on_sent() is + * called after the email is successfully sent. + * + * To use it + * + * - create an lws_email struct + * + * - initialize data, loop, the email_* strings, max_content_size and + * the callbacks + * + * - call lws_email_init() + * + * When you have at least one email to send, call lws_email_check() to + * schedule starting to send it. + */ +//@{ +#if defined(LWS_WITH_SMTP) + +typedef struct lws_smtp_client lws_smtp_client_t; +typedef struct lws_abstract lws_abstract_t; + +typedef struct lws_smtp_client_info { + void *data; + + char ip[32]; /**< Fill before init, eg, "127.0.0.1" */ + char helo[32]; /**< Fill before init, eg, "myserver.com" */ + + const lws_abstract_t *abs; /**< abstract transport to use */ + struct lws_vhost *vh; + + time_t retry_interval; + time_t delivery_timeout; + + size_t email_queue_max; + size_t max_content_size; +} lws_smtp_client_info_t; + +typedef struct lws_smtp_email { + struct lws_dll2 list; + + void *data; + void *extra; + + time_t added; + time_t last_try; + + const char *email_from; + const char *email_to; + const char *payload; + + int (*done)(struct lws_smtp_email *e, void *buf, size_t len); + + int tries; +} lws_smtp_email_t; + + +/** + * lws_smtp_client_create() - Initialize a struct lws_email + * + * \param abs: abstract transport to use with the new SMTP client + * \param ci: parameters describing the new SMTP client characteristics + * + * Prepares a struct lws_email for use ending SMTP + */ +LWS_VISIBLE LWS_EXTERN lws_smtp_client_t * +lws_smtp_client_create(const lws_smtp_client_info_t *ci); + +/** + * lws_smtp_client_alloc_email_helper() - Allocates and inits an email object + * + * \param payload: the email payload string, with headers and terminating . + * \param payload_len: size in bytes of the payload string + * \param sender: the sender name and email + * \param recipient: the recipient name and email + * + * Allocates an email object and copies the payload, sender and recipient into + * it and initializes it. Returns NULL if OOM, otherwise the allocated email + * object. + * + * Because it copies the arguments into an allocated buffer, the original + * arguments can be safely destroyed after calling this. + * + * The done() callback must free the email object. It doesn't have to free any + * individual members. + */ +LWS_VISIBLE LWS_EXTERN lws_smtp_email_t * +lws_smtp_client_alloc_email_helper(const char *payload, size_t payload_len, + const char *sender, const char *recipient, + const char *extra, size_t extra_len, void *data, + int (*done)(struct lws_smtp_email *e, + void *buf, size_t len)); + +/** + * lws_smtp_client_add_email() - Add email to the list of ones being sent + * + * \param c: smtp client + * \param e: email to queue for sending on \p c + * + * Adds an email to the linked-list of emails to send + */ +LWS_VISIBLE LWS_EXTERN int +lws_smtp_client_add_email(lws_smtp_client_t *c, lws_smtp_email_t *e); + +/** + * lws_smtp_client_kick() - Request check for new email + * + * \param email: lws_smtp_client_t context to kick + * + * Gives smtp client a chance to move things on + */ +LWS_VISIBLE LWS_EXTERN void +lws_smtp_client_kick(lws_smtp_client_t *email); + +/** + * lws_smtp_client_destroy() - stop using the struct lws_email + * + * \param email: the lws_smtp_client_t context + * + * Stop sending email using email and free allocations + */ +LWS_VISIBLE LWS_EXTERN void +lws_smtp_client_destroy(lws_smtp_client_t **email); + + +#endif +//@} diff --git a/include/libwebsockets/abstract/transports.h b/include/libwebsockets/abstract/transports.h new file mode 100644 index 000000000..637521f2a --- /dev/null +++ b/include/libwebsockets/abstract/transports.h @@ -0,0 +1,31 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * included from libwebsockets.h + */ + +struct lws_abstract; +typedef struct lws_abstract lws_abstract_t; + +LWS_VISIBLE LWS_EXTERN void +lws_abstract_copy(lws_abstract_t *dest, const lws_abstract_t *src); + +LWS_VISIBLE LWS_EXTERN const lws_abstract_t * +lws_abstract_get_by_name(const char *name); diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h index 4dc20a8a1..79874046c 100644 --- a/include/libwebsockets/lws-callbacks.h +++ b/include/libwebsockets/lws-callbacks.h @@ -734,6 +734,9 @@ enum lws_callback_reasons { LWS_CALLBACK_RAW_ADOPT = 62, /**< RAW mode connection was adopted (equivalent to 'wsi created') */ + LWS_CALLBACK_RAW_CONNECTED = 101, + /**< outgoing client RAW mode connection was connected */ + LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL = 81, LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL = 82, diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index 971bfa042..77328f0d2 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -886,128 +886,4 @@ lws_get_ssl(struct lws *wsi); LWS_VISIBLE LWS_EXTERN void lws_explicit_bzero(void *p, size_t len); -/** \defgroup smtp SMTP related functions - * ##SMTP related functions - * \ingroup lwsapi - * - * These apis let you communicate with a local SMTP server to send email from - * lws. It handles all the SMTP sequencing and protocol actions. - * - * Your system should have postfix, sendmail or another MTA listening on port - * 25 and able to send email using the "mail" commandline app. Usually distro - * MTAs are configured for this by default. - * - * It runs via its own libuv events if initialized (which requires giving it - * a libuv loop to attach to). - * - * It operates using three callbacks, on_next() queries if there is a new email - * to send, on_get_body() asks for the body of the email, and on_sent() is - * called after the email is successfully sent. - * - * To use it - * - * - create an lws_email struct - * - * - initialize data, loop, the email_* strings, max_content_size and - * the callbacks - * - * - call lws_email_init() - * - * When you have at least one email to send, call lws_email_check() to - * schedule starting to send it. - */ -//@{ -#ifdef LWS_WITH_SMTP - -/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ -enum lwsgs_smtp_states { - LGSSMTP_IDLE, /**< awaiting new email */ - LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ - LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ - LGSSMTP_SENT_HELO, /**< sent the HELO */ - LGSSMTP_SENT_FROM, /**< sent FROM */ - LGSSMTP_SENT_TO, /**< sent TO */ - LGSSMTP_SENT_DATA, /**< sent DATA request */ - LGSSMTP_SENT_BODY, /**< sent the email body */ - LGSSMTP_SENT_QUIT, /**< sent the session quit */ -}; - -/** struct lws_email - abstract context for performing SMTP operations */ -struct lws_email { - void *data; - /**< opaque pointer set by user code and available to the callbacks */ - uv_loop_t *loop; - /**< the libuv loop we will work on */ - - char email_smtp_ip[32]; /**< Fill before init, eg, "127.0.0.1" */ - char email_helo[32]; /**< Fill before init, eg, "myserver.com" */ - char email_from[100]; /**< Fill before init or on_next */ - char email_to[100]; /**< Fill before init or on_next */ - - unsigned int max_content_size; - /**< largest possible email body size */ - - /* Fill all the callbacks before init */ - - int (*on_next)(struct lws_email *email); - /**< (Fill in before calling lws_email_init) - * called when idle, 0 = another email to send, nonzero is idle. - * If you return 0, all of the email_* char arrays must be set - * to something useful. */ - int (*on_sent)(struct lws_email *email); - /**< (Fill in before calling lws_email_init) - * called when transfer of the email to the SMTP server was - * successful, your callback would remove the current email - * from its queue */ - int (*on_get_body)(struct lws_email *email, char *buf, int len); - /**< (Fill in before calling lws_email_init) - * called when the body part of the queued email is about to be - * sent to the SMTP server. */ - - - /* private things */ - uv_timer_t timeout_email; /**< private */ - enum lwsgs_smtp_states estate; /**< private */ - uv_connect_t email_connect_req; /**< private */ - uv_tcp_t email_client; /**< private */ - time_t email_connect_started; /**< private */ - char email_buf[256]; /**< private */ - char *content; /**< private */ -}; - -/** - * lws_email_init() - Initialize a struct lws_email - * - * \param email: struct lws_email to init - * \param loop: libuv loop to use - * \param max_content: max email content size - * - * Prepares a struct lws_email for use ending SMTP - */ -LWS_VISIBLE LWS_EXTERN int -lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content); - -/** - * lws_email_check() - Request check for new email - * - * \param email: struct lws_email context to check - * - * Schedules a check for new emails in 1s... call this when you have queued an - * email for send. - */ -LWS_VISIBLE LWS_EXTERN void -lws_email_check(struct lws_email *email); -/** - * lws_email_destroy() - stop using the struct lws_email - * - * \param email: the struct lws_email context - * - * Stop sending email using email and free allocations - */ -LWS_VISIBLE LWS_EXTERN void -lws_email_destroy(struct lws_email *email); - -#endif -//@} - ///@} diff --git a/lib/abstract/abstract.c b/lib/abstract/abstract.c new file mode 100644 index 000000000..2a602cf7b --- /dev/null +++ b/lib/abstract/abstract.c @@ -0,0 +1,52 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include + +extern lws_abstract_t lws_abstract_transport_cli_raw_skt; + +static const lws_abstract_t *available_abstractions[] = { + &lws_abstract_transport_cli_raw_skt, +}; + +/* + * the definition is opaque, so a helper to copy it into place + */ + +void +lws_abstract_copy(lws_abstract_t *dest, const lws_abstract_t *src) +{ + memcpy(dest, src, sizeof(*dest)); +} + + +const lws_abstract_t * +lws_abstract_get_by_name(const char *name) +{ + int n; + + for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abstractions); n++) + if (!strcmp(name, available_abstractions[n]->name)) + return available_abstractions[n]; + + return NULL; +} diff --git a/lib/abstract/private.h b/lib/abstract/private.h new file mode 100644 index 000000000..4962268d0 --- /dev/null +++ b/lib/abstract/private.h @@ -0,0 +1,68 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +struct lws_abstract; + +typedef void lws_abs_user_t; +typedef void lws_abs_t; + +/* + * The abstract callbacks are in three parts + * + * - create and destroy + * + * - events handled by the transport + * + * - events handled by the user of the transport + * + * the canned abstract transports only define the first two types... the + * remaining callbacks must be filled in to callback functions specific to + * the user of the abstract transport. + */ + +typedef struct lws_abstract { + + const char *name; + + lws_abs_user_t * (*create)(struct lws_abstract *abs, void *user); + void (*destroy)(lws_abs_user_t **d); + + /* events the abstract object invokes (filled in by transport) */ + + int (*tx)(lws_abs_user_t *d, uint8_t *buf, size_t len); + int (*client_conn)(lws_abs_user_t *d, struct lws_vhost *vh, + const char *ip, uint16_t port, int tls_flags); + int (*close)(lws_abs_user_t *d); + int (*ask_for_writeable)(lws_abs_user_t *d); + int (*set_timeout)(lws_abs_user_t *d, int reason, int secs); + int (*state)(lws_abs_user_t *d); + + /* events the transport invokes (filled in by abstract object) */ + + int (*accept)(lws_abs_user_t *d); + int (*rx)(lws_abs_user_t *d, uint8_t *buf, size_t len); + int (*writeable)(lws_abs_user_t *d, size_t budget); + int (*closed)(lws_abs_user_t *d); + int (*heartbeat)(lws_abs_user_t *d); + +} lws_abstract_t; + + diff --git a/lib/abstract/smtp/private.h b/lib/abstract/smtp/private.h new file mode 100644 index 000000000..bbc51f85c --- /dev/null +++ b/lib/abstract/smtp/private.h @@ -0,0 +1,51 @@ +/* + * libwebsockets lib/abstruct/smtp/private.h + * + * Copyright (C) 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "abstract/private.h" + +/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ +typedef enum lwsgs_smtp_states { + LGSSMTP_IDLE, /**< awaiting new email */ + LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ + LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ + LGSSMTP_SENT_HELO, /**< sent the HELO */ + LGSSMTP_SENT_FROM, /**< sent FROM */ + LGSSMTP_SENT_TO, /**< sent TO */ + LGSSMTP_SENT_DATA, /**< sent DATA request */ + LGSSMTP_SENT_BODY, /**< sent the email body */ + LGSSMTP_SENT_QUIT, /**< sent the session quit */ +} lwsgs_smtp_states_t; + +/** struct lws_email - abstract context for performing SMTP operations */ +typedef struct lws_smtp_client { + struct lws_dll2_owner pending_owner; + + lws_smtp_client_info_t i; + lws_abstract_t abs; + + lws_abs_user_t *abs_conn; + + lwsgs_smtp_states_t estate; + time_t email_connect_started; + + unsigned char send_pending:1; +} lws_smtp_client_t; + diff --git a/lib/abstract/smtp/smtp.c b/lib/abstract/smtp/smtp.c new file mode 100644 index 000000000..988700f8c --- /dev/null +++ b/lib/abstract/smtp/smtp.c @@ -0,0 +1,395 @@ +/* + * Abstract SMTP support for libwebsockets + * + * Copyright (C) 2016-2019 Andy Green + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" +#include "abstract/smtp/private.h" + +static const short retcodes[] = { + 0, /* idle */ + 0, /* connecting */ + 220, /* connected */ + 250, /* helo */ + 250, /* from */ + 250, /* to */ + 354, /* data */ + 250, /* body */ + 221, /* quit */ +}; + +static void +lws_smtp_client_state_transition(lws_smtp_client_t *c, lwsgs_smtp_states_t s) +{ + lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s); + c->estate = s; +} + +void +lws_smtp_client_kick(lws_smtp_client_t *c) +{ + lws_smtp_email_t *e; + lws_dll2_t *d; + char buf[64]; + int n; + + if (c->estate != LGSSMTP_IDLE) + return; + + /* is there something to do? */ + +again: + d = lws_dll2_get_head(&c->pending_owner); + if (!d) + return; + + e = lws_container_of(d, lws_smtp_email_t, list); + + /* do we need to time out this guy? */ + + if ((time_t)lws_now_secs() - e->added > (time_t)c->i.delivery_timeout) { + lwsl_err("%s: timing out email\n", __func__); + lws_dll2_remove(&e->list); + n = lws_snprintf(buf, sizeof(buf), "0 Timed out retrying send"); + e->done(e, buf, n); + + if (lws_dll2_get_head(&c->pending_owner)) + goto again; + + return; + } + + /* is it time for his retry yet? */ + + if (e->last_try && + (time_t)lws_now_secs() - e->last_try < (time_t)c->i.retry_interval) { + /* no... send him to the tail */ + lws_dll2_remove(&e->list); + lws_dll2_add_tail(&e->list, &c->pending_owner); + return; + } + + /* check if we have a connection to the server ongoing */ + + if (c->abs.state(c->abs_conn)) { + /* + * there's a connection, it could be still trying to connect + * or established + */ + c->abs.ask_for_writeable(c->abs_conn); + + return; + } + + /* there's no existing connection */ + + lws_smtp_client_state_transition(c, LGSSMTP_CONNECTING); + lwsl_notice("LGSSMTP_IDLE: connecting to %s:25\n", c->i.ip); + + if (c->abs.client_conn(c->abs_conn, c->i.vh, c->i.ip, 25, 0)) { + lwsl_err("%s: failed to connect to %s:25\n", + __func__, c->i.ip); + + return; + } + + e->tries++; + e->last_try = lws_now_secs(); +} + +/* + * we became connected + */ + +static int +lws_smtp_client_abs_accept(lws_abs_user_t *abs_priv) +{ + lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv; + + lws_smtp_client_state_transition(c, LGSSMTP_CONNECTED); + + return 0; +} + +static int +lws_smtp_client_abs_rx(lws_abs_user_t *abs_priv, uint8_t *buf, size_t len) +{ + lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv; + lws_smtp_email_t *e; + lws_dll2_t *pd2; + int n; + + pd2 = lws_dll2_get_head(&c->pending_owner); + if (!pd2) + return 0; + + e = lws_container_of(pd2, lws_smtp_email_t, list); + if (!e) + return 0; + + lwsl_debug("%s: rx: '%.*s'\n", __func__, (int)len, (const char *)buf); + + n = atoi((char *)buf); + if (n != retcodes[c->estate]) { + lwsl_notice("%s: bad response from server: %d (state %d) %.*s\n", + __func__, n, c->estate, (int)len, buf); + + lws_dll2_remove(&e->list); + lws_dll2_add_tail(&e->list, &c->pending_owner); + lws_smtp_client_state_transition(c, LGSSMTP_IDLE); + lws_smtp_client_kick(c); + + return 0; + } + + if (c->estate == LGSSMTP_SENT_QUIT) { + lwsl_debug("%s: done\n", __func__); + lws_smtp_client_state_transition(c, LGSSMTP_IDLE); + + lws_dll2_remove(&e->list); + if (e->done && e->done(e, "sent OK", 7)) + return 1; + + return 1; + } + + c->send_pending = 1; + c->abs.ask_for_writeable(c->abs_conn); + + return 0; +} + +static int +lws_smtp_client_abs_writeable(lws_abs_user_t *abs_priv, size_t budget) +{ + lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv; + char b[256 + LWS_PRE], *p = b + LWS_PRE; + lws_smtp_email_t *e; + lws_dll2_t *pd2; + int n; + + pd2 = lws_dll2_get_head(&c->pending_owner); + if (!pd2) + return 0; + + e = lws_container_of(pd2, lws_smtp_email_t, list); + if (!e) + return 0; + + + if (!c->send_pending) + return 0; + + c->send_pending = 0; + + lwsl_debug("%s: writing response for state %d\n", __func__, c->estate); + + switch (c->estate) { + case LGSSMTP_CONNECTED: + n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->i.helo); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_HELO); + break; + case LGSSMTP_SENT_HELO: + n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n", + e->email_from); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_FROM); + break; + case LGSSMTP_SENT_FROM: + n = lws_snprintf(p, sizeof(b) - LWS_PRE, + "RCPT TO: <%s>\n", e->email_to); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_TO); + break; + case LGSSMTP_SENT_TO: + n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n"); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_DATA); + break; + case LGSSMTP_SENT_DATA: + p = (char *)e->payload; + n = strlen(e->payload); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_BODY); + break; + case LGSSMTP_SENT_BODY: + n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n"); + lws_smtp_client_state_transition(c, LGSSMTP_SENT_QUIT); + break; + case LGSSMTP_SENT_QUIT: + return 0; + + default: + return 0; + } + + //puts(p); + c->abs.tx(c->abs_conn, (uint8_t *)p, n); + + return 0; +} + +static int +lws_smtp_client_abs_closed(lws_abs_user_t *abs_priv) +{ + lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv; + + c->abs_conn = NULL; + + return 0; +} + +static int +lws_smtp_client_abs_heartbeat(lws_abs_user_t *abs_priv) +{ + lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv; + + lws_smtp_client_kick(c); + + return 0; +} + +lws_smtp_email_t * +lws_smtp_client_alloc_email_helper(const char *payload, size_t payload_len, + const char *sender, const char *recipient, + const char *extra, size_t extra_len, void *data, + int (*done)(struct lws_smtp_email *e, + void *buf, size_t len)) +{ + size_t ls = strlen(sender), lr = strlen(recipient); + lws_smtp_email_t *em; + char *p; + + em = malloc(sizeof(*em) + payload_len + ls + lr + extra_len + 4); + if (!em) { + lwsl_err("OOM\n"); + return NULL; + } + + p = (char *)&em[1]; + + memset(em, 0, sizeof(*em)); + + em->data = data; + em->done = done; + + em->email_from = p; + memcpy(p, sender, ls + 1); + p += ls + 1; + em->email_to = p; + memcpy(p, recipient, lr + 1); + p += lr + 1; + em->payload = p; + memcpy(p, payload, payload_len + 1); + p += payload_len + 1; + + if (extra) { + em->extra = p; + memcpy(p, extra, extra_len + 1); + } + + return em; +} + +int +lws_smtp_client_add_email(lws_smtp_client_t *c, lws_smtp_email_t *e) +{ + if (c->pending_owner.count > c->i.email_queue_max) { + lwsl_err("%s: email queue at limit of %d\n", __func__, + (int)c->i.email_queue_max); + + return 1; + } + + e->added = lws_now_secs(); + e->last_try = 0; + e->tries = 0; + + lws_dll2_clear(&e->list); + lws_dll2_add_tail(&e->list, &c->pending_owner); + + lws_smtp_client_kick(c); + + return 0; +} + +lws_smtp_client_t * +lws_smtp_client_create(const lws_smtp_client_info_t *ci) +{ + lws_smtp_client_t *c; + + c = lws_zalloc(sizeof(*c), "email client"); + if (!c) + return NULL; + + c->i = *ci; + c->abs = *ci->abs; + + /* fill in the additional abstract callbacks we fulfil */ + + c->abs.accept = lws_smtp_client_abs_accept; + c->abs.rx = lws_smtp_client_abs_rx; + c->abs.writeable = lws_smtp_client_abs_writeable; + c->abs.closed = lws_smtp_client_abs_closed; + c->abs.heartbeat = lws_smtp_client_abs_heartbeat; + + if (!c->i.email_queue_max) + c->i.email_queue_max = 8; + + if (!c->i.retry_interval) + c->i.retry_interval = 15 * 60; + + if (!c->i.delivery_timeout) + c->i.delivery_timeout = 12 * 60 * 60; + + c->abs_conn = c->abs.create(&c->abs, c); + if (!c->abs_conn) { + lws_free(c); + + return NULL; + } + + lws_smtp_client_state_transition(c, LGSSMTP_IDLE); + + return c; +} + +static int +cleanup(struct lws_dll2 *d, void *user) +{ + lws_smtp_email_t *e; + + e = lws_container_of(d, lws_smtp_email_t, list); + if (e->done && e->done(e, "destroying", 10)) + return 1; + + return 0; +} + +void +lws_smtp_client_destroy(lws_smtp_client_t **c) +{ + if (!*c) + return; + + lws_dll2_foreach_safe(&(*c)->pending_owner, NULL, cleanup); + + if ((*c)->abs_conn) { + (*c)->abs.close((*c)->abs_conn); + (*c)->abs.destroy(&(*c)->abs_conn); + } + + lws_free_set_NULL(*c); +} diff --git a/lib/abstract/transports/raw-skt.c b/lib/abstract/transports/raw-skt.c new file mode 100644 index 000000000..739b7a0d0 --- /dev/null +++ b/lib/abstract/transports/raw-skt.c @@ -0,0 +1,309 @@ +/* + * libwebsockets lib/abstruct/transports/raw-skt.c + * + * Copyright (C) 2019 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" +#include "abstract/private.h" + +typedef struct lws_atrs_priv { + struct lws_abstract *abs; + struct lws *wsi; + void *user; + + lws_dll2_t same_abs_transport_list; + + uint8_t established:1; + uint8_t connecting:1; +} lws_atrs_priv_t; + +struct vhd { + lws_dll2_owner_t owner; +}; + +static int +heartbeat_cb(struct lws_dll2 *d, void *user) +{ + lws_atrs_priv_t *priv = lws_container_of(d, lws_atrs_priv_t, + same_abs_transport_list); + if (priv->abs->heartbeat) + priv->abs->heartbeat(priv->user); + + return 0; +} + +static int +callback_abs_client_raw_skt(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)user; + struct vhd *vhd = (struct vhd *) + lws_protocol_vh_priv_get(lws_get_vhost(wsi), + lws_get_protocol(wsi)); + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), sizeof(struct vhd)); + if (!vhd) + return 1; + lws_timed_callback_vh_protocol(lws_get_vhost(wsi), + lws_get_protocol(wsi), + LWS_CALLBACK_USER, 1); + break; + + case LWS_CALLBACK_USER: + /* + * This comes at 1Hz without a wsi context, so there is no + * valid priv. We need to track the live abstract objects that + * are using our abstract protocol, and pass the heartbeat + * through to the ones that care. + */ + if (!vhd) + break; + + lws_dll2_foreach_safe(&vhd->owner, NULL, heartbeat_cb); + + lws_timed_callback_vh_protocol(lws_get_vhost(wsi), + lws_get_protocol(wsi), + LWS_CALLBACK_USER, 1); + break; + + case LWS_CALLBACK_RAW_CONNECTED: + lwsl_debug("LWS_CALLBACK_RAW_CONNECTED\n"); + priv->connecting = 0; + priv->established = 1; + if (priv->abs->accept) + priv->abs->accept(priv->user); + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_user("CONNECTION_ERROR\n"); + if (in) + lwsl_user(" %s\n", (const char *)in); + + /* fallthru */ + case LWS_CALLBACK_RAW_CLOSE: + if (!user) + break; + lwsl_debug("LWS_CALLBACK_RAW_CLOSE\n"); + priv->established = 0; + priv->connecting = 0; + if (priv->abs && priv->abs->closed) + priv->abs->closed(priv->user); + lws_free(priv); + lws_set_wsi_user(wsi, NULL); + break; + + case LWS_CALLBACK_RAW_RX: + lwsl_debug("LWS_CALLBACK_RAW_RX (%d)\n", (int)len); + return !!priv->abs->rx(priv->user, in, len); + + case LWS_CALLBACK_RAW_WRITEABLE: + lwsl_debug("LWS_CALLBACK_RAW_WRITEABLE\n"); + priv->abs->writeable(priv->user, + lws_get_peer_write_allowance(priv->wsi)); + break; + + case LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL: + lws_dll2_add_tail(&priv->same_abs_transport_list, &vhd->owner); + break; + + case LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL: + lws_dll2_remove(&priv->same_abs_transport_list); + break; + + default: + break; + } + + return 0; +} + +const struct lws_protocols protocol_abs_client_raw_skt = { + "lws-abs-cli-raw-skt", callback_abs_client_raw_skt, + 0, 1024, 1024, NULL, 0 +}; + +static int +lws_atcrs_tx(lws_abs_user_t *abs_priv, uint8_t *buf, size_t len) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)abs_priv; + + if (!priv->wsi) { + lwsl_err("%s: NULL priv->wsi\n", __func__); + return 1; + } + + lwsl_debug("%s: priv %p, wsi %p, ro %p\n", __func__, + priv, priv->wsi, priv->wsi->role_ops); + + if (lws_write(priv->wsi, buf, len, LWS_WRITE_RAW) < 0) + priv->abs->close(priv->user); + + return 0; +} + +#if !defined(LWS_WITHOUT_CLIENT) +static int +lws_atcrs_client_conn(lws_abs_user_t *abs_priv, struct lws_vhost *vh, + const char *ip, uint16_t port, int tls_flags) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)abs_priv; + struct lws_client_connect_info i; + + if (priv->connecting) + return 0; + + if (priv->established) { + lws_set_timeout(priv->wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5); + + return 0; + } + + lwsl_debug("%s: priv %p connecting to %s:%u %p\n", __func__, priv, + ip, port, vh->context); + + memset(&i, 0, sizeof(i)); + + i.path = ""; + i.vhost = vh; + i.port = port; + i.address = ip; + i.method = "RAW"; + i.userdata = priv; + i.host = i.address; + i.pwsi = &priv->wsi; + i.origin = i.address; + i.context = vh->context; + i.ssl_connection = tls_flags; + i.local_protocol_name = "lws-abs-cli-raw-skt"; + + priv->wsi = lws_client_connect_via_info(&i); + if (!priv->wsi) + return 1; + + priv->connecting = 1; + + return 0; +} +#endif + +static int +lws_atcrs_close(lws_abs_user_t *abs_priv) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)abs_priv; + struct lws *wsi = priv->wsi; + + if (!priv->wsi) + return 0; + + if (!lws_raw_transaction_completed(priv->wsi)) + return 0; + + priv->wsi = NULL; + lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC); + + /* priv is destroyed in the CLOSE callback */ + + return 0; +} + +static int +lws_atcrs_ask_for_writeable(lws_abs_user_t *abs_priv) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)abs_priv; + + if (!priv->wsi || !priv->established) + return 1; + + lws_callback_on_writable(priv->wsi); + + return 0; +} + +static lws_abs_user_t * +lws_atcrs_create(struct lws_abstract *abs, void *user) +{ + lws_atrs_priv_t *p = lws_zalloc(sizeof(*p), __func__); + + if (!p) + return NULL; + + lwsl_debug("%s: created priv %p\n", __func__, p); + + p->abs = abs; + p->user = user; + + return (lws_abs_user_t *)p; +} + +static void +lws_atcrs_destroy(lws_abs_user_t **abs_priv) +{ + lws_free_set_NULL(*abs_priv); +} + +static int +lws_atcrs_set_timeout(lws_abs_user_t *d, int reason, int secs) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)d; + + lws_set_timeout(priv->wsi, reason, secs); + + return 0; +} + +static int +lws_atcrs_state(lws_abs_user_t *abs_priv) +{ + lws_atrs_priv_t *priv = (lws_atrs_priv_t *)abs_priv; + + if (!priv || !priv->wsi || (!priv->established && !priv->connecting)) + return 0; + + return 1; +} + +lws_abstract_t lws_abstract_transport_cli_raw_skt = { + "raw-skt", + lws_atcrs_create, + lws_atcrs_destroy, + + lws_atcrs_tx, +#if defined(LWS_WITHOUT_CLIENT) + NULL, +#else + lws_atcrs_client_conn, +#endif + lws_atcrs_close, + lws_atcrs_ask_for_writeable, + lws_atcrs_set_timeout, + lws_atcrs_state, + + /* + * remaining callbacks must be defined by abstract object and are + * called by this protocol handler + */ + + NULL, /* accept */ + NULL, /* rx */ + NULL, /* writeable */ + NULL /* closed */ +}; diff --git a/lib/core-net/adopt.c b/lib/core-net/adopt.c index f0e8fedff..88f718b7c 100644 --- a/lib/core-net/adopt.c +++ b/lib/core-net/adopt.c @@ -60,6 +60,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi) return NULL; } + new_wsi->wsistate |= LWSIFR_SERVER; new_wsi->tsi = n; lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, vhost->name, new_wsi->tsi); @@ -208,11 +209,12 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, * selected yet so we issue this to the vhosts's default protocol, * itself by default protocols[0] */ + new_wsi->wsistate |= LWSIFR_SERVER; n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]) n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]; - lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); + lwsl_err("new wsi wsistate 0x%x\n", new_wsi->wsistate); if (context->event_loop_ops->accept) if (context->event_loop_ops->accept(new_wsi)) diff --git a/lib/core-net/private.h b/lib/core-net/private.h index 3144e98a1..8116025f6 100644 --- a/lib/core-net/private.h +++ b/lib/core-net/private.h @@ -1070,6 +1070,8 @@ int lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, int buffered); +extern const struct lws_protocols protocol_abs_client_raw_skt; + #ifdef __cplusplus }; #endif diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index a3f962b1b..175ab340d 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -56,6 +56,13 @@ const struct lws_event_loop_ops *available_event_libs[] = { NULL }; +const struct lws_protocols *available_abstract_protocols[] = { +#if defined(LWS_ROLE_RAW) + &protocol_abs_client_raw_skt, +#endif + NULL +}; + static const char * const mount_protocols[] = { "http://", "https://", @@ -420,12 +427,11 @@ lws_create_vhost(struct lws_context *context, **vh1 = &context->vhost_list; const struct lws_http_mount *mounts; const struct lws_protocols *pcols = info->protocols; - const struct lws_protocol_vhost_options *pvo; #ifdef LWS_WITH_PLUGINS struct lws_plugin *plugin = context->plugin_list; #endif struct lws_protocols *lwsp; - int m, f = !info->pvo, fx = 0; + int m, f = !info->pvo, fx = 0, abs_pcol_count; char buf[20]; #if !defined(LWS_WITHOUT_CLIENT) && defined(LWS_HAVE_GETENV) char *p; @@ -539,18 +545,30 @@ lws_create_vhost(struct lws_context *context, fx = 1; #endif + abs_pcol_count = (int)LWS_ARRAY_SIZE(available_abstract_protocols) - 1; + /* - * give the vhost a unified list of protocols including the - * ones that came from plugins + * give the vhost a unified list of protocols including: + * + * - internal, abstracted ones + * - the ones that came from plugins + * - his user protocols */ - lwsp = lws_zalloc(sizeof(struct lws_protocols) * (vh->count_protocols + - context->plugin_protocol_count + fx + 1), + lwsp = lws_zalloc(sizeof(struct lws_protocols) * + (vh->count_protocols + + abs_pcol_count + + context->plugin_protocol_count + + fx + 1), "vhost-specific plugin table"); if (!lwsp) { lwsl_err("OOM\n"); return NULL; } + /* + * 1: user protocols (from pprotocols or protocols) + */ + m = vh->count_protocols; if (!pcols) { for (n = 0; n < m; n++) @@ -559,7 +577,17 @@ lws_create_vhost(struct lws_context *context, memcpy(lwsp, pcols, sizeof(struct lws_protocols) * m); /* - * For compatibility, all protocols enabled on vhost if only + * 2: abstract protocols + */ + + for (n = 0; n < abs_pcol_count; n++) { + memcpy(&lwsp[m++], available_abstract_protocols[n], + sizeof(*lwsp)); + vh->count_protocols++; + } + + /* + * 3: For compatibility, all protocols enabled on vhost if only * the default vhost exists. Otherwise only vhosts who ask * for a protocol get it enabled. */ @@ -569,7 +597,6 @@ lws_create_vhost(struct lws_context *context, (void)f; #ifdef LWS_WITH_PLUGINS if (plugin) { - while (plugin) { for (n = 0; n < plugin->caps.count_protocols; n++) { /* @@ -591,21 +618,12 @@ lws_create_vhost(struct lws_context *context, #endif #if defined(LWS_WITH_HTTP_PROXY) && defined(LWS_ROLE_WS) - memcpy(&lwsp[m++], &lws_ws_proxy, sizeof(lws_ws_proxy)); + memcpy(&lwsp[m++], &lws_ws_proxy, sizeof(*lwsp)); vh->count_protocols++; #endif - if (!pcols || -#ifdef LWS_WITH_PLUGINS - (context->plugin_list) || -#endif - (context->options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { - vh->protocols = lwsp; - vh->allocated_vhost_protocols = 1; - } else { - vh->protocols = pcols; - lws_free(lwsp); - } + vh->protocols = lwsp; + vh->allocated_vhost_protocols = 1; vh->same_vh_protocol_heads = (struct lws_dll *) lws_zalloc(sizeof(struct lws_dll) * @@ -643,22 +661,6 @@ lws_create_vhost(struct lws_context *context, mount_protocols[mounts->origin_protocol], mounts->origin, mounts->mountpoint); - /* convert interpreter protocol names to pointers */ - pvo = mounts->interpret; - while (pvo) { - for (n = 0; n < vh->count_protocols; n++) { - if (strcmp(pvo->value, vh->protocols[n].name)) - continue; - ((struct lws_protocol_vhost_options *)pvo)-> - value = (const char *)(lws_intptr_t)n; - break; - } - if (n == vh->count_protocols) - lwsl_err("ignoring unknown interp pr %s\n", - pvo->value); - pvo = pvo->next; - } - mounts = mounts->mount_next; } diff --git a/lib/misc/smtp.c b/lib/misc/smtp.c deleted file mode 100644 index 290499c15..000000000 --- a/lib/misc/smtp.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * SMTP support for libwebsockets - * - * Copyright (C) 2016-2017 Andy Green - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -#include "core/private.h" - -static unsigned int -lwsgs_now_secs(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - return tv.tv_sec; -} - -static void -ccb(uv_handle_t* handle) -{ - -} - -static void -alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) -{ - struct lws_email *email = (struct lws_email *)handle->data; - - *buf = uv_buf_init(email->email_buf, sizeof(email->email_buf) - 1); -} - -static void -on_write_end(uv_write_t *req, int status) { - lwsl_notice("%s\n", __func__); - if (status == -1) { - fprintf(stderr, "error on_write_end"); - return; - } -} - -static void -lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf) -{ - struct lws_email *email = (struct lws_email *)s->data; - static const short retcodes[] = { - 0, /* idle */ - 0, /* connecting */ - 220, /* connected */ - 250, /* helo */ - 250, /* from */ - 250, /* to */ - 354, /* data */ - 250, /* body */ - 221, /* quit */ - }; - uv_write_t write_req; - uv_buf_t wbuf; - int n; - - if (nread >= 0) - email->email_buf[nread] = '\0'; - lwsl_notice("%s: %s\n", __func__, buf->base); - if (nread == -1) { - lwsl_err("%s: failed\n", __func__); - return; - } - - n = atoi(buf->base); - if (n != retcodes[email->estate]) { - lwsl_err("%s: bad response from server\n", __func__); - goto close_conn; - } - - switch (email->estate) { - case LGSSMTP_CONNECTED: - n = sprintf(email->content, "HELO %s\n", email->email_helo); - email->estate = LGSSMTP_SENT_HELO; - break; - case LGSSMTP_SENT_HELO: - n = sprintf(email->content, "MAIL FROM: <%s>\n", - email->email_from); - email->estate = LGSSMTP_SENT_FROM; - break; - case LGSSMTP_SENT_FROM: - n = sprintf(email->content, "RCPT TO: <%s>\n", email->email_to); - email->estate = LGSSMTP_SENT_TO; - break; - case LGSSMTP_SENT_TO: - n = sprintf(email->content, "DATA\n"); - email->estate = LGSSMTP_SENT_DATA; - break; - case LGSSMTP_SENT_DATA: - if (email->on_get_body(email, email->content, - email->max_content_size)) - return; - n = strlen(email->content); - email->estate = LGSSMTP_SENT_BODY; - break; - case LGSSMTP_SENT_BODY: - n = sprintf(email->content, "quit\n"); - email->estate = LGSSMTP_SENT_QUIT; - break; - case LGSSMTP_SENT_QUIT: - lwsl_notice("%s: done\n", __func__); - email->on_sent(email); - email->estate = LGSSMTP_IDLE; - goto close_conn; - default: - return; - } - - puts(email->content); - wbuf = uv_buf_init(email->content, n); - uv_write(&write_req, s, &wbuf, 1, on_write_end); - - return; - -close_conn: - - uv_close((uv_handle_t *)s, ccb); -} - -static void -lwsgs_email_on_connect(uv_connect_t *req, int status) -{ - struct lws_email *email = (struct lws_email *)req->data; - - lwsl_notice("%s\n", __func__); - - if (status == -1) { - lwsl_err("%s: failed\n", __func__); - return; - } - - uv_read_start(req->handle, alloc_buffer, lwsgs_email_read); - email->estate = LGSSMTP_CONNECTED; -} - - -static void -uv_timeout_cb_email(uv_timer_t *w -#if UV_VERSION_MAJOR == 0 - , int status -#endif -) -{ - struct lws_email *email = lws_container_of(w, struct lws_email, - timeout_email); - time_t now = lwsgs_now_secs(); - struct sockaddr_in req_addr; - - switch (email->estate) { - case LGSSMTP_IDLE: - - if (email->on_next(email)) - break; - - email->estate = LGSSMTP_CONNECTING; - - uv_tcp_init(email->loop, &email->email_client); - if (uv_ip4_addr(email->email_smtp_ip, 25, &req_addr)) { - lwsl_err("Unable to convert mailserver ads\n"); - return; - } - - lwsl_notice("LGSSMTP_IDLE: connecting\n"); - - email->email_connect_started = now; - email->email_connect_req.data = email; - email->email_client.data = email; - uv_tcp_connect(&email->email_connect_req, &email->email_client, - (struct sockaddr *)&req_addr, - lwsgs_email_on_connect); - - uv_timer_start(&email->timeout_email, - uv_timeout_cb_email, 5000, 0); - - break; - - case LGSSMTP_CONNECTING: - if (email->email_connect_started - now > 5) { - lwsl_err("mail session timed out\n"); - /* !!! kill the connection */ - uv_close((uv_handle_t *) &email->email_connect_req, ccb); - email->estate = LGSSMTP_IDLE; - } - break; - - default: - break; - } -} - -LWS_VISIBLE LWS_EXTERN int -lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content) -{ - email->content = lws_malloc(max_content, "email content"); - if (!email->content) - return 1; - - email->max_content_size = max_content; - uv_timer_init(loop, &email->timeout_email); - - email->loop = loop; - - /* trigger him one time in a bit */ - uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 2000, 0); - - return 0; -} - -LWS_VISIBLE LWS_EXTERN void -lws_email_check(struct lws_email *email) -{ - uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 1000, 0); -} - -LWS_VISIBLE LWS_EXTERN void -lws_email_destroy(struct lws_email *email) -{ - if (email->content) - lws_free_set_NULL(email->content); - - uv_timer_stop(&email->timeout_email); - uv_close((uv_handle_t *)&email->timeout_email, NULL); -} diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 44f33c067..b31d86a1a 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -25,14 +25,195 @@ lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) return getaddrinfo(ads, NULL, &hints, result); } + +struct lws * +lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen) +{ + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; + const char *meth = NULL; + struct lws_pollfd pfd; + const char *cce = ""; + int n, m, rawish = 0; + + if (wsi->stash) + meth = wsi->stash->method; + else + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + + if (meth && !strcmp(meth, "RAW")) + rawish = 1; + + if (wsi_piggyback) + goto send_hs; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* we are connected to server, or proxy */ + + /* http proxy */ + if (wsi->vhost->http.http_proxy_port) { + + /* + * OK from now on we talk via the proxy, so connect to that + * + * (will overwrite existing pointer, + * leaving old string/frag there but unreferenced) + */ + if (wsi->stash) { + lws_free(wsi->stash->address); + wsi->stash->address = + lws_strdup(wsi->vhost->http.http_proxy_address); + if (!wsi->stash->address) + goto failed; + } else + if (lws_hdr_simple_create(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->vhost->http.http_proxy_address)) + goto failed; + wsi->c_port = wsi->vhost->http.http_proxy_port; + + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing to proxy socket\n"); + cce = "proxy write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + AWAITING_TIMEOUT); + + lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY); + + return wsi; + } +#endif +#if defined(LWS_WITH_SOCKS5) + /* socks proxy */ + else if (wsi->vhost->socks_proxy_port) { + n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_debug("ERROR writing socks greeting\n"); + cce = "socks write failed"; + goto failed; + } + + lws_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, + AWAITING_TIMEOUT); + + lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY); + + return wsi; + } +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +send_hs: + + if (wsi_piggyback && + !lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) { + /* + * We are pipelining on an already-established connection... + * we can skip tls establishment. + */ + + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); + + /* + * we can't send our headers directly, because they have to + * be sent when the parent is writeable. The parent will check + * for anybody on his client transaction queue that is in + * LRS_H1C_ISSUE_HANDSHAKE2, and let them write. + * + * If we are trying to do this too early, before the master + * connection has written his own headers, then it will just + * wait in the queue until it's possible to send them. + */ + lws_callback_on_writable(wsi_piggyback); + lwsl_info("%s: wsi %p: waiting to send hdrs (par state 0x%x)\n", + __func__, wsi, lwsi_state(wsi_piggyback)); + } else { + lwsl_info("%s: wsi %p: %s %s client created own conn (raw %d)\n", + __func__, wsi, wsi->role_ops->name, + wsi->protocol->name, rawish); + + /* we are making our own connection */ + if (!rawish) + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); + else { + /* for a method = "RAW" connection, this makes us + * established */ + + /* clear his established timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + m = wsi->role_ops->adoption_cb[0]; + if (m) { + n = user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + m, wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("LWS_CALLBACK_RAW_PROXY_CLI_ADOPT failed\n"); + goto failed; + } + } + + /* service.c pollout processing wants this */ + wsi->hdr_parsing_completed = 1; + lwsl_info("%s: setting ESTABLISHED\n", __func__); + lwsi_set_state(wsi, LRS_ESTABLISHED); + + return wsi; + } + + /* + * provoke service to issue the handshake directly. + * + * we need to do it this way because in the proxy case, this is + * the next state and executed only if and when we get a good + * proxy response inside the state machine... but notice in + * SSL case this may not have sent anything yet with 0 return, + * and won't until many retries from main loop. To stop that + * becoming endless, cover with a timeout. + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + AWAITING_TIMEOUT); + + assert(lws_socket_is_valid(wsi->desc.sockfd)); + + pfd.fd = wsi->desc.sockfd; + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLIN; + + n = lws_service_fd(wsi->context, &pfd); + if (n < 0) { + cce = "first service failed"; + goto failed; + } + if (n) /* returns 1 on failure after closing wsi */ + return NULL; + } +#endif + return wsi; + +failed: + wsi->protocol->callback(wsi, + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, + wsi->user_space, (void *)cce, strlen(cce)); + wsi->already_did_cce = 1; + + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect2"); + + return NULL; +} + struct lws * lws_client_connect_2(struct lws *wsi) { #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) struct lws_context *context = wsi->context; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; - struct lws *wsi_piggyback = NULL; - struct lws_pollfd pfd; const char *adsin; ssize_t plen = 0; #endif @@ -40,7 +221,7 @@ lws_client_connect_2(struct lws *wsi) struct sockaddr_un sau; char unix_skt = 0; #endif - int n, m, port = 0, rawish = 0; + int n, port = 0; const char *cce = "", *iface; const struct sockaddr *psa; const char *meth = NULL; @@ -72,9 +253,6 @@ lws_client_connect_2(struct lws *wsi) else meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); - if (meth && !strcmp(meth, "RAW")) - rawish = 1; - if (meth && strcmp(meth, "GET") && strcmp(meth, "POST")) goto create_new_conn; @@ -151,11 +329,8 @@ lws_client_connect_2(struct lws *wsi) * and wait for our turn at client transaction_complete * to take over parsing the rx. */ - - wsi_piggyback = w; - lws_vhost_unlock(wsi->vhost); /* } ---------- */ - goto send_hs; + return lws_client_connect_3(wsi, w, plen); } } lws_end_foreach_dll_safe(d, d1); @@ -543,157 +718,10 @@ ads_known: } } - lwsl_client("connected\n"); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* we are connected to server, or proxy */ - /* http proxy */ - if (wsi->vhost->http.http_proxy_port) { + return lws_client_connect_3(wsi, NULL, plen); - /* - * OK from now on we talk via the proxy, so connect to that - * - * (will overwrite existing pointer, - * leaving old string/frag there but unreferenced) - */ - if (wsi->stash) { - lws_free(wsi->stash->address); - wsi->stash->address = - lws_strdup(wsi->vhost->http.http_proxy_address); - if (!wsi->stash->address) - goto failed; - } else - if (lws_hdr_simple_create(wsi, - _WSI_TOKEN_CLIENT_PEER_ADDRESS, - wsi->vhost->http.http_proxy_address)) - goto failed; - wsi->c_port = wsi->vhost->http.http_proxy_port; - - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, (int)plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing to proxy socket\n"); - cce = "proxy write failed"; - goto failed; - } - - lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, - AWAITING_TIMEOUT); - - lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY); - - return wsi; - } -#endif -#if defined(LWS_WITH_SOCKS5) - /* socks proxy */ - else if (wsi->vhost->socks_proxy_port) { - n = send(wsi->desc.sockfd, (char *)pt->serv_buf, plen, - MSG_NOSIGNAL); - if (n < 0) { - lwsl_debug("ERROR writing socks greeting\n"); - cce = "socks write failed"; - goto failed; - } - - lws_set_timeout(wsi, - PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, - AWAITING_TIMEOUT); - - lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY); - - return wsi; - } -#endif -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -send_hs: - - if (wsi_piggyback && - !lws_dll2_is_detached(&wsi->dll2_cli_txn_queue)) { - /* - * We are pipelining on an already-established connection... - * we can skip tls establishment. - */ - - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); - - /* - * we can't send our headers directly, because they have to - * be sent when the parent is writeable. The parent will check - * for anybody on his client transaction queue that is in - * LRS_H1C_ISSUE_HANDSHAKE2, and let them write. - * - * If we are trying to do this too early, before the master - * connection has written his own headers, then it will just - * wait in the queue until it's possible to send them. - */ - lws_callback_on_writable(wsi_piggyback); - lwsl_info("%s: wsi %p: waiting to send hdrs (par state 0x%x)\n", - __func__, wsi, lwsi_state(wsi_piggyback)); - } else { - lwsl_info("%s: wsi %p: client creating own connection\n", - __func__, wsi); - - /* we are making our own connection */ - if (!rawish) - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); - else { - /* for a method = "RAW" connection, this makes us - * established */ - - /* clear his established timeout */ - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - m = wsi->role_ops->adoption_cb[0]; - if (m) { - n = user_callback_handle_rxflow( - wsi->protocol->callback, wsi, - m, wsi->user_space, NULL, 0); - if (n < 0) { - lwsl_info("LWS_CALLBACK_RAW_PROXY_CLI_ADOPT failed\n"); - goto failed; - } - } - - /* service.c pollout processing wants this */ - wsi->hdr_parsing_completed = 1; - - lwsi_set_state(wsi, LRS_ESTABLISHED); - - return wsi; - } - - /* - * provoke service to issue the handshake directly. - * - * we need to do it this way because in the proxy case, this is - * the next state and executed only if and when we get a good - * proxy response inside the state machine... but notice in - * SSL case this may not have sent anything yet with 0 return, - * and won't until many retries from main loop. To stop that - * becoming endless, cover with a timeout. - */ - - lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, - AWAITING_TIMEOUT); - - assert(lws_socket_is_valid(wsi->desc.sockfd)); - - pfd.fd = wsi->desc.sockfd; - pfd.events = LWS_POLLIN; - pfd.revents = LWS_POLLIN; - - n = lws_service_fd(context, &pfd); - if (n < 0) { - cce = "first service failed"; - goto failed; - } - if (n) /* returns 1 on failure after closing wsi */ - return NULL; - } -#endif - return wsi; oom4: if (lwsi_role_client(wsi) /* && lwsi_state_est(wsi) */) { @@ -734,6 +762,7 @@ failed1: return NULL; } + #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) /** diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index 12018ccaf..02c54e788 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -759,7 +759,11 @@ lws_client_interpret_server_handshake(struct lws *wsi) if (ah) ah->http_response = n; - if (n == 301 || n == 302 || n == 303 || n == 307 || n == 308) { + if ( +#if defined(LWS_WITH_HTTP_PROXY) + !wsi->http.proxy_clientside && +#endif + (n == 301 || n == 302 || n == 303 || n == 307 || n == 308)) { p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_LOCATION); if (!p) { cce = "HS: Redirect code but no Location"; @@ -1249,8 +1253,13 @@ spin_chunks: { struct lws *wsi_eff = lws_client_wsi_effective(wsi); - if (!wsi_eff->protocol_bind_balance == + if ( +#if defined(LWS_WITH_HTTP_PROXY) + !wsi_eff->protocol_bind_balance == !!wsi_eff->http.proxy_clientside && +#else + !!wsi_eff->protocol_bind_balance && +#endif user_callback_handle_rxflow(wsi_eff->protocol->callback, wsi_eff, LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ, wsi_eff->user_space, *buf, n)) { diff --git a/lib/roles/private.h b/lib/roles/private.h index 6eddf3af9..d8fcfeac5 100644 --- a/lib/roles/private.h +++ b/lib/roles/private.h @@ -329,3 +329,6 @@ enum { int lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot); + +struct lws * +lws_client_connect_3(struct lws *wsi, struct lws *wsi_piggyback, ssize_t plen); diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c index 6fabae381..e19bd2a3b 100644 --- a/lib/roles/raw-file/ops-raw-file.c +++ b/lib/roles/raw-file/ops-raw-file.c @@ -107,8 +107,10 @@ struct lws_role_ops role_ops_raw_file = { LWS_CALLBACK_RAW_ADOPT_FILE }, /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE, LWS_CALLBACK_RAW_RX_FILE }, - /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 }, - /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 }, + /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, + LWS_CALLBACK_RAW_WRITEABLE_FILE}, + /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, + LWS_CALLBACK_RAW_CLOSE_FILE}, /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL, LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL }, /* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL, diff --git a/lib/roles/raw-proxy/ops-raw-proxy.c b/lib/roles/raw-proxy/ops-raw-proxy.c index 0a75ef583..6957744d5 100644 --- a/lib/roles/raw-proxy/ops-raw-proxy.c +++ b/lib/roles/raw-proxy/ops-raw-proxy.c @@ -119,7 +119,7 @@ rops_adoption_bind_raw_proxy(struct lws *wsi, int type, { /* no http but socket... must be raw skt */ if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) || - !(type & LWS_ADOPT_FLAG_RAW_PROXY) || (type & _LWS_ADOPT_FINISH)) + (!(type & LWS_ADOPT_FLAG_RAW_PROXY)) || (type & _LWS_ADOPT_FINISH)) return 0; /* no match */ if (type & LWS_ADOPT_FLAG_UDP) @@ -160,10 +160,10 @@ rops_client_bind_raw_proxy(struct lws *wsi, /* we are a fallback if nothing else matched */ - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, - &role_ops_raw_proxy); +// lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, +// &role_ops_raw_proxy); - return 1; + return 0; } static int diff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c index 3a4fe9995..950f6a816 100644 --- a/lib/roles/raw-skt/ops-raw-skt.c +++ b/lib/roles/raw-skt/ops-raw-skt.c @@ -96,6 +96,11 @@ try_pollout: if (!(pollfd->revents & LWS_POLLOUT)) return LWS_HPI_RET_HANDLED; +#if !defined(LWS_WITHOUT_CLIENT) + if (lwsi_state(wsi) == LRS_WAITING_CONNECT) + lws_client_connect_3(wsi, NULL, 0); +#endif + /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { lwsl_notice("%s a\n", __func__); @@ -223,12 +228,14 @@ struct lws_role_ops role_ops_raw_skt = { #else NULL, #endif - /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT, + /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_CONNECTED, LWS_CALLBACK_RAW_ADOPT }, /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX, LWS_CALLBACK_RAW_RX }, - /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 }, - /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 }, + /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, + LWS_CALLBACK_RAW_WRITEABLE}, + /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, + LWS_CALLBACK_RAW_CLOSE }, /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL, LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL }, /* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL, diff --git a/minimal-examples/api-tests/README.md b/minimal-examples/api-tests/README.md index 8a1477d3b..a28df4f61 100644 --- a/minimal-examples/api-tests/README.md +++ b/minimal-examples/api-tests/README.md @@ -8,4 +8,5 @@ api-test-lws_tokenize|Generic secure string tokenizer api api-test-fts|LWS Full-text Search api api-test-gencrypto|LWS Generic Crypto apis api-test-jose|LWS JOSE apis +api-test-smtp_client|SMTP client for sending emails diff --git a/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt b/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt new file mode 100644 index 000000000..43f424695 --- /dev/null +++ b/minimal-examples/api-tests/api-test-smtp_client/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-api-test-smtp_client) +set(SRCS main.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_WITH_SMTP 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/api-tests/api-test-smtp_client/README.md b/minimal-examples/api-tests/api-test-smtp_client/README.md new file mode 100644 index 000000000..a3b3d01ff --- /dev/null +++ b/minimal-examples/api-tests/api-test-smtp_client/README.md @@ -0,0 +1,29 @@ +# lws api test smtp client + +Demonstrates how to send email through your local MTA + +## build + +Requires lws was built with `-DLWS_WITH_SMTP=1` at cmake. + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 +-r |Send the test email to this email address + + +``` + $ ./lws-api-test-smtp_client -r andy@warmcat.com +[2019/04/17 05:12:06:5293] USER: LWS API selftest: SMTP client +[2019/04/17 05:12:06:5635] NOTICE: LGSSMTP_IDLE: connecting to 127.0.0.1:25 +[2019/04/17 05:12:06:6238] NOTICE: email_sent_or_failed: sent OK +[2019/04/17 05:12:06:6394] USER: Completed: PASS + +``` + diff --git a/minimal-examples/api-tests/api-test-smtp_client/main.c b/minimal-examples/api-tests/api-test-smtp_client/main.c new file mode 100644 index 000000000..4ba79f826 --- /dev/null +++ b/minimal-examples/api-tests/api-test-smtp_client/main.c @@ -0,0 +1,137 @@ +/* + * lws-api-test-smtp_client + * + * Written in 2010-2019 by Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + */ + +#include + +static int interrupted, result = 1; +static const char *recip; + +static void +sigint_handler(int sig) +{ + interrupted = 1; +} + +static int +email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len) +{ + /* you could examine email->data here */ + if (buf) + lwsl_notice("%s: %.*s\n", __func__, (int)len, (const char *)buf); + else + lwsl_notice("%s:\n", __func__); + + /* destroy any allocations in email */ + + free((char *)email->payload); + + result = 0; + interrupted = 1; + + return 0; +} + +int main(int argc, const char **argv) +{ + int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + struct lws_context_creation_info info; + struct lws_context *context; + lws_smtp_client_info_t sci; + lws_smtp_client_t *smtpc; + lws_smtp_email_t email; + struct lws_vhost *vh; + const char *p; + + /* the normal lws init */ + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + p = lws_cmdline_option(argc, argv, "-r"); + if (!p) { + lwsl_err("-r is required\n"); + return 1; + } + recip = p; + + lws_set_log_level(logs, NULL); + lwsl_user("LWS API selftest: SMTP client\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = CONTEXT_PORT_NO_LISTEN; + info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + vh = lws_create_vhost(context, &info); + if (!vh) { + lwsl_err("Failed to create first vhost\n"); + goto bail1; + } + + /* create the smtp client */ + + memset(&sci, 0, sizeof(sci)); + sci.data = NULL /* stmp client specific user data */; + sci.abs = lws_abstract_get_by_name("raw_skt"); + sci.vh = vh; + lws_strncpy(sci.ip, "127.0.0.1", sizeof(sci.ip)); + lws_strncpy(sci.helo, "lws-test-client", sizeof(sci.helo)); + + smtpc = lws_smtp_client_create(&sci); + if (!smtpc) { + lwsl_err("%s: failed to create SMTP client\n", __func__); + goto bail1; + } + + /* attach an email to it */ + + memset(&email, 0, sizeof(email)); + email.data = NULL /* email specific user data */; + email.email_from = recip; + email.email_to = "andy@warmcat.com"; + email.payload = malloc(2048); + if (!email.payload) { + goto bail1; + } + + lws_snprintf((char *)email.payload, 2048, + "From: noreply@example.com\n" + "To: %s\n" + "Subject: Test email for lws smtp-client\n" + "\n" + "Hello this was an api test for lws smtp-client\n" + "\r\n.\r\n", recip); + email.done = email_sent_or_failed; + + if (lws_smtp_client_add_email(smtpc, &email)) { + lwsl_err("%s: failed to add email\n", __func__); + goto bail; + } + + /* the usual lws event loop */ + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + +bail: + lws_smtp_client_destroy(&smtpc); +bail1: + lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS"); + + lws_context_destroy(context); + + return result; +} diff --git a/plugins/generic-sessions/handlers.c b/plugins/generic-sessions/handlers.c index 7daf257dc..ade66f2c6 100644 --- a/plugins/generic-sessions/handlers.c +++ b/plugins/generic-sessions/handlers.c @@ -432,7 +432,7 @@ lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd, "Hello, %s\n\n" "We received a password reset request from IP %s for this email,\n" "to confirm you want to do that, please click the link below.\n\n", - lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1), + lws_sql_purify(esc, vhd->email_from, sizeof(esc) - 1), lws_sql_purify(esc1, u.username, sizeof(esc1) - 1), lws_sql_purify(esc2, u.email, sizeof(esc2) - 1), lws_sql_purify(esc3, u.username, sizeof(esc3) - 1), @@ -586,7 +586,7 @@ lwsgs_handler_register_form(struct per_vhost_data__gs *vhd, "automated email, you can contact a real person at\n" "%s.\n" "\n.\n", - lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1), + lws_sql_purify(esc, vhd->email_from, sizeof(esc) - 1), lws_sql_purify(esc1, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc1) - 1), lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1), lws_sql_purify(esc3, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc3) - 1), diff --git a/plugins/generic-sessions/private-lwsgs.h b/plugins/generic-sessions/private-lwsgs.h index 23a02986a..0829b479e 100644 --- a/plugins/generic-sessions/private-lwsgs.h +++ b/plugins/generic-sessions/private-lwsgs.h @@ -62,7 +62,7 @@ struct lwsgs_user { }; struct per_vhost_data__gs { - struct lws_email email; + lws_smtp_client_t *smtp_client; struct lwsgs_user u; struct lws_context *context; char session_db[256]; @@ -72,14 +72,14 @@ struct per_vhost_data__gs { char email_title[128]; char email_template[128]; char email_confirm_url[128]; - lwsgw_hash admin_password_sha1; + char email_from[128]; + lwsgw_hash admin_password_sha256; sqlite3 *pdb; int timeout_idle_secs; int timeout_absolute_secs; int timeout_anon_absolute_secs; int timeout_email_secs; time_t last_session_expire; - char email_inited; }; struct per_session_data__gs { diff --git a/plugins/generic-sessions/protocol_generic_sessions.c b/plugins/generic-sessions/protocol_generic_sessions.c index 975659daf..5d965ee05 100644 --- a/plugins/generic-sessions/protocol_generic_sessions.c +++ b/plugins/generic-sessions/protocol_generic_sessions.c @@ -1,7 +1,7 @@ /* * ws protocol handler plugin for "generic sessions" * - * Copyright (C) 2010-2016 Andy Green + * Copyright (C) 2010-2019 Andy Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,7 +50,7 @@ struct lwsgs_fill_args { }; static const struct lws_protocols protocols[]; - +#if 0 static int lwsgs_lookup_callback_email(void *priv, int cols, char **col_val, char **col_name) @@ -92,11 +92,13 @@ lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len) return 0; } +#endif +#if 0 static int -lwsgs_email_cb_sent(struct lws_email *email) +lwsgs_smtp_client_done(struct lws_smtp_email *e, void *buf, size_t len) { - struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data; + struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)e->data; char s[200], esc[50]; /* mark the user as having sent the verification email */ @@ -120,9 +122,10 @@ lwsgs_email_cb_sent(struct lws_email *email) return 0; } - +#endif +#if 0 static int -lwsgs_email_cb_on_next(struct lws_email *email) +lwsgs_smtp_send_pending(struct lws_email *email) { struct per_vhost_data__gs *vhd = lws_container_of(email, struct per_vhost_data__gs, email); @@ -181,7 +184,7 @@ lwsgs_email_cb_on_next(struct lws_email *email) return 0; } - +#endif struct lwsgs_subst_args { @@ -252,6 +255,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, struct lws_session_info *sinfo; char s[LWSGS_EMAIL_CONTENT_SIZE]; unsigned char *p, *start, *end; + lws_smtp_client_info_t sci; sqlite3_stmt *sm; lwsgw_hash sid; const char *cp; @@ -271,15 +275,13 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, vhd->timeout_absolute_secs = 36000; vhd->timeout_anon_absolute_secs = 1200; vhd->timeout_email_secs = 24 * 3600; - strcpy(vhd->email.email_helo, "unconfigured.com"); - strcpy(vhd->email.email_from, "noreply@unconfigured.com"); - strcpy(vhd->email_title, "Registration Email from unconfigured"); - strcpy(vhd->email.email_smtp_ip, "127.0.0.1"); - vhd->email.on_next = lwsgs_email_cb_on_next; - vhd->email.on_get_body = lwsgs_email_cb_get_body; - vhd->email.on_sent = lwsgs_email_cb_sent; - vhd->email.data = (void *)vhd; + memset(&sci, 0, sizeof(sci)); + + strcpy(sci.helo, "unconfigured.com"); + strcpy(sci.ip, "127.0.0.1"); + strcpy(vhd->email_from, "noreply@unconfigured.com"); + strcpy(vhd->email_title, "Registration Email from unconfigured"); pvo = (const struct lws_protocol_vhost_options *)in; while (pvo) { @@ -287,8 +289,8 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, lws_strncpy(vhd->admin_user, pvo->value, sizeof(vhd->admin_user)); if (!strcmp(pvo->name, "admin-password-sha1")) - lws_strncpy(vhd->admin_password_sha1.id, pvo->value, - sizeof(vhd->admin_password_sha1.id)); + lws_strncpy(vhd->admin_password_sha256.id, pvo->value, + sizeof(vhd->admin_password_sha256.id)); if (!strcmp(pvo->name, "session-db")) lws_strncpy(vhd->session_db, pvo->value, sizeof(vhd->session_db)); @@ -296,11 +298,10 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, lws_strncpy(vhd->confounder, pvo->value, sizeof(vhd->confounder)); if (!strcmp(pvo->name, "email-from")) - lws_strncpy(vhd->email.email_from, pvo->value, - sizeof(vhd->email.email_from)); + lws_strncpy(vhd->email_from, pvo->value, + sizeof(vhd->email_from)); if (!strcmp(pvo->name, "email-helo")) - lws_strncpy(vhd->email.email_helo, pvo->value, - sizeof(vhd->email.email_helo)); + lws_strncpy(sci.helo, pvo->value, sizeof(sci.helo)); if (!strcmp(pvo->name, "email-template")) lws_strncpy(vhd->email_template, pvo->value, sizeof(vhd->email_template)); @@ -314,8 +315,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, lws_strncpy(vhd->email_confirm_url, pvo->value, sizeof(vhd->email_confirm_url)); if (!strcmp(pvo->name, "email-server-ip")) - lws_strncpy(vhd->email.email_smtp_ip, pvo->value, - sizeof(vhd->email.email_smtp_ip)); + lws_strncpy(sci.ip, pvo->value, sizeof(sci.ip)); if (!strcmp(pvo->name, "timeout-idle-secs")) vhd->timeout_idle_secs = atoi(pvo->value); @@ -328,11 +328,11 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, pvo = pvo->next; } if (!vhd->admin_user[0] || - !vhd->admin_password_sha1.id[0] || + !vhd->admin_password_sha256.id[0] || !vhd->session_db[0]) { lwsl_err("generic-sessions: " "You must give \"admin-user\", " - "\"admin-password-sha1\", " + "\"admin-password-sha256\", " "and \"session_db\" per-vhost options\n"); return 1; } @@ -401,10 +401,16 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, return 1; } - lws_email_init(&vhd->email, lws_uv_getloop(vhd->context, 0), - LWSGS_EMAIL_CONTENT_SIZE); + sci.data = vhd; + sci.abs = lws_abstract_get_by_name("raw_skt"); + sci.vh = lws_get_vhost(wsi); + + vhd->smtp_client = lws_smtp_client_create(&sci); + if (!vhd->smtp_client) { + lwsl_err("%s: failed to create SMTP client\n", __func__); + return 1; + } - vhd->email_inited = 1; break; case LWS_CALLBACK_PROTOCOL_DESTROY: @@ -413,16 +419,15 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, sqlite3_close(vhd->pdb); vhd->pdb = NULL; } - if (vhd->email_inited) { - lws_email_destroy(&vhd->email); - vhd->email_inited = 0; - } + if (vhd->smtp_client) + lws_smtp_client_destroy(&vhd->smtp_client); break; case LWS_CALLBACK_HTTP_WRITEABLE: if (!pss->check_response) break; - n = lws_write(wsi, (unsigned char *)&pss->check_response_value, 1, LWS_WRITE_HTTP_FINAL); + n = lws_write(wsi, (unsigned char *)&pss->check_response_value, + 1, LWS_WRITE_HTTP_FINAL); if (n != 1) return -1; goto try_to_reuse; @@ -610,7 +615,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, goto reg_done; } /* get the email monitor to take a look */ - lws_email_check(&vhd->email); + lws_smtp_client_kick(vhd->smtp_client); n = FGS_FORGOT_GOOD; goto reg_done; } @@ -632,7 +637,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason, n = FGS_REG_GOOD; /* get the email monitor to take a look */ - lws_email_check(&vhd->email); + lws_smtp_client_kick(vhd->smtp_client); } reg_done: lws_strncpy(pss->onward, lws_spa_get_string(pss->spa, n), diff --git a/plugins/generic-sessions/utils.c b/plugins/generic-sessions/utils.c index e897ed1aa..22a5cb561 100644 --- a/plugins/generic-sessions/utils.c +++ b/plugins/generic-sessions/utils.c @@ -50,7 +50,7 @@ lwsgw_check_admin(struct per_vhost_data__gs *vhd, lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin); sha1_to_lwsgw_hash(hash_bin.bin, &pw_hash); - return !strcmp(vhd->admin_password_sha1.id, pw_hash.id); + return !strcmp(vhd->admin_password_sha256.id, pw_hash.id); } /*