diff --git a/OAT.xml b/OAT.xml
index d84e0d6d845cc37b2a64360ef528d6385d9a36dc..d908236d56b1125bdb0c8e14ecfd7e25c2060219 100644
--- a/OAT.xml
+++ b/OAT.xml
@@ -85,6 +85,10 @@
+
+
+
+
diff --git a/include/libwebsockets/lws-mqtt.h b/include/libwebsockets/lws-mqtt.h
new file mode 100644
index 0000000000000000000000000000000000000000..72f770185df6b912a89e0903efe2fb9cf2819568
--- /dev/null
+++ b/include/libwebsockets/lws-mqtt.h
@@ -0,0 +1,330 @@
+/*
+ * libwebsockets - protocol - mqtt
+ *
+ * Copyright (C) 2010 - 2020 Andy Green
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * included from libwebsockets.h
+ */
+
+#ifndef _LWS_MQTT_H
+#define _LWS_MQTT_H 1
+
+struct _lws_mqtt_related;
+typedef struct _lws_mqtt_related lws_mqtt_related_t;
+struct lws_mqtt_str_st;
+typedef struct lws_mqtt_str_st lws_mqtt_str_t;
+
+#define MQTT_VER_3_1_1 4
+
+#define LWS_MQTT_FINAL_PART 1
+
+typedef enum {
+ QOS0,
+ QOS1,
+ QOS2, /* not supported */
+ RESERVED_QOS_LEVEL,
+ FAILURE_QOS_LEVEL = 0x80
+} lws_mqtt_qos_levels_t;
+
+typedef union {
+ struct {
+ uint8_t retain:1;
+ uint8_t qos:2;
+ uint8_t dup:1;
+ uint8_t ctrl_pkt_type:4;
+ } flags;
+ uint8_t bits;
+} lws_mqtt_fixed_hdr_t;
+
+/*
+ * MQTT connection parameters, passed into struct
+ * lws_client_connect_info to establish a connection using
+ * lws_client_connect_via_info().
+*/
+typedef struct lws_mqtt_client_connect_param_s {
+ const char *client_id; /* Client ID */
+ uint16_t keep_alive; /* MQTT keep alive
+ interval in
+ seconds */
+ uint8_t clean_start; /* MQTT clean
+ session */
+ struct {
+ const char *topic;
+ const char *message;
+ lws_mqtt_qos_levels_t qos;
+ uint8_t retain;
+ } will_param; /* MQTT LWT
+ parameters */
+ const char *username;
+ const char *password;
+} lws_mqtt_client_connect_param_t;
+
+/*
+ * MQTT publish parameters
+*/
+typedef struct lws_mqtt_publish_param_s {
+ char *topic; /* Topic Name */
+ uint16_t topic_len;
+ const void *payload; /* Publish Payload */
+ uint32_t payload_len; /* Size of the
+ complete payload */
+ uint32_t payload_pos; /* where we are in payload */
+ lws_mqtt_qos_levels_t qos;
+
+ /*--v-Following will be used by LWS-v--*/
+ uint16_t packet_id; /* Packet ID for QoS >
+ 0 */
+ uint8_t dup:1; /* Retried PUBLISH,
+ for QoS > 0 */
+} lws_mqtt_publish_param_t;
+
+typedef struct topic_elem {
+ const char *name; /* Topic Name */
+ lws_mqtt_qos_levels_t qos; /* Requested QoS */
+
+ /*--v-Following will be used by LWS-v--*/
+ uint8_t acked;
+} lws_mqtt_topic_elem_t;
+
+/*
+ * MQTT publish parameters
+*/
+typedef struct lws_mqtt_subscribe_param_s {
+ uint32_t num_topics; /* Number of topics */
+ lws_mqtt_topic_elem_t *topic; /* Array of topic elements */
+
+ /*--v-Following will be used by LWS-v--*/
+ uint16_t packet_id;
+} lws_mqtt_subscribe_param_t;
+
+typedef enum {
+ LMQCP_RESERVED,
+ LMQCP_CTOS_CONNECT, /* Connection request */
+ LMQCP_STOC_CONNACK, /* Connection acknowledgment */
+ LMQCP_PUBLISH, /* Publish Message */
+ LMQCP_PUBACK, /* QoS 1: Publish acknowledgment */
+ LMQCP_PUBREC, /* QoS 2.1: Publish received */
+ LMQCP_PUBREL, /* QoS 2.2: Publish release */
+ LMQCP_PUBCOMP, /* QoS 2.3: Publish complete */
+ LMQCP_CTOS_SUBSCRIBE, /* Subscribe request */
+ LMQCP_STOC_SUBACK, /* Subscribe acknowledgment */
+ LMQCP_CTOS_UNSUBSCRIBE, /* Unsubscribe request */
+ LMQCP_STOC_UNSUBACK, /* Unsubscribe acknowledgment */
+ LMQCP_CTOS_PINGREQ, /* PING request */
+ LMQCP_STOC_PINGRESP, /* PONG response */
+ LMQCP_DISCONNECT, /* Disconnect notification */
+ LMQCP_AUTH /* Authentication exchange */
+} lws_mqtt_control_packet_t;
+
+/* flags from byte 8 of C_TO_S CONNECT */
+typedef enum {
+ LMQCFT_USERNAME = (1 << 7),
+ LMQCFT_PASSWORD = (1 << 6),
+ LMQCFT_WILL_RETAIN = (1 << 5),
+ LMQCFT_WILL_QOS = (1 << 3),
+ LMQCFT_WILL_FLAG = (1 << 2),
+ LMQCFT_CLEAN_START = (1 << 1),
+ LMQCFT_RESERVED = (1 << 0),
+
+ LMQCFT_WILL_QOS_MASK = (3 << 3),
+} lws_mqtt_connect_flags_t;
+
+/* flags for S_TO_C CONNACK */
+typedef enum {
+ LMQCFT_SESSION_PRESENT = (1 << 0),
+} lws_mqtt_connack_flags_t;
+
+typedef enum {
+ LMQCP_REASON_SUCCESS = 0x00,
+ LMQCP_REASON_NORMAL_DISCONNECTION = 0x00,
+ LMQCP_REASON_GRANTED_QOS0 = 0x00,
+ LMQCP_REASON_GRANTED_QOS1 = 0x01,
+ LMQCP_REASON_GRANTED_QOS2 = 0x02,
+ LMQCP_REASON_DISCONNECT_WILL = 0x04,
+ LMQCP_REASON_NO_MATCHING_SUBSCRIBER = 0x10,
+ LMQCP_REASON_NO_SUBSCRIPTION_EXISTED = 0x11,
+ LMQCP_REASON_CONTINUE_AUTHENTICATION = 0x18,
+ LMQCP_REASON_RE_AUTHENTICATE = 0x19,
+
+ LMQCP_REASON_UNSPECIFIED_ERROR = 0x80,
+ LMQCP_REASON_MALFORMED_PACKET = 0x81,
+ LMQCP_REASON_PROTOCOL_ERROR = 0x82,
+ LMQCP_REASON_IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
+
+ /* Begin - Error codes for CONNACK */
+ LMQCP_REASON_UNSUPPORTED_PROTOCOL = 0x84,
+ LMQCP_REASON_CLIENT_ID_INVALID = 0x85,
+ LMQCP_REASON_BAD_CREDENTIALS = 0x86,
+ LMQCP_REASON_NOT_AUTHORIZED = 0x87,
+ /* End - Error codes for CONNACK */
+
+ LMQCP_REASON_SERVER_UNAVAILABLE = 0x88,
+ LMQCP_REASON_SERVER_BUSY = 0x89,
+ LMQCP_REASON_BANNED = 0x8a,
+ LMQCP_REASON_SERVER_SHUTTING_DOWN = 0x8b,
+ LMQCP_REASON_BAD_AUTHENTICATION_METHOD = 0x8c,
+ LMQCP_REASON_KEEPALIVE_TIMEOUT = 0x8d,
+ LMQCP_REASON_SESSION_TAKEN_OVER = 0x8e,
+ LMQCP_REASON_TOPIC_FILTER_INVALID = 0x8f,
+ LMQCP_REASON_TOPIC_NAME_INVALID = 0x90,
+ LMQCP_REASON_PACKET_ID_IN_USE = 0x91,
+ LMQCP_REASON_PACKET_ID_NOT_FOUND = 0x92,
+ LMQCP_REASON_MAX_RX_EXCEEDED = 0x93,
+ LMQCP_REASON_TOPIC_ALIAS_INVALID = 0x94,
+ LMQCP_REASON_PACKET_TOO_LARGE = 0x95,
+ LMQCP_REASON_RATELIMIT = 0x96,
+ LMQCP_REASON_QUOTA_EXCEEDED = 0x97,
+ LMQCP_REASON_ADMINISTRATIVE_ACTION = 0x98,
+ LMQCP_REASON_PAYLOAD_FORMAT_INVALID = 0x99,
+ LMQCP_REASON_RETAIN_NOT_SUPPORTED = 0x9a,
+ LMQCP_REASON_QOS_NOT_SUPPORTED = 0x9b,
+ LMQCP_REASON_USE_ANOTHER_SERVER = 0x9c,
+ LMQCP_REASON_SERVER_MOVED = 0x9d,
+ LMQCP_REASON_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9e,
+ LMQCP_REASON_CONNECTION_RATE_EXCEEDED = 0x9f,
+ LMQCP_REASON_MAXIMUM_CONNECT_TIME = 0xa0,
+ LMQCP_REASON_SUBSCRIPTION_IDS_NOT_SUPPORTED = 0xa1,
+ LMQCP_REASON_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xa2,
+} lws_mqtt_reason_t;
+
+typedef enum {
+ LMQPROP_INVALID,
+ LMQPROP_PAYLOAD_FORMAT_INDICATOR = 0x01,
+ LMQPROP_MESSAGE_EXPIRY_INTERVAL = 0x02,
+ LMQPROP_CONTENT_TYPE = 0x03,
+ LMQPROP_RESPONSE_TOPIC = 0x08,
+ LMQPROP_CORRELATION_DATA = 0x09,
+ LMQPROP_SUBSCRIPTION_IDENTIFIER = 0x0b,
+ LMQPROP_SESSION_EXPIRY_INTERVAL = 0x11,
+ LMQPROP_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
+ LMQPROP_SERVER_KEEP_ALIVE = 0x13,
+ LMQPROP_AUTHENTICATION_METHOD = 0x15,
+ LMQPROP_AUTHENTICATION_DATA = 0x16,
+ LMQPROP_REQUEST_PROBLEM_INFORMATION = 0x17,
+ LMQPROP_WILL_DELAY_INTERVAL = 0x18,
+ LMQPROP_REQUEST_RESPONSE_INFORMATION = 0x19,
+ LMQPROP_RESPONSE_INFORMATION = 0x1a,
+ LMQPROP_SERVER_REFERENCE = 0x1c,
+ LMQPROP_REASON_STRING = 0x1f,
+ LMQPROP_RECEIVE_MAXIMUM = 0x21,
+ LMQPROP_TOPIC_ALIAS_MAXIMUM = 0x22,
+ LMQPROP_TOPIC_ALIAS = 0x23,
+ LMQPROP_MAXIMUM_QOS = 0x24,
+ LMQPROP_RETAIN_AVAILABLE = 0x25,
+ LMQPROP_USER_PROPERTY = 0x26,
+ LMQPROP_MAXIMUM_PACKET_SIZE = 0x27,
+ LMQPROP_WILDCARD_SUBSCRIPTION_AVAIL = 0x28,
+ LMQPROP_SUBSCRIPTION_IDENTIFIER_AVAIL = 0x29,
+ LMQPROP_SHARED_SUBSCRIPTION_AVAIL = 0x2a
+} lws_mqtt_property;
+
+int
+lws_read_mqtt(struct lws *wsi, unsigned char *buf, lws_filepos_t len);
+
+/* returns 0 if bd1 and bd2 are "the same", that includes empty, else nonzero */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_bindata_cmp(const lws_mqtt_str_t *bd1, const lws_mqtt_str_t *bd2);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_mqtt_str_init(lws_mqtt_str_t *s, uint8_t *buf, uint16_t lim, char nf);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create(uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create_init(uint8_t *buf, uint16_t len, uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN lws_mqtt_str_t *
+lws_mqtt_str_create_cstr_dup(const char *buf, uint16_t lim);
+
+LWS_VISIBLE LWS_EXTERN uint8_t *
+lws_mqtt_str_next(lws_mqtt_str_t *s, uint16_t *budget);
+
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_str_advance(lws_mqtt_str_t *s, int n);
+
+LWS_VISIBLE LWS_EXTERN void
+lws_mqtt_str_free(lws_mqtt_str_t **s);
+
+
+/**
+ * lws_mqtt_client_send_publish() - lws_write a publish packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param pub: additional information on what we're publishing
+ * \param buf: payload to send
+ * \param len: length of data in buf
+ * \param final: flag indicating this is the last part
+ *
+ * Issues part of, or the whole of, a PUBLISH frame. The first part of the
+ * frame contains the header, and uses the .qos and .payload_len parts of \p pub
+ * since MQTT requires the frame to specify the PUBLISH message length at the
+ * start. The \p len paramter may be less than \p pub.payload_len, in which
+ * case subsequent calls with more payload are needed to complete the frame.
+ *
+ * Although the connection is stuck waiting for the remainder, in that it can't
+ * issue any other frames until the current one is completed, lws returns to the
+ * event loop normally and can continue the calls with additional payload even
+ * for huge frames as the data becomes available, consistent with timeout needs
+ * and latency to start any new frame (even, eg, related to ping / pong).
+ *
+ * If you're sending large frames, the OS will typically not allow the data to
+ * be sent all at once to kernel side. So you should ideally cut the payload
+ * up into 1 or 2- mtu sized chunks and send that.
+ *
+ * Final should be set when you're calling with the last part of the payload.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_client_send_publish(struct lws *wsi, lws_mqtt_publish_param_t *pub,
+ const void *buf, uint32_t len, int final);
+
+/**
+ * lws_mqtt_client_send_subcribe() - lws_write a subscribe packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param sub: which topic(s) we want to subscribe to
+ *
+ * For topics other child streams have not already subscribed to, send a packet
+ * to the server asking to subscribe to them. If all topics listed are already
+ * subscribed to be the shared network connection, just trigger the
+ * LWS_CALLBACK_MQTT_SUBSCRIBED callback as if a SUBACK had come.
+ *
+ * \p sub doesn't need to exist after the return from this function.
+ */
+LWS_VISIBLE LWS_EXTERN int
+lws_mqtt_client_send_subcribe(struct lws *wsi, lws_mqtt_subscribe_param_t *sub);
+
+/**
+ * lws_mqtt_client_send_unsubcribe() - lws_write a unsubscribe packet
+ *
+ * \param wsi: the mqtt child wsi
+ * \param sub: which topic(s) we want to unsubscribe from
+ *
+ * For topics other child streams are not subscribed to, send a packet
+ * to the server asking to unsubscribe from them. If all topics
+ * listed are already subscribed by other child streams on the shared
+ * network connection, just trigger the LWS_CALLBACK_MQTT_UNSUBSCRIBED
+ * callback as if a UNSUBACK had come.
+ *
+ * \p unsub doesn't need to exist after the return from this function.
+ */
+LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
+lws_mqtt_client_send_unsubcribe(struct lws *wsi,
+ const lws_mqtt_subscribe_param_t *unsub);
+
+#endif /* _LWS_MQTT_H */
diff --git a/lib/abstract/protocols/smtp/smtp-sequencer.c b/lib/abstract/protocols/smtp/smtp-sequencer.c
new file mode 100644
index 0000000000000000000000000000000000000000..4b745aab1e7c55719f785e436f23b1971d6dd9bd
--- /dev/null
+++ b/lib/abstract/protocols/smtp/smtp-sequencer.c
@@ -0,0 +1,320 @@
+/*
+ * Abstract SMTP support for libwebsockets - SMTP sequencer
+ *
+ * Copyright (C) 2016-2019 Andy Green
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * 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_seq_timeout_us(s->seq, 10 * LWS_USEC_PER_SEC);
+ 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;
+}