1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lib/abstract/smtp/smtp.c
Andy Green a72b422be3 abstract: add abstract transport tokens
SMTP was improved to use the new abstract stuff a while ago,
but it was only implemented with raw socket abstract transport,
and a couple of 'api cheats' remained passing network information
for the peer connection through the supposedly abstract apis.

This patch adds a flexible generic token array to supply
abstract transport-specific information through the abstract apis,
removing the network information from the abstract connect() op.

The SMTP minimal example is modified to use this new method to
pass the network information.

The abstract transport struct was opaque, but there are real
uses to override it in user code, so this patch also makes it
part of the public abi.
2019-06-19 19:10:14 +01:00

393 lines
8.3 KiB
C

/*
* Abstract SMTP support for libwebsockets
*
* Copyright (C) 2016-2019 Andy Green <andy@warmcat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation:
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include "core/private.h"
#include "abstract/smtp/private.h"
static const short retcodes[] = {
0, /* idle */
0, /* connecting */
220, /* connected */
250, /* helo */
250, /* from */
250, /* to */
354, /* data */
250, /* body */
221, /* quit */
};
static void
lws_smtp_client_state_transition(lws_smtp_client_t *c, lwsgs_smtp_states_t s)
{
lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
c->estate = s;
}
void
lws_smtp_client_kick(lws_smtp_client_t *c)
{
lws_smtp_email_t *e;
lws_dll2_t *d;
char buf[64];
int n;
if (c->estate != LGSSMTP_IDLE)
return;
/* is there something to do? */
again:
d = lws_dll2_get_head(&c->pending_owner);
if (!d)
return;
e = lws_container_of(d, lws_smtp_email_t, list);
/* do we need to time out this guy? */
if ((time_t)lws_now_secs() - e->added > (time_t)c->i.delivery_timeout) {
lwsl_err("%s: timing out email\n", __func__);
lws_dll2_remove(&e->list);
n = lws_snprintf(buf, sizeof(buf), "0 Timed out retrying send");
e->done(e, buf, n);
if (lws_dll2_get_head(&c->pending_owner))
goto again;
return;
}
/* is it time for his retry yet? */
if (e->last_try &&
(time_t)lws_now_secs() - e->last_try < (time_t)c->i.retry_interval) {
/* no... send him to the tail */
lws_dll2_remove(&e->list);
lws_dll2_add_tail(&e->list, &c->pending_owner);
return;
}
/* check if we have a connection to the server ongoing */
if (c->abs.state(c->abs_conn)) {
/*
* there's a connection, it could be still trying to connect
* or established
*/
c->abs.ask_for_writeable(c->abs_conn);
return;
}
/* there's no existing connection */
lws_smtp_client_state_transition(c, LGSSMTP_CONNECTING);
if (c->abs.client_conn(c->abs_conn, c->i.vh, c->i.abs_tokens)) {
lwsl_err("%s: failed to connect\n", __func__);
return;
}
e->tries++;
e->last_try = lws_now_secs();
}
/*
* we became connected
*/
static int
lws_smtp_client_abs_accept(lws_abs_user_t *abs_priv)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv;
lws_smtp_client_state_transition(c, LGSSMTP_CONNECTED);
return 0;
}
static int
lws_smtp_client_abs_rx(lws_abs_user_t *abs_priv, uint8_t *buf, size_t len)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv;
lws_smtp_email_t *e;
lws_dll2_t *pd2;
int n;
pd2 = lws_dll2_get_head(&c->pending_owner);
if (!pd2)
return 0;
e = lws_container_of(pd2, lws_smtp_email_t, list);
if (!e)
return 0;
lwsl_debug("%s: rx: '%.*s'\n", __func__, (int)len, (const char *)buf);
n = atoi((char *)buf);
if (n != retcodes[c->estate]) {
lwsl_notice("%s: bad response from server: %d (state %d) %.*s\n",
__func__, n, c->estate, (int)len, buf);
lws_dll2_remove(&e->list);
lws_dll2_add_tail(&e->list, &c->pending_owner);
lws_smtp_client_state_transition(c, LGSSMTP_IDLE);
lws_smtp_client_kick(c);
return 0;
}
if (c->estate == LGSSMTP_SENT_QUIT) {
lwsl_debug("%s: done\n", __func__);
lws_smtp_client_state_transition(c, LGSSMTP_IDLE);
lws_dll2_remove(&e->list);
if (e->done && e->done(e, "sent OK", 7))
return 1;
return 1;
}
c->send_pending = 1;
c->abs.ask_for_writeable(c->abs_conn);
return 0;
}
static int
lws_smtp_client_abs_writeable(lws_abs_user_t *abs_priv, size_t budget)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv;
char b[256 + LWS_PRE], *p = b + LWS_PRE;
lws_smtp_email_t *e;
lws_dll2_t *pd2;
int n;
pd2 = lws_dll2_get_head(&c->pending_owner);
if (!pd2)
return 0;
e = lws_container_of(pd2, lws_smtp_email_t, list);
if (!e)
return 0;
if (!c->send_pending)
return 0;
c->send_pending = 0;
lwsl_debug("%s: writing response for state %d\n", __func__, c->estate);
switch (c->estate) {
case LGSSMTP_CONNECTED:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->i.helo);
lws_smtp_client_state_transition(c, LGSSMTP_SENT_HELO);
break;
case LGSSMTP_SENT_HELO:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n",
e->email_from);
lws_smtp_client_state_transition(c, LGSSMTP_SENT_FROM);
break;
case LGSSMTP_SENT_FROM:
n = lws_snprintf(p, sizeof(b) - LWS_PRE,
"RCPT TO: <%s>\n", e->email_to);
lws_smtp_client_state_transition(c, LGSSMTP_SENT_TO);
break;
case LGSSMTP_SENT_TO:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n");
lws_smtp_client_state_transition(c, LGSSMTP_SENT_DATA);
break;
case LGSSMTP_SENT_DATA:
p = (char *)e->payload;
n = strlen(e->payload);
lws_smtp_client_state_transition(c, LGSSMTP_SENT_BODY);
break;
case LGSSMTP_SENT_BODY:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n");
lws_smtp_client_state_transition(c, LGSSMTP_SENT_QUIT);
break;
case LGSSMTP_SENT_QUIT:
return 0;
default:
return 0;
}
//puts(p);
c->abs.tx(c->abs_conn, (uint8_t *)p, n);
return 0;
}
static int
lws_smtp_client_abs_closed(lws_abs_user_t *abs_priv)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv;
c->abs_conn = NULL;
return 0;
}
static int
lws_smtp_client_abs_heartbeat(lws_abs_user_t *abs_priv)
{
lws_smtp_client_t *c = (lws_smtp_client_t *)abs_priv;
lws_smtp_client_kick(c);
return 0;
}
lws_smtp_email_t *
lws_smtp_client_alloc_email_helper(const char *payload, size_t payload_len,
const char *sender, const char *recipient,
const char *extra, size_t extra_len, void *data,
int (*done)(struct lws_smtp_email *e,
void *buf, size_t len))
{
size_t ls = strlen(sender), lr = strlen(recipient);
lws_smtp_email_t *em;
char *p;
em = malloc(sizeof(*em) + payload_len + ls + lr + extra_len + 4);
if (!em) {
lwsl_err("OOM\n");
return NULL;
}
p = (char *)&em[1];
memset(em, 0, sizeof(*em));
em->data = data;
em->done = done;
em->email_from = p;
memcpy(p, sender, ls + 1);
p += ls + 1;
em->email_to = p;
memcpy(p, recipient, lr + 1);
p += lr + 1;
em->payload = p;
memcpy(p, payload, payload_len + 1);
p += payload_len + 1;
if (extra) {
em->extra = p;
memcpy(p, extra, extra_len + 1);
}
return em;
}
int
lws_smtp_client_add_email(lws_smtp_client_t *c, lws_smtp_email_t *e)
{
if (c->pending_owner.count > c->i.email_queue_max) {
lwsl_err("%s: email queue at limit of %d\n", __func__,
(int)c->i.email_queue_max);
return 1;
}
e->added = lws_now_secs();
e->last_try = 0;
e->tries = 0;
lws_dll2_clear(&e->list);
lws_dll2_add_tail(&e->list, &c->pending_owner);
lws_smtp_client_kick(c);
return 0;
}
lws_smtp_client_t *
lws_smtp_client_create(const lws_smtp_client_info_t *ci)
{
lws_smtp_client_t *c;
c = lws_zalloc(sizeof(*c), "email client");
if (!c)
return NULL;
c->i = *ci;
c->abs = *ci->abs;
/* fill in the additional abstract callbacks we fulfil */
c->abs.accept = lws_smtp_client_abs_accept;
c->abs.rx = lws_smtp_client_abs_rx;
c->abs.writeable = lws_smtp_client_abs_writeable;
c->abs.closed = lws_smtp_client_abs_closed;
c->abs.heartbeat = lws_smtp_client_abs_heartbeat;
if (!c->i.email_queue_max)
c->i.email_queue_max = 8;
if (!c->i.retry_interval)
c->i.retry_interval = 15 * 60;
if (!c->i.delivery_timeout)
c->i.delivery_timeout = 12 * 60 * 60;
c->abs_conn = c->abs.create(&c->abs, c);
if (!c->abs_conn) {
lws_free(c);
return NULL;
}
lws_smtp_client_state_transition(c, LGSSMTP_IDLE);
return c;
}
static int
cleanup(struct lws_dll2 *d, void *user)
{
lws_smtp_email_t *e;
e = lws_container_of(d, lws_smtp_email_t, list);
if (e->done && e->done(e, "destroying", 10))
return 1;
return 0;
}
void
lws_smtp_client_destroy(lws_smtp_client_t **c)
{
if (!*c)
return;
lws_dll2_foreach_safe(&(*c)->pending_owner, NULL, cleanup);
if ((*c)->abs_conn) {
(*c)->abs.close((*c)->abs_conn);
(*c)->abs.destroy(&(*c)->abs_conn);
}
lws_free_set_NULL(*c);
}