| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * Abstract SMTP support for libwebsockets - SMTP sequencer
- *
- * Copyright (C) 2016-2019 Andy Green <[email protected]>
- *
- * 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;
- }
|