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

abstract: existing connection compare

This commit is contained in:
Andy Green 2019-07-05 09:38:32 +01:00
parent fc295b7959
commit 5013162b1e
19 changed files with 1001 additions and 481 deletions

View file

@ -1447,7 +1447,9 @@ endif()
if (LWS_WITH_SMTP)
list(APPEND SOURCES
lib/abstract/protocols/smtp/smtp.c)
lib/abstract/protocols/smtp/smtp.c
lib/abstract/protocols/smtp/smtp-sequencer.c
)
endif()
if (LWS_WITH_RANGES)

View file

@ -1,4 +1,4 @@
# `lws_sequencer_t` introduction
# `struct lws_sequencer` introduction
Often a single network action like a client GET is just part of a
larger series of actions, perhaps involving different connections.
@ -10,7 +10,7 @@ loop thread.
![lws_sequencer](/doc-assets/lws_sequencer.svg)
`lws_sequencer_t` provides a generic way to stage multi-step
`struct lws_sequencer` provides a generic way to stage multi-step
operations from inside the event loop. Because it participates
in the event loop similar to a wsi, it always operates from the
service thread context and can access structures that share the
@ -48,7 +48,7 @@ only sends the message. This allows the timeout to be used to, eg, wait
out a retry cooloff period and then start the retry when the
`LWSSEQ_TIMED_OUT` is received, according to the state of the sequencer.
## Creating an `lws_sequencer_t`
## Creating an `struct lws_sequencer`
```
typedef struct lws_seq_info {
@ -63,7 +63,7 @@ typedef struct lws_seq_info {
```
```
lws_sequencer_t *
struct lws_sequencer *
lws_sequencer_create(lws_seq_info_t *info);
```
@ -77,7 +77,7 @@ typedef int (*lws_seq_event_cb)(struct lws_sequencer *seq, void *user_data,
lws_seq_events_t event, void *data);
```
`lws_sequencer_t` objects are private to lws and opaque to the user. A small
`struct lws_sequencer` objects are private to lws and opaque to the user. A small
set of apis lets you perform operations on the pointer returned by the
create api.
@ -95,7 +95,7 @@ callback from inside the event loop at a rate of one per event loop wait.
## Destroying sequencers
`lws_sequencer_t` objects are cleaned up during context destruction if they are
`struct lws_sequencer` objects are cleaned up during context destruction if they are
still around.
Normally the sequencer callback receives a queued message that

View file

@ -42,8 +42,7 @@ typedef struct lws_token_map {
/*
* The indvidual protocols and transports define their own name_index-es which
* are meaningful to them. Define index 0 globally as the end of an array of
* them, and separate the ones used for protocols and transport so we can
* sanity check they are at least in the correct category.
* them, and provide bases so user protocol and transport ones don't overlap.
*/
enum {
@ -56,6 +55,7 @@ enum {
struct lws_abs_transport;
struct lws_abs_protocol;
typedef struct lws_abs lws_abs_t;
LWS_VISIBLE LWS_EXTERN const lws_token_map_t *
lws_abs_get_token(const lws_token_map_t *token_map, short name_index);
@ -67,28 +67,40 @@ lws_abs_get_token(const lws_token_map_t *token_map, short name_index);
typedef void lws_abs_transport_inst_t;
typedef void lws_abs_protocol_inst_t;
typedef struct lws_abs {
void *user;
struct lws_vhost *vh;
/**
* lws_abstract_alloc() - allocate and configure an lws_abs_t
*
* \param vhost: the struct lws_vhost to bind to
* \param user: opaque user pointer
* \param abstract_path: "protocol.transport" names
* \param ap_tokens: tokens for protocol options
* \param at_tokens: tokens for transport
* \param seq: optional sequencer we should bind to, or NULL
* \param opaque_user_data: data given in sequencer callback, if any
*
* Returns an allocated lws_abs_t pointer set up with the other arguments.
*
* Doesn't create a connection instance, just allocates the lws_abs_t and
* sets it up with the arguments.
*
* Returns NULL is there's any problem.
*/
LWS_VISIBLE LWS_EXTERN lws_abs_t *
lws_abstract_alloc(struct lws_vhost *vhost, void *user,
const char *abstract_path, const lws_token_map_t *ap_tokens,
const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
void *opaque_user_data);
const struct lws_abs_protocol *ap;
const lws_token_map_t *ap_tokens;
const struct lws_abs_transport *at;
const lws_token_map_t *at_tokens;
/**
* lws_abstract_free() - free an allocated lws_abs_t
*
* \param pabs: pointer to the lws_abs_t * to free
*
* Frees and sets the pointer to NULL.
*/
struct lws_sequencer *seq;
void *opaque_user_data;
/*
* These are filled in by lws_abs_bind_and_create_instance() in the
* instance copy. They do not need to be set when creating the struct
* for use by lws_abs_bind_and_create_instance()
*/
struct lws_dll2 abstract_instances;
lws_abs_transport_inst_t *ati;
lws_abs_protocol_inst_t *api;
} lws_abs_t;
LWS_VISIBLE LWS_EXTERN void
lws_abstract_free(lws_abs_t **pabs);
/**
* lws_abs_bind_and_create_instance - use an abstract protocol and transport

View file

@ -22,20 +22,51 @@
* IN THE SOFTWARE.
*/
/*
* Information about how this protocol handles multiple use of connections.
*
* .flags of 0 indicates each connection must start with a fresh transport.
*
* Flags can be used to indicate the protocol itself supports different
* kinds of multiple use. However the actual use or not of these may depend on
* negotiation with the remote peer.
*
* LWS_AP_FLAG_PIPELINE_TRANSACTIONS: other instances can be queued on one
* with an existing connection and get a
* chance to "hot take over" the existing
* transport in turn, like h1 keepalive
* pipelining
*
* LWS_AP_FLAG_MUXABLE_STREAM: an existing connection can absorb more child
* connections and mux them as separate child
* streams ongoing, like h2
*/
enum {
LWS_AP_FLAG_PIPELINE_TRANSACTIONS = (1 << 0),
LWS_AP_FLAG_MUXABLE_STREAM = (1 << 1),
};
typedef struct lws_abs_protocol {
const char *name;
int alloc;
int flags;
int (*create)(const struct lws_abs *ai);
void (*destroy)(lws_abs_protocol_inst_t **d);
int (*create)(const struct lws_abs *ai);
void (*destroy)(lws_abs_protocol_inst_t **d);
int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
/* events the transport invokes (handled by abstract protocol) */
int (*accept)(lws_abs_protocol_inst_t *d);
int (*rx)(lws_abs_protocol_inst_t *d, uint8_t *buf, size_t len);
int (*writeable)(lws_abs_protocol_inst_t *d, size_t budget);
int (*closed)(lws_abs_protocol_inst_t *d);
int (*heartbeat)(lws_abs_protocol_inst_t *d);
int (*accept)(lws_abs_protocol_inst_t *d);
int (*rx)(lws_abs_protocol_inst_t *d, const uint8_t *b, size_t l);
int (*writeable)(lws_abs_protocol_inst_t *d, size_t budget);
int (*closed)(lws_abs_protocol_inst_t *d);
int (*heartbeat)(lws_abs_protocol_inst_t *d);
/* as parent, we get a notification a new child / queue entry
* bound to us... this is the parent lws_abs_t as arg */
int (*child_bind)(lws_abs_t *abs);
} lws_abs_protocol_t;
/**
@ -54,3 +85,4 @@ lws_abs_protocol_get_by_name(const char *name);
*/
#include <libwebsockets/abstract/protocols/smtp.h>

View file

@ -33,65 +33,57 @@
* 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.
* You can either use the abstract protocol layer directly, or instead use the
* provided smtp sequencer... this takes care of creating the protocol
* connections, and provides and email queue and retry management.
*/
//@{
#if defined(LWS_WITH_SMTP)
enum {
LTMI_PSMTP_V_HELO = LTMI_PROTOCOL_BASE, /* u.value */
LTMI_PSMTP_LV_RETRY_INTERVAL, /* u.lvalue */
LTMI_PSMTP_LV_DELIVERY_TIMEOUT, /* u.lvalue */
LTMI_PSMTP_LV_EMAIL_QUEUE_MAX, /* u.lvalue */
LTMI_PSMTP_LV_MAX_CONTENT_SIZE, /* u.lvalue */
LTMI_PSMTP_V_LWS_SMTP_EMAIL_T, /* u.value */
};
typedef struct lws_smtp_client lws_smtp_client_t;
typedef struct lws_abs lws_abs_t;
enum {
LWS_SMTP_DISPOSITION_SENT,
LWS_SMTP_DISPOSITION_FAILED,
LWS_SMTP_DISPOSITION_FAILED_DESTROY
};
typedef struct lws_smtp_email {
struct lws_dll2 list;
typedef struct lws_smtp_sequencer_args {
const char helo[32];
struct lws_vhost *vhost;
time_t retry_interval;
time_t delivery_timeout;
size_t email_queue_max;
size_t max_content_size;
} lws_smtp_sequencer_args_t;
void *data;
void *extra;
typedef struct lws_smtp_sequencer lws_smtp_sequencer_t;
typedef struct lws_smtp_email lws_smtp_email_t;
time_t added;
time_t last_try;
LWS_VISIBLE LWS_EXTERN lws_smtp_sequencer_t *
lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args);
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_VISIBLE LWS_EXTERN void
lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s);
typedef int (*lws_smtp_cb_t)(void *e, void *d, int disp, const void *b, size_t l);
typedef struct lws_smtp_email lws_smtp_email_t;
/**
* lws_smtp_client_alloc_email_helper() - Allocates and inits an email object
* lws_smtpc_add_email() - Allocates and queues an email object
*
* \param s: smtp sequencer to queue on
* \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
* \param data: opaque user data returned in the done callback
* \param done: callback called when the email send succeeded or failed
*
* Allocates an email object and copies the payload, sender and recipient into
* it and initializes it. Returns NULL if OOM, otherwise the allocated email
@ -103,33 +95,21 @@ typedef struct lws_smtp_email {
* 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_VISIBLE LWS_EXTERN int
lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
size_t payload_len, const char *sender,
const char *recipient, void *data, lws_smtp_cb_t done);
/**
* lws_smtp_client_add_email() - Add email to the list of ones being sent
* lws_smtpc_free_email() - Add email to the list of ones being sent
*
* \param instance: smtp client + transport
* \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_abs_t *instance, lws_smtp_email_t *e);
lws_smtpc_free_email(lws_smtp_email_t *e);
/**
* lws_smtp_client_kick() - Request check for new email
*
* \param instance: instance to kick
*
* Gives smtp client a chance to move things on
*/
LWS_VISIBLE LWS_EXTERN void
lws_smtp_client_kick(lws_abs_t *instance);
#endif
//@}

View file

@ -30,9 +30,12 @@ typedef struct lws_abs_transport {
const char *name;
int alloc;
int (*create)(struct lws_abs *abs);
int (*create)(lws_abs_t *abs);
void (*destroy)(lws_abs_transport_inst_t **d);
/* check if the transport settings for these connections are the same */
int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
/* events the abstract protocol invokes (handled by transport) */
int (*tx)(lws_abs_transport_inst_t *d, uint8_t *buf, size_t len);

View file

@ -30,18 +30,23 @@ extern const lws_abs_transport_t lws_abs_transport_cli_raw_skt,
#if defined(LWS_WITH_SMTP)
extern const lws_abs_protocol_t lws_abs_protocol_smtp;
#endif
#if defined(LWS_WITH_MQTT)
extern const lws_abs_protocol_t lws_abs_protocol_mqttc;
#endif
static const lws_abs_transport_t * const available_abs_transports[] = {
&lws_abs_transport_cli_raw_skt,
&lws_abs_transport_cli_unit_test,
};
/* HACK: microsoft compiler can't handle zero length array definition */
#if defined(LWS_WITH_SMTP)
#if defined(LWS_WITH_ABSTRACT)
static const lws_abs_protocol_t * const available_abs_protocols[] = {
#if defined(LWS_WITH_SMTP)
&lws_abs_protocol_smtp,
#endif
#if defined(LWS_WITH_MQTT)
&lws_abs_protocol_mqttc,
#endif
};
#endif
@ -62,7 +67,7 @@ lws_abs_transport_get_by_name(const char *name)
const lws_abs_protocol_t *
lws_abs_protocol_get_by_name(const char *name)
{
#if defined(LWS_WITH_SMTP)
#if defined(LWS_WITH_ABSTRACT)
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_protocols); n++)
@ -89,20 +94,59 @@ lws_abs_get_token(const lws_token_map_t *token_map, short name_index)
return NULL;
}
void
lws_abs_destroy_instance(lws_abs_t **ai)
static int
lws_abstract_compare_connection(lws_abs_t *abs1, lws_abs_t *abs2)
{
lws_abs_t *a = *ai;
/* it has to be using the same protocol */
if (abs1->ap != abs2->ap)
return 1;
if (a->api)
a->ap->destroy(&a->api);
if (a->ati)
a->at->destroy(&a->ati);
/* protocol has to allow some kind of binding */
if (!abs1->ap->flags)
return 1;
lws_dll2_remove(&a->abstract_instances);
/* it has to be using the same transport */
if (abs1->at != abs2->at)
return 1;
*ai = NULL;
free(a);
/*
* The transport must feel the endpoint and conditions in use match the
* requested endpoint and conditions... and the transport type must be
* willing to allow it
*/
if (abs1->at->compare(abs1, abs2))
return 1;
/*
* The protocol must feel they are in compatible modes if any
* (and the protocol type must be willing to allow it)
*/
if (abs1->ap->compare(abs1, abs2))
return 1;
/*
* If no objection by now, we can say there's already a comparable
* connection and both the protocol and transport feel we can make
* use of it.
*/
return 0;
}
static int
find_compatible(struct lws_dll2 *d, void *user)
{
lws_abs_t *ai1 = (lws_abs_t *)user,
*ai2 = lws_container_of(d, lws_abs_t, abstract_instances);
if (!lws_abstract_compare_connection(ai1, ai2)) {
/* we can bind to it */
lws_dll2_add_tail(&ai1->bound, &ai2->children_owner);
return 1;
}
return 0;
}
lws_abs_t *
@ -110,6 +154,7 @@ lws_abs_bind_and_create_instance(const lws_abs_t *abs)
{
size_t size = sizeof(lws_abs_t) + abs->ap->alloc + abs->at->alloc;
lws_abs_t *ai;
int n;
/*
* since we know we will allocate the lws_abs_t, the protocol's
@ -124,10 +169,27 @@ lws_abs_bind_and_create_instance(const lws_abs_t *abs)
ai->ati = NULL;
ai->api = (char *)ai + sizeof(lws_abs_t);
if (ai->ap->create(ai)) {
ai->api = NULL;
goto bail;
}
if (!ai->ap->flags) /* protocol only understands single connections */
goto fresh;
lws_vhost_lock(ai->vh); /* ----------------------------------- vh { */
/*
* Let's have a look for any already-connected transport we can use
*/
n = lws_dll2_foreach_safe(&ai->vh->abstract_instances_owner, ai,
find_compatible);
lws_vhost_unlock(ai->vh); /* } vh --------------------------------- */
if (n)
goto vh_list_add;
/* there's no existing connection doing what we want */
fresh:
ai->ati = (char *)ai->api + abs->ap->alloc;
if (ai->at->create(ai)) {
@ -135,12 +197,36 @@ lws_abs_bind_and_create_instance(const lws_abs_t *abs)
goto bail;
}
vh_list_add:
/* add us to the vhost's dll2 of instances */
lws_dll2_clear(&ai->abstract_instances);
lws_dll2_add_head(&ai->abstract_instances,
&ai->vh->abstract_instances_owner);
if (ai->ap->create(ai)) {
ai->api = NULL;
goto bail;
}
if (ai->bound.owner) { /* we are a piggybacker */
lws_abs_t *ai2 = lws_container_of(ai->bound.owner, lws_abs_t,
children_owner);
/*
* Provide an 'event' in the parent context to start handling
* the bind if it's otherwise idle. We give the parent abs
* because we don't know if we're "next" or whatever. Just that
* a child joined him and he should look into his child
* situation in case he was waiting for one to appear.
*/
if (ai2->ap->child_bind(ai2)) {
lwsl_info("%s: anticpated child bind fail\n", __func__);
lws_dll2_remove(&ai->bound);
goto bail;
}
}
return ai;
bail:
@ -148,3 +234,122 @@ bail:
return NULL;
}
/*
* We get called to clean up each child that was still bound to a parent
* at the time the parent is getting destroyed.
*/
static void
__lws_abs_destroy_instance2(lws_abs_t **ai)
{
lws_abs_t *a = *ai;
if (a->api)
a->ap->destroy(&a->api);
if (a->ati)
a->at->destroy(&a->ati);
lws_dll2_remove(&a->abstract_instances);
*ai = NULL;
free(a);
}
static int
__reap_children(struct lws_dll2 *d, void *user)
{
lws_abs_t *ac = lws_container_of(d, lws_abs_t, bound);
lws_dll2_foreach_safe(&ac->children_owner, NULL, __reap_children);
/* then destroy ourselves */
__lws_abs_destroy_instance2(&ac);
return 0;
}
void
lws_abs_destroy_instance(lws_abs_t **ai)
{
lws_abs_t *a = *ai;
/* destroy child instances that are bound to us first... */
lws_vhost_lock(a->vh); /* ----------------------------------- vh { */
lws_dll2_foreach_safe(&a->children_owner, NULL, __reap_children);
/* ...then destroy ourselves */
__lws_abs_destroy_instance2(ai);
lws_vhost_unlock(a->vh); /* } vh --------------------------------- */
}
lws_abs_t *
lws_abstract_alloc(struct lws_vhost *vhost, void *user,
const char *abstract_path, const lws_token_map_t *ap_tokens,
const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
void *opaque_user_data)
{
lws_abs_t *abs = lws_zalloc(sizeof(*abs), __func__);
struct lws_tokenize ts;
lws_tokenize_elem e;
char tmp[30];
if (!abs)
return NULL;
lws_tokenize_init(&ts, abstract_path, LWS_TOKENIZE_F_MINUS_NONTERM);
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_TOKEN)
goto abs_path_problem;
if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
goto abs_path_problem;
abs->ap = lws_abs_protocol_get_by_name(tmp);
if (!abs->ap)
goto abs_path_problem;
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_DELIMITER)
goto abs_path_problem;
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_TOKEN)
goto abs_path_problem;
if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
goto abs_path_problem;
abs->at = lws_abs_transport_get_by_name(tmp);
if (!abs->at)
goto abs_path_problem;
abs->vh = vhost;
abs->ap_tokens = ap_tokens;
abs->at_tokens = at_tokens;
abs->seq = seq;
abs->opaque_user_data = opaque_user_data;
lwsl_info("%s: allocated %s\n", __func__, abstract_path);
return abs;
abs_path_problem:
lwsl_err("%s: bad abs path '%s'\n", __func__, abstract_path);
lws_free_set_NULL(abs);
return NULL;
}
void
lws_abstract_free(lws_abs_t **pabs)
{
if (*pabs)
lws_free_set_NULL(*pabs);
}

View file

@ -22,4 +22,34 @@
* IN THE SOFTWARE.
*/
#if !defined(__PRIVATE_LIB_ABSTRACT_H__)
#define __PRIVATE_LIB_ABSTRACT_H__
typedef struct lws_token_map lws_token_map_t;
typedef void lws_abs_transport_inst_t;
typedef void lws_abs_protocol_inst_t;
typedef struct lws_abs {
void *user;
struct lws_vhost *vh;
const struct lws_abs_protocol *ap;
const lws_token_map_t *ap_tokens;
const struct lws_abs_transport *at;
const lws_token_map_t *at_tokens;
struct lws_sequencer *seq;
void *opaque_user_data;
/* vh lock */
struct lws_dll2_owner children_owner; /* our children / queue */
/* vh lock */
struct lws_dll2 bound; /* parent or encapsulator */
/* vh lock */
struct lws_dll2 abstract_instances;
lws_abs_transport_inst_t *ati;
lws_abs_protocol_inst_t *api;
} lws_abs_t;
#endif

View file

@ -0,0 +1,24 @@
#define LWS_SMTP_MAX_EMAIL_LEN 32
/*
* These are allocated on to the heap with an over-allocation to hold the
* payload at the end
*/
typedef struct lws_smtp_email {
struct lws_dll2 list;
void *data;
char from[LWS_SMTP_MAX_EMAIL_LEN];
char to[LWS_SMTP_MAX_EMAIL_LEN];
time_t added;
time_t last_try;
lws_smtp_cb_t done;
int tries;
/* email payload follows */
} lws_smtp_email_t;

View file

@ -0,0 +1,320 @@
/*
* Abstract SMTP support for libwebsockets - SMTP sequencer
*
* 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
*
* This sequencer sits above the abstract protocol, and manages queueing,
* retrying mail transmission, and retry limits.
*
* Having the sequencer means that, eg, we can manage retries after complete
* connection failure.
*
* Connections to the smtp server are serialized
*/
#include "private-lib-core.h"
#include "private-lib-abstract-protocols-smtp.h"
typedef enum {
LSMTPSS_DISCONNECTED,
LSMTPSS_CONNECTING,
LSMTPSS_CONNECTED,
LSMTPSS_BUSY,
} smtpss_connstate_t;
typedef struct lws_smtp_sequencer {
struct lws_dll2_owner emails_owner; /* email queue */
lws_abs_t *abs, *instance;
lws_smtp_sequencer_args_t args;
struct lws_sequencer *seq;
smtpss_connstate_t connstate;
time_t email_connect_started;
/* holds the HELO for the smtp protocol to consume */
lws_token_map_t apt[3];
} lws_smtp_sequencer_t;
/* sequencer messages specific to this sequencer */
enum {
SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
SEQ_MSG_CLIENT_DONE,
};
/*
* We're going to bind to the raw-skt transport, so tell that what we want it
* to connect to
*/
static const lws_token_map_t smtp_rs_transport_tokens[] = {
{
.u = { .value = "127.0.0.1" },
.name_index = LTMI_PEER_V_DNS_ADDRESS,
}, {
.u = { .lvalue = 25 },
.name_index = LTMI_PEER_LV_PORT,
}, {
}
};
static void
lws_smtpc_kick_internal(lws_smtp_sequencer_t *s)
{
lws_smtp_email_t *e;
lws_dll2_t *d;
char buf[64];
int n;
lws_dll2_t *pd2;
pd2 = lws_dll2_get_head(&s->emails_owner);
if (!pd2)
return;
e = lws_container_of(pd2, lws_smtp_email_t, list);
if (!e)
return;
/* Is there something to do? If so, we need a connection... */
if (s->connstate == LSMTPSS_DISCONNECTED) {
s->apt[0].u.value = s->args.helo;
s->apt[0].name_index = LTMI_PSMTP_V_HELO;
s->apt[1].u.value = (void *)e;
s->apt[1].name_index = LTMI_PSMTP_V_LWS_SMTP_EMAIL_T;
/*
* create and connect the smtp protocol + transport
*/
s->abs = lws_abstract_alloc(s->args.vhost, NULL, "smtp.raw_skt",
s->apt, smtp_rs_transport_tokens,
s->seq, NULL);
if (!s->abs)
return;
s->instance = lws_abs_bind_and_create_instance(s->abs);
if (!s->instance) {
lws_abstract_free(&s->abs);
lwsl_err("%s: failed to create SMTP client\n", __func__);
goto bail1;
}
s->connstate = LSMTPSS_CONNECTING;
lws_sequencer_timeout(s->seq, 10);
return;
}
/* ask the transport if we have a connection to the server ongoing */
if (s->abs->at->state(s->abs->ati)) {
/*
* there's a connection, it could be still trying to connect
* or established
*/
s->abs->at->ask_for_writeable(s->abs->ati);
return;
}
/* there's no existing connection */
lws_smtpc_state_transition(c, LGSSMTP_CONNECTING);
if (s->abs->at->client_conn(s->abs)) {
lwsl_err("%s: failed to connect\n", __func__);
return;
}
e->tries++;
e->last_try = lws_now_secs();
}
/*
* The callback we get from the smtp protocol... we use this to drive
* decisions about destroy email, retry and fail.
*
* Sequencer will handle it via the event loop.
*/
static int
email_result(void *e, void *d, int disp, void *b, size_t l)
{
lws_smtp_sequencer_t *s = (lws_smtp_sequencer_t *)d;
lws_sequencer_event(s->seq, LWSSEQ_USER_BASE + disp, e);
return 0;
}
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);
lws_dll2_remove(d);
lws_free(e);
return 0;
}
static lws_seq_cb_return_t
smtp_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
{
struct lws_smtp_sequencer_t *s = (struct lws_smtp_sequencer_t *)user;
switch ((int)event) {
case LWSSEQ_CREATED: /* our sequencer just got started */
lwsl_notice("%s: %s: created\n", __func__,
lws_sequencer_name(seq));
s->connstate = LSMTPSS_DISCONNECTED;
s->state = 0; /* first thing we'll do is the first url */
goto step;
case LWSSEQ_DESTROYED:
lws_dll2_foreach_safe(&s->pending_owner, NULL, cleanup);
break;
case LWSSEQ_TIMED_OUT:
lwsl_notice("%s: LWSSEQ_TIMED_OUT\n", __func__);
break;
case LWSSEQ_USER_BASE + LWS_SMTP_DISPOSITION_SENT:
lws_smtpc_free_email(data);
break;
case LWSSEQ_WSI_CONNECTED:
s->connstate = LSMTPSS_CONNECTED;
lws_smtpc_kick_internal(s);
break;
case LWSSEQ_WSI_CONN_FAIL:
case LWSSEQ_WSI_CONN_CLOSE:
s->connstate = LSMTPSS_DISCONNECTED;
lws_smtpc_kick_internal(s);
break;
case SEQ_MSG_SENT:
break;
default:
break;
}
return LWSSEQ_RET_CONTINUE;
}
/*
* Creates an lws_sequencer to manage the test sequence
*/
lws_smtp_sequencer_t *
lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args)
{
lws_smtp_sequencer_t *s;
struct lws_sequencer *seq;
/*
* Create a sequencer in the event loop to manage the SMTP queue
*/
seq = lws_sequencer_create(args->vhost->context, 0,
sizeof(lws_smtp_sequencer_t), (void **)&s,
smtp_sequencer_cb, "smtp-seq");
if (!seq) {
lwsl_err("%s: unable to create sequencer\n", __func__);
return NULL;
}
s->abs = *args->abs;
s->args = *args;
s->seq = seq;
/* set defaults in our copy of the args */
if (!s->args.helo[0])
strcpy(s->args.helo, "default-helo");
if (!s->args.email_queue_max)
s->args.email_queue_max = 8;
if (!s->args.retry_interval)
s->args.retry_interval = 15 * 60;
if (!s->args.delivery_timeout)
s->args.delivery_timeout = 12 * 60 * 60;
return s;
}
void
lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s)
{
/* sequencer destruction destroys all assets */
lws_sequencer_destroy(&s->seq);
}
int
lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
size_t payload_len, const char *sender,
const char *recipient, void *data, lws_smtp_cb_t done)
{
lws_smtp_email_t *e;
if (s->emails_owner.count > s->args.email_queue_max) {
lwsl_err("%s: email queue at limit of %d\n", __func__,
(int)s->args.email_queue_max);
return 1;
}
if (!done)
return 1;
e = malloc(sizeof(*e) + payload_len + 1);
if (!e)
return 1;
memset(e, 0, sizeof(*e));
e->data = data;
e->done = done;
lws_strncpy(e->from, sender, sizeof(e->from));
lws_strncpy(e->to, recipient, sizeof(e->to));
memcpy((char *)&e[1], payload, payload_len + 1);
e->added = lws_now_secs();
e->last_try = 0;
e->tries = 0;
lws_dll2_clear(&e->list);
lws_dll2_add_tail(&e->list, &s->emails_owner);
lws_smtpc_kick_internal(s);
return 0;
}

View file

@ -30,33 +30,36 @@ 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 */
/* (server sends greeting) */
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 */
/*
* (server sends, eg, "250 Ok: queued as 12345")
* at this point we can return to LGSSMTP_SENT_HELO and send a
* new email, or continue below to QUIT, or just wait
*/
LGSSMTP_SENT_QUIT, /**< sent the session quit */
/* (server sends, eg, "221 Bye" and closes the connection) */
} lwsgs_smtp_states_t;
/** struct lws_email - abstract context for performing SMTP operations */
typedef struct lws_smtp_client {
struct lws_dll2_owner pending_owner;
/** abstract protocol instance data */
const struct lws_abs *abs;
typedef struct lws_smtp_client_protocol {
const struct lws_abs *abs;
lwsgs_smtp_states_t estate;
const char *helo;
lws_smtp_email_t *e; /* the email we are trying to send */
const char *helo;
lwsgs_smtp_states_t estate;
time_t email_connect_started;
time_t retry_interval;
time_t delivery_timeout;
size_t email_queue_max;
size_t max_content_size;
unsigned char send_pending:1;
} lws_smtp_client_t;
unsigned char send_pending:1;
} lws_smtpcp_t;
static const short retcodes[] = {
0, /* idle */
@ -71,80 +74,71 @@ static const short retcodes[] = {
};
static void
lws_smtp_client_state_transition(lws_smtp_client_t *c, lwsgs_smtp_states_t s)
lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s)
{
lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
c->estate = s;
}
static void
lws_smtp_client_kick_internal(lws_smtp_client_t *c)
static lws_smtp_email_t *
lws_smtpc_get_email(lws_smtpcp_t *c)
{
lws_smtp_email_t *e;
const lws_token_map_t *tm;
/* ... the email we want to send */
tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T);
if (!tm) {
assert(0);
return NULL;
}
return (lws_smtp_email_t *)tm->u.value;
}
/*
* Called when something happened so that we know now the final disposition of
* the email send attempt, for good or ill.
*
* Inform the owner via the done callback and set up the next queued one if any.
*
* Returns nonzero if we queued a new one
*/
static int
lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf,
size_t len)
{
lws_smtpcp_t *ch;
lws_abs_t *ach;
lws_dll2_t *d;
char buf[64];
int n;
if (c->estate != LGSSMTP_IDLE)
return;
lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
/* is there something to do? */
/* lifetime of the email object is handled by done callback */
c->e->done(c->e, c->e->data, disp, buf, len);
c->e = NULL;
again:
d = lws_dll2_get_head(&c->pending_owner);
/* this may not be the time to try to send anything else... */
if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY)
return 0;
/* ... otherwise... do we have another queued? */
d = lws_dll2_get_tail(&c->abs->children_owner);
if (!d)
return;
return 0;
e = lws_container_of(d, lws_smtp_email_t, list);
ach = lws_container_of(d, lws_abs_t, bound);
ch = (lws_smtpcp_t *)ach->api;
/* do we need to time out this guy? */
c->e = lws_smtpc_get_email(ch);
if ((time_t)lws_now_secs() - e->added > (time_t)c->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);
/* since we took it on, remove it from the queue */
lws_dll2_remove(d);
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->retry_interval) {
/* no... send him to the tail */
lws_dll2_remove(&e->list);
lws_dll2_add_tail(&e->list, &c->pending_owner);
return;
}
/* ask the transport if we have a connection to the server ongoing */
if (c->abs->at->state(c->abs->ati)) {
/*
* there's a connection, it could be still trying to connect
* or established
*/
c->abs->at->ask_for_writeable(c->abs->ati);
return;
}
/* there's no existing connection */
lws_smtp_client_state_transition(c, LGSSMTP_CONNECTING);
if (c->abs->at->client_conn(c->abs)) {
lwsl_err("%s: failed to connect\n", __func__);
return;
}
e->tries++;
e->last_try = lws_now_secs();
return 1;
}
/*
@ -152,53 +146,82 @@ again:
*/
static int
lws_smtp_client_abs_accept(lws_abs_protocol_inst_t *api)
lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)api;
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
lws_smtp_client_state_transition(c, LGSSMTP_CONNECTED);
/* we have become connected in the tcp sense */
lws_smtpc_state_transition(c, LGSSMTP_CONNECTED);
/*
* From the accept(), the next thing that should happen is the SMTP
* server sends its greeting like "220 smtp2.example.com ESMTP Postfix",
* we'll hear about it in the rx callback, or time out
*/
c->abs->at->set_timeout(c->abs->ati,
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3);
return 0;
}
static int
lws_smtp_client_abs_rx(lws_abs_protocol_inst_t *api, uint8_t *buf, size_t len)
lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)api;
lws_smtp_email_t *e;
lws_dll2_t *pd2;
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
char at[5];
int n;
pd2 = lws_dll2_get_head(&c->pending_owner);
if (!pd2)
return 0;
c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0);
e = lws_container_of(pd2, lws_smtp_email_t, list);
if (!e)
return 0;
lws_strncpy(at, (const char *)buf, sizeof(at));
n = atoi(at);
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);
switch (c->estate) {
case LGSSMTP_CONNECTED:
if (n != 220) {
/*
* The server did not properly greet us... we can't
* even get started, so fail the transport connection
* (and anything queued on it)
*/
lwsl_err("%s: server: %.*s\n", __func__, (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_internal(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;
}
break;
case LGSSMTP_SENT_BODY:
/*
* We finished one way or another... let's prepare to send a
* new one... or wait until server hangs up on us
*/
if (!lws_smtpc_email_disposition(c,
n == 250 ? LWS_SMTP_DISPOSITION_SENT :
LWS_SMTP_DISPOSITION_FAILED,
"destroyed", 0))
return 0; /* become idle */
break; /* ask to send */
case LGSSMTP_SENT_QUIT:
lwsl_debug("%s: done\n", __func__);
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
return 1;
default:
if (n != retcodes[c->estate]) {
lwsl_notice("%s: bad response: %d (state %d) %.*s\n",
__func__, n, c->estate, (int)len, buf);
lws_smtpc_email_disposition(c,
LWS_SMTP_DISPOSITION_FAILED, buf, len);
return 0;
}
break;
}
c->send_pending = 1;
@ -208,24 +231,13 @@ lws_smtp_client_abs_rx(lws_abs_protocol_inst_t *api, uint8_t *buf, size_t len)
}
static int
lws_smtp_client_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)api;
char b[256 + LWS_PRE], *p = b + LWS_PRE;
lws_smtp_email_t *e;
lws_dll2_t *pd2;
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
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)
if (!c->send_pending || !c->e)
return 0;
c->send_pending = 0;
@ -235,31 +247,37 @@ lws_smtp_client_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
switch (c->estate) {
case LGSSMTP_CONNECTED:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo);
lws_smtp_client_state_transition(c, LGSSMTP_SENT_HELO);
lws_smtpc_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);
c->e->from);
lws_smtpc_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);
"RCPT TO: <%s>\n", c->e->to);
lws_smtpc_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);
lws_smtpc_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);
p = (char *)&c->e[1];
n = strlen(p);
lws_smtpc_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);
lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT);
break;
case LGSSMTP_SENT_QUIT:
return 0;
@ -274,179 +292,88 @@ lws_smtp_client_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
}
static int
lws_smtp_client_abs_closed(lws_abs_protocol_inst_t *api)
lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)api;
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
if (c)
lws_smtp_client_state_transition(c, LGSSMTP_IDLE);
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
return 0;
}
/*
* Creating for initial transport and for piggybacking on another transport
* both get created here the same. But piggybackers have ai->bound attached.
*/
static int
lws_smtp_client_abs_heartbeat(lws_abs_protocol_inst_t *api)
lws_smtpc_create(const lws_abs_t *ai)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)api;
lws_smtp_client_kick_internal(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_abs_t *instance, lws_smtp_email_t *e)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)instance->api;
if (c->pending_owner.count > c->email_queue_max) {
lwsl_err("%s: email queue at limit of %d\n", __func__,
(int)c->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_internal(c);
return 0;
}
void
lws_smtp_client_kick(lws_abs_t *instance)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)instance->api;
lws_smtp_client_kick_internal(c);
}
static int
lws_smtp_client_create(const lws_abs_t *ai)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)ai->api;
const lws_token_map_t *tm;
lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api;
memset(c, 0, sizeof(*c));
c->abs = ai;
c->e = lws_smtpc_get_email(c);
tm = lws_abs_get_token(ai->ap_tokens, LTMI_PSMTP_V_HELO);
if (!tm) {
lwsl_err("%s: LTMI_PSMTP_V_HELO is required\n", __func__);
lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ?
LGSSMTP_CONNECTING : LGSSMTP_IDLE);
return 1;
}
c->helo = tm->u.value;
c->email_queue_max = 8;
c->retry_interval = 15 * 60;
c->delivery_timeout = 12 * 60 * 60;
tm = lws_abs_get_token(ai->ap_tokens, LTMI_PSMTP_LV_EMAIL_QUEUE_MAX);
if (tm)
c->email_queue_max = tm->u.lvalue;
tm = lws_abs_get_token(ai->ap_tokens, LTMI_PSMTP_LV_RETRY_INTERVAL);
if (tm)
c->retry_interval = tm->u.lvalue;
tm = lws_abs_get_token(ai->ap_tokens, LTMI_PSMTP_LV_DELIVERY_TIMEOUT);
if (tm)
c->delivery_timeout = tm->u.lvalue;
lws_smtp_client_state_transition(c, LGSSMTP_IDLE);
return 0;
}
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;
/* If we are initiating the transport, we will get an accept() next...
*
* If we are piggybacking, the parent will get a .child_bind() after
* this to give it a chance to act on us joining (eg, it was completely
* idle and we joined).
*/
return 0;
}
static void
lws_smtp_client_destroy(lws_abs_protocol_inst_t **_c)
lws_smtpc_destroy(lws_abs_protocol_inst_t **_c)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)*_c;
lws_smtpcp_t *c = (lws_smtpcp_t *)*_c;
if (!c)
return;
lws_dll2_foreach_safe(&c->pending_owner, NULL, cleanup);
/*
* We don't free anything because the abstract layer combined our
* allocation with that of the instance, and it will free the whole
* thing after this.
*/
/* so if we are still holding on to c->e, we have failed to send it */
if (c->e)
lws_smtpc_email_disposition(c,
LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0);
*_c = NULL;
}
static int
lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
return 0;
}
static int
lws_smtpc_child_bind(lws_abs_t *abs)
{
return 0;
}
/* events the transport invokes (handled by abstract protocol) */
const lws_abs_protocol_t lws_abs_protocol_smtp = {
.name = "smtp",
.alloc = sizeof(lws_smtp_client_t),
.alloc = sizeof(lws_smtpcp_t),
.flags = LWSABSPR_FLAG_PIPELINE,
.create = lws_smtp_client_create,
.destroy = lws_smtp_client_destroy,
.create = lws_smtpc_create,
.destroy = lws_smtpc_destroy,
.compare = lws_smtpc_compare,
.accept = lws_smtp_client_abs_accept,
.rx = lws_smtp_client_abs_rx,
.writeable = lws_smtp_client_abs_writeable,
.closed = lws_smtp_client_abs_closed,
.heartbeat = lws_smtp_client_abs_heartbeat,
.accept = lws_smtpc_abs_accept,
.rx = lws_smtpc_abs_rx,
.writeable = lws_smtpc_abs_writeable,
.closed = lws_smtpc_abs_closed,
.heartbeat = NULL,
.child_bind = lws_smtpc_child_bind,
};

View file

@ -321,9 +321,9 @@ static void
lws_atcrs_destroy(lws_abs_transport_inst_t **pati)
{
/*
* We don't free anything because the abstract layer combined our
* allocation with that of the instance, and it will free the whole
* thing after this.
* For ourselves, we don't free anything because the abstract layer
* combined our allocation with that of the abs instance, and it will
* free the whole thing after this.
*/
*pati = NULL;
}
@ -349,12 +349,51 @@ lws_atcrs_state(lws_abs_transport_inst_t *ati)
return 1;
}
static int
lws_atcrs_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
const lws_token_map_t *tm1, *tm2;
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_V_DNS_ADDRESS);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_V_DNS_ADDRESS);
/* Address token is mandatory and must match */
if (!tm1 || !tm2 || strcmp(tm1->u.value, tm2->u.value))
return 1;
/* Port token is mandatory and must match */
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_PORT);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_PORT);
if (!tm1 || !tm2 || tm1->u.lvalue != tm2->u.lvalue)
return 1;
/* TLS is optional... */
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_TLS_FLAGS);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_TLS_FLAGS);
/* ... but both must have the same situation with it given or not... */
if (!!tm1 != !!tm2)
return 1;
/* if not using TLS, then that's enough to call it */
if (!tm1)
return 0;
/* ...and if there are tls flags, both must have the same tls flags */
if (tm1->u.lvalue != tm2->u.lvalue)
return 1;
/* ... and both must use the same client tls ctx / vhost */
return abs1->vh != abs2->vh;
}
const lws_abs_transport_t lws_abs_transport_cli_raw_skt = {
.name = "raw_skt",
.alloc = sizeof(abs_raw_skt_priv_t),
.create = lws_atcrs_create,
.destroy = lws_atcrs_destroy,
.compare = lws_atcrs_compare,
.tx = lws_atcrs_tx,
#if !defined(LWS_WITH_CLIENT)

View file

@ -513,12 +513,19 @@ lws_unit_test_result_name(int in)
return dnames[in];
}
static int
lws_atcut_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
return 0;
}
const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
.name = "unit_test",
.alloc = sizeof(abs_unit_test_priv_t),
.create = lws_atcut_create,
.destroy = lws_atcut_destroy,
.compare = lws_atcut_compare,
.tx = lws_atcut_tx,
#if !defined(LWS_WITH_CLIENT)

View file

@ -528,7 +528,7 @@ struct lws_vhost {
const struct lws_protocol_vhost_options *headers;
struct lws_dll2_owner *same_vh_protocol_owner;
struct lws_vhost *no_listener_vhost_list;
struct lws_dll2_owner abstract_instances_owner;
struct lws_dll2_owner abstract_instances_owner; /* vh lock */
#if defined(LWS_WITH_CLIENT)
struct lws_dll2_owner dll_cli_active_conns_owner;

View file

@ -641,6 +641,10 @@ lws_context_destroy2(struct lws_context *context);
#define PRIu64 "llu"
#endif
#if defined(LWS_WITH_ABSTRACT)
#include "private-lib-abstract.h"
#endif
#ifdef __cplusplus
};
#endif

View file

@ -21,7 +21,7 @@ sigint_handler(int sig)
}
static int
email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len)
done_cb(struct lws_smtp_email *email, void *buf, size_t len)
{
/* you could examine email->data here */
if (buf)
@ -39,39 +39,16 @@ email_sent_or_failed(struct lws_smtp_email *email, void *buf, size_t len)
return 0;
}
/*
* We're going to bind to the raw-skt transport, so tell that what we want it
* to connect to
*/
static const lws_token_map_t smtp_raw_skt_transport_tokens[] = {
{
.u = { .value = "127.0.0.1" },
.name_index = LTMI_PEER_V_DNS_ADDRESS,
}, {
.u = { .lvalue = 25 },
.name_index = LTMI_PEER_LV_PORT,
}, {
}
};
static const lws_token_map_t smtp_protocol_tokens[] = {
{
.u = { .value = "lws-test-client" },
.name_index = LTMI_PSMTP_V_HELO,
}, {
}
};
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;
lws_smtp_sequencer_args_t ss_args;
struct lws_context *context;
lws_abs_t abs, *instance;
lws_smtp_email_t email;
lws_smtp_sequencer_t *sseq;
lws_smtp_email_t *email;
struct lws_vhost *vh;
char payload[2048];
const char *p;
/* the normal lws init */
@ -107,56 +84,30 @@ int main(int argc, const char **argv)
goto bail1;
}
/*
* create an smtp client that's hooked up to real sockets
*/
memset(&ss_args, 0, sizeof(ss_args));
ss_args.helo = "lws-abs-smtp-test";
ss_args.vhost = vh;
memset(&abs, 0, sizeof(abs));
abs.vh = vh;
/* select the protocol and bind its tokens */
abs.ap = lws_abs_protocol_get_by_name("smtp");
if (!abs.ap)
goto bail1;
abs.ap_tokens = smtp_protocol_tokens;
/* select the transport and bind its tokens */
abs.at = lws_abs_transport_get_by_name("raw_skt");
if (!abs.at)
goto bail1;
abs.at_tokens = smtp_raw_skt_transport_tokens;
instance = lws_abs_bind_and_create_instance(&abs);
if (!instance) {
lwsl_err("%s: failed to create SMTP client\n", __func__);
sseq = lws_smtp_sequencer_create(&ss_args);
if (!sseq) {
lwsl_err("%s: smtp sequencer create failed\n", __func__);
goto bail1;
}
/* attach an email to it */
memset(&email, 0, sizeof(email));
email.data = NULL /* email specific user data */;
email.email_from = "andy@warmcat.com";
email.email_to = recip;
email.payload = malloc(2048);
if (!email.payload) {
goto bail1;
}
lws_snprintf((char *)email.payload, 2048,
n = lws_snprintf(payload, sizeof(payload),
"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(instance, &email)) {
if (lws_smtpc_add_email(sseq, payload, n, "testserver",
"andy@warmcat.com", recip, NULL, done_cb)) {
lwsl_err("%s: failed to add email\n", __func__);
goto bail;
goto bail1;
}
/* the usual lws event loop */
@ -164,8 +115,6 @@ int main(int argc, const char **argv)
while (n >= 0 && !interrupted)
n = lws_service(context, 0);
bail:
bail1:
lwsl_user("Completed: %s\n", result ? "FAIL" : "PASS");

View file

@ -55,7 +55,7 @@ smtp_test_instance_init(lws_abs_t *instance)
"\r\n.\r\n", "andy@warmcat.com");
email->done = email_sent_or_failed;
if (lws_smtp_client_add_email(instance, email)) {
if (lws_smtpc_add_email(instance, email)) {
lwsl_err("%s: failed to add email\n", __func__);
return 1;
}
@ -163,7 +163,7 @@ sigint_handler(int sig)
* set the HELO our SMTP client will use
*/
static const lws_token_map_t smtp_protocol_tokens[] = {
static const lws_token_map_t smtp_ap_tokens[] = {
{
.u = { .value = "lws-test-client" },
.name_index = LTMI_PSMTP_V_HELO,
@ -183,8 +183,8 @@ int main(int argc, const char **argv)
struct lws_context_creation_info info;
lws_test_sequencer_args_t args;
struct lws_context *context;
lws_abs_t *abs = NULL;
struct lws_vhost *vh;
lws_abs_t abs, *instance;
const char *p;
/* the normal lws init */
@ -213,32 +213,16 @@ int main(int argc, const char **argv)
goto bail1;
}
/* create the smtp client */
/* create the abs used to create connections */
memset(&abs, 0, sizeof(abs));
abs.vh = vh;
/* select the protocol and bind its tokens */
abs.ap = lws_abs_protocol_get_by_name("smtp");
if (!abs.ap)
goto bail1;
abs.ap_tokens = smtp_protocol_tokens;
/* select the transport and bind its tokens */
abs.at = lws_abs_transport_get_by_name("unit_test");
if (!abs.at)
goto bail1;
instance = lws_abs_bind_and_create_instance(&abs);
if (!instance)
abs = lws_abstract_alloc(vh, NULL, "smtp.unit_test",
&smtp_ap_tokens[0], NULL);
if (!abs)
goto bail1;
/* configure the test sequencer */
args.abs = &abs;
args.abs = abs;
args.tests = tests;
args.results = results;
args.results_max = LWS_ARRAY_SIZE(results);
@ -271,5 +255,7 @@ bail1:
lws_context_destroy(context);
lws_abstract_free(&abs);
return !count_tests || count_passes != count_tests;
}

View file

@ -510,12 +510,12 @@ lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd,
puts(s);
em = lws_smtp_client_alloc_email_helper(s, n, vhd->email_from, u.email,
em = lws_smtpc_alloc_email_helper(s, n, vhd->email_from, u.email,
u.username, strlen(u.username),
vhd, lwsgs_smtp_client_done);
if (!em)
return 1;
if (lws_smtp_client_add_email(vhd->smtp_client, em))
if (lws_smtpc_add_email(vhd->smtp_client, em))
return 1;
return 0;
@ -636,7 +636,7 @@ lwsgs_handler_register_form(struct per_vhost_data__gs *vhd,
vhd->email_confirm_url, hash.id,
vhd->email_contact_person);
em = lws_smtp_client_alloc_email_helper(s, n, vhd->email_from,
em = lws_smtpc_alloc_email_helper(s, n, vhd->email_from,
lws_spa_get_string(pss->spa, FGS_EMAIL),
lws_spa_get_string(pss->spa, FGS_USERNAME),
strlen(lws_spa_get_string(pss->spa, FGS_USERNAME)),
@ -644,7 +644,7 @@ lwsgs_handler_register_form(struct per_vhost_data__gs *vhd,
if (!em)
return 1;
if (lws_smtp_client_add_email(vhd->smtp_client, em))
if (lws_smtpc_add_email(vhd->smtp_client, em))
return 1;
return 0;

View file

@ -562,7 +562,7 @@ callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
goto reg_done;
}
/* get the email monitor to take a look */
lws_smtp_client_kick(vhd->smtp_client);
lws_smtpc_kick(vhd->smtp_client);
n = FGS_FORGOT_GOOD;
goto reg_done;
}
@ -584,7 +584,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_smtp_client_kick(vhd->smtp_client);
lws_smtpc_kick(vhd->smtp_client);
}
reg_done:
lws_snprintf(pss->onward, sizeof(pss->onward),