1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

smtp: make abstract

This commit is contained in:
Andy Green 2019-04-21 19:57:19 +01:00
parent ae6346db64
commit 8d473ad78c
29 changed files with 1630 additions and 624 deletions

View file

@ -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)

View file

@ -529,6 +529,9 @@ struct lws;
#include <libwebsockets/lws-fts.h>
#include <libwebsockets/lws-diskcache.h>
#include <libwebsockets/abstract/transports.h>
#include <libwebsockets/abstract/smtp.h>
#if defined(LWS_WITH_TLS)
#if defined(LWS_WITH_MBEDTLS)

View file

@ -0,0 +1,164 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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
//@}

View file

@ -0,0 +1,31 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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);

View file

@ -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,

View file

@ -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
//@}
///@}

52
lib/abstract/abstract.c Normal file
View file

@ -0,0 +1,52 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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>
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;
}

68
lib/abstract/private.h Normal file
View file

@ -0,0 +1,68 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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;

View file

@ -0,0 +1,51 @@
/*
* libwebsockets lib/abstruct/smtp/private.h
*
* Copyright (C) 2019 Andy Green <andy@warmcat.com>
*
* 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;

395
lib/abstract/smtp/smtp.c Normal file
View file

@ -0,0 +1,395 @@
/*
* Abstract SMTP support for libwebsockets
*
* Copyright (C) 2016-2019 Andy Green <andy@warmcat.com>
*
* 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);
}

View file

@ -0,0 +1,309 @@
/*
* libwebsockets lib/abstruct/transports/raw-skt.c
*
* Copyright (C) 2019 Andy Green <andy@warmcat.com>
*
* 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 */
};

View file

@ -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))

View file

@ -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

View file

@ -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;
}

View file

@ -1,242 +0,0 @@
/*
* SMTP support for libwebsockets
*
* Copyright (C) 2016-2017 Andy Green <andy@warmcat.com>
*
* 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);
}

View file

@ -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)
/**

View file

@ -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)) {

View file

@ -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);

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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 <libwebsockets.h>\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()

View file

@ -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 <loglevel>|Debug verbosity in decimal, eg, -d15
-r <recipient@whatever.com>|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
```

View file

@ -0,0 +1,137 @@
/*
* lws-api-test-smtp_client
*
* Written in 2010-2019 by Andy Green <andy@warmcat.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*/
#include <libwebsockets.h>
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 <recipient email> 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;
}

View file

@ -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),

View file

@ -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 {

View file

@ -1,7 +1,7 @@
/*
* ws protocol handler plugin for "generic sessions"
*
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
* Copyright (C) 2010-2019 Andy Green <andy@warmcat.com>
*
* 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),

View file

@ -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);
}
/*