241 lines
5.5 KiB
C
241 lines
5.5 KiB
C
/*
|
|
* SMTP support for libwebsockets
|
|
*
|
|
* Copyright (C) 2016 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 "private-libwebsockets.h"
|
|
|
|
static unsigned int
|
|
lwsgs_now_secs(void)
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return tv.tv_sec;
|
|
}
|
|
|
|
static void
|
|
ccb(uv_handle_t* handle)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
|
|
{
|
|
struct lws_email *email = (struct lws_email *)handle->data;
|
|
|
|
*buf = uv_buf_init(email->email_buf, sizeof(email->email_buf) - 1);
|
|
}
|
|
|
|
static void
|
|
on_write_end(uv_write_t *req, int status) {
|
|
lwsl_notice("%s\n", __func__);
|
|
if (status == -1) {
|
|
fprintf(stderr, "error on_write_end");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lwsgs_email_read(struct uv_stream_s *s, ssize_t nread, const uv_buf_t *buf)
|
|
{
|
|
struct lws_email *email = (struct lws_email *)s->data;
|
|
static const short retcodes[] = {
|
|
0, /* idle */
|
|
0, /* connecting */
|
|
220, /* connected */
|
|
250, /* helo */
|
|
250, /* from */
|
|
250, /* to */
|
|
354, /* data */
|
|
250, /* body */
|
|
221, /* quit */
|
|
};
|
|
uv_write_t write_req;
|
|
uv_buf_t wbuf;
|
|
int n;
|
|
|
|
if (nread >= 0)
|
|
email->email_buf[nread] = '\0';
|
|
lwsl_notice("%s: %s\n", __func__, buf->base);
|
|
if (nread == -1) {
|
|
lwsl_err("%s: failed\n", __func__);
|
|
return;
|
|
}
|
|
|
|
n = atoi(buf->base);
|
|
if (n != retcodes[email->estate]) {
|
|
lwsl_err("%s: bad response from server\n", __func__);
|
|
goto close_conn;
|
|
}
|
|
|
|
switch (email->estate) {
|
|
case LGSSMTP_CONNECTED:
|
|
n = sprintf(email->content, "HELO %s\n", email->email_helo);
|
|
email->estate = LGSSMTP_SENT_HELO;
|
|
break;
|
|
case LGSSMTP_SENT_HELO:
|
|
n = sprintf(email->content, "MAIL FROM: <%s>\n", email->email_from);
|
|
email->estate = LGSSMTP_SENT_FROM;
|
|
break;
|
|
case LGSSMTP_SENT_FROM:
|
|
n = sprintf(email->content, "RCPT TO: <%s>\n", email->email_to);
|
|
email->estate = LGSSMTP_SENT_TO;
|
|
break;
|
|
case LGSSMTP_SENT_TO:
|
|
n = sprintf(email->content, "DATA\n");
|
|
email->estate = LGSSMTP_SENT_DATA;
|
|
break;
|
|
case LGSSMTP_SENT_DATA:
|
|
if (email->on_get_body(email, email->content, email->max_content_size))
|
|
return;
|
|
n = strlen(email->content);
|
|
email->estate = LGSSMTP_SENT_BODY;
|
|
break;
|
|
case LGSSMTP_SENT_BODY:
|
|
n = sprintf(email->content, "quit\n");
|
|
email->estate = LGSSMTP_SENT_QUIT;
|
|
break;
|
|
case LGSSMTP_SENT_QUIT:
|
|
lwsl_notice("%s: done\n", __func__);
|
|
email->on_sent(email);
|
|
email->estate = LGSSMTP_IDLE;
|
|
goto close_conn;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
puts(email->content);
|
|
wbuf = uv_buf_init(email->content, n);
|
|
uv_write(&write_req, s, &wbuf, 1, on_write_end);
|
|
|
|
return;
|
|
|
|
close_conn:
|
|
|
|
uv_close((uv_handle_t *)s, ccb);
|
|
}
|
|
|
|
static void
|
|
lwsgs_email_on_connect(uv_connect_t *req, int status)
|
|
{
|
|
struct lws_email *email = (struct lws_email *)req->data;
|
|
|
|
lwsl_notice("%s\n", __func__);
|
|
|
|
if (status == -1) {
|
|
lwsl_err("%s: failed\n", __func__);
|
|
return;
|
|
}
|
|
|
|
uv_read_start(req->handle, alloc_buffer, lwsgs_email_read);
|
|
email->estate = LGSSMTP_CONNECTED;
|
|
}
|
|
|
|
|
|
static void
|
|
uv_timeout_cb_email(uv_timer_t *w
|
|
#if UV_VERSION_MAJOR == 0
|
|
, int status
|
|
#endif
|
|
)
|
|
{
|
|
struct lws_email *email = lws_container_of(w, struct lws_email,
|
|
timeout_email);
|
|
time_t now = lwsgs_now_secs();
|
|
struct sockaddr_in req_addr;
|
|
|
|
switch (email->estate) {
|
|
case LGSSMTP_IDLE:
|
|
|
|
if (email->on_next(email))
|
|
break;
|
|
|
|
email->estate = LGSSMTP_CONNECTING;
|
|
|
|
uv_tcp_init(email->loop, &email->email_client);
|
|
if (uv_ip4_addr(email->email_smtp_ip, 25, &req_addr)) {
|
|
lwsl_err("Unable to convert mailserver ads\n");
|
|
return;
|
|
}
|
|
|
|
lwsl_notice("LGSSMTP_IDLE: connecting\n");
|
|
|
|
email->email_connect_started = now;
|
|
email->email_connect_req.data = email;
|
|
email->email_client.data = email;
|
|
uv_tcp_connect(&email->email_connect_req, &email->email_client,
|
|
(struct sockaddr *)&req_addr,
|
|
lwsgs_email_on_connect);
|
|
|
|
uv_timer_start(&email->timeout_email,
|
|
uv_timeout_cb_email, 5000, 0);
|
|
|
|
break;
|
|
|
|
case LGSSMTP_CONNECTING:
|
|
if (email->email_connect_started - now > 5) {
|
|
lwsl_err("mail session timed out\n");
|
|
/* !!! kill the connection */
|
|
uv_close((uv_handle_t *) &email->email_connect_req, ccb);
|
|
email->estate = LGSSMTP_IDLE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
LWS_VISIBLE LWS_EXTERN int
|
|
lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content)
|
|
{
|
|
email->content = lws_malloc(max_content);
|
|
if (!email->content)
|
|
return 1;
|
|
|
|
email->max_content_size = max_content;
|
|
uv_timer_init(loop, &email->timeout_email);
|
|
|
|
email->loop = loop;
|
|
|
|
/* trigger him one time in a bit */
|
|
uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 2000, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lws_email_check(struct lws_email *email)
|
|
{
|
|
uv_timer_start(&email->timeout_email, uv_timeout_cb_email, 1000, 0);
|
|
}
|
|
|
|
LWS_VISIBLE LWS_EXTERN void
|
|
lws_email_destroy(struct lws_email *email)
|
|
{
|
|
if (email->content)
|
|
lws_free_set_NULL(email->content);
|
|
|
|
uv_timer_stop(&email->timeout_email);
|
|
uv_close((uv_handle_t *)&email->timeout_email, NULL);
|
|
}
|
|
|