/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2019 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "private-lib-core.h" #include "private-lib-abstract.h" /** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ 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; /** abstract protocol instance data */ typedef struct lws_smtp_client_protocol { const struct lws_abs *abs; lwsgs_smtp_states_t estate; lws_smtp_email_t *e; /* the email we are trying to send */ const char *helo; unsigned char send_pending:1; } lws_smtpcp_t; 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_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 lws_smtp_email_t * lws_smtpc_get_email(lws_smtpcp_t *c) { 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; lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO); /* lifetime of the email object is handled by done callback */ c->e->done(c->e, c->e->data, disp, buf, len); c->e = NULL; /* 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 0; ach = lws_container_of(d, lws_abs_t, bound); ch = (lws_smtpcp_t *)ach->api; c->e = lws_smtpc_get_email(ch); /* since we took it on, remove it from the queue */ lws_dll2_remove(d); return 1; } /* * we became connected */ static int lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api) { lws_smtpcp_t *c = (lws_smtpcp_t *)api; /* 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_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len) { lws_smtpcp_t *c = (lws_smtpcp_t *)api; char dotstar[96], at[5]; int n; c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0); lws_strncpy(at, (const char *)buf, sizeof(at)); n = atoi(at); 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) */ lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar)); lwsl_err("%s: server: %s\n", __func__, dotstar); 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]) { lws_strnncpy(dotstar, buf, len, sizeof(dotstar)); lwsl_notice("%s: bad response: %d (state %d) %s\n", __func__, n, c->estate, dotstar); lws_smtpc_email_disposition(c, LWS_SMTP_DISPOSITION_FAILED, buf, len); return 0; } break; } c->send_pending = 1; c->abs->at->ask_for_writeable(c->abs->ati); return 0; } static int lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget) { char b[256 + LWS_PRE], *p = b + LWS_PRE; lws_smtpcp_t *c = (lws_smtpcp_t *)api; int n; if (!c->send_pending || !c->e) 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->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", 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", 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_smtpc_state_transition(c, LGSSMTP_SENT_DATA); break; case LGSSMTP_SENT_DATA: 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_smtpc_state_transition(c, LGSSMTP_SENT_QUIT); break; case LGSSMTP_SENT_QUIT: return 0; default: return 0; } //puts(p); c->abs->at->tx(c->abs->ati, (uint8_t *)p, n); return 0; } static int lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api) { lws_smtpcp_t *c = (lws_smtpcp_t *)api; if (c) 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_smtpc_create(const lws_abs_t *ai) { lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api; memset(c, 0, sizeof(*c)); c->abs = ai; c->e = lws_smtpc_get_email(c); lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ? LGSSMTP_CONNECTING : LGSSMTP_IDLE); /* 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_smtpc_destroy(lws_abs_protocol_inst_t **_c) { lws_smtpcp_t *c = (lws_smtpcp_t *)*_c; if (!c) return; /* 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_smtpcp_t), .flags = LWSABSPR_FLAG_PIPELINE, .create = lws_smtpc_create, .destroy = lws_smtpc_destroy, .compare = lws_smtpc_compare, .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, };