From 45ee5b18c0e67976e8fc42baa01ff2b83eb987ab Mon Sep 17 00:00:00 2001 From: maosiping Date: Tue, 12 Apr 2022 19:45:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=9D=E6=8C=81=E5=BC=80=E6=BA=90=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=AE=8C=E6=95=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: maosiping --- OAT.xml | 4 + include/libwebsockets/lws-mqtt.h | 330 +++++++++++++++++++ lib/abstract/protocols/smtp/smtp-sequencer.c | 320 ++++++++++++++++++ 3 files changed, 654 insertions(+) create mode 100644 include/libwebsockets/lws-mqtt.h create mode 100644 lib/abstract/protocols/smtp/smtp-sequencer.c diff --git a/OAT.xml b/OAT.xml index d84e0d6d..d908236d 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 00000000..72f77018 --- /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 00000000..4b745aab --- /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; +} -- Gitee