Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2016-06-07 09:49:59 +08:00
parent ed7c63e07d
commit 602d884028
4 changed files with 343 additions and 0 deletions

View file

@ -99,6 +99,7 @@ option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OF
option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF)
option(LWS_WITH_LEJP "With the Lightweight JSON Parser" OFF)
option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF)
option(LWS_WITH_SMTP "Provide SMTP support" OFF)
if (LWS_WITH_LWSWS)
message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV")
@ -115,6 +116,11 @@ message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV")
set(LWS_WITH_LIBUV 1)
endif()
if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV)
message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV")
set(LWS_WITH_LIBUV 1)
endif()
if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
set(LWS_WITH_SHARED OFF)
@ -592,6 +598,11 @@ if (LWS_WITH_LEJP_CONF)
)
endif()
if (LWS_WITH_SMTP)
list(APPEND SOURCES
lib/smtp.c)
endif()
# Add helper files for Windows.
if (WIN32)
set(WIN32_HELPERS_PATH win32port/win32helpers)
@ -1514,6 +1525,7 @@ message(" LWS_WITH_ACCESS_LOG = ${LWS_WITH_ACCESS_LOG}")
message(" LWS_WITH_SERVER_STATUS = ${LWS_WITH_SERVER_STATUS}")
message(" LWS_WITH_LEJP = ${LWS_WITH_LEJP}")
message(" LWS_WITH_LEJP_CONF = ${LWS_WITH_LEJP_CONF}")
message(" LWS_WITH_SMTP = ${LWS_WITH_SMTP}")
message("---------------------------------------------------------------------")
# These will be available to parent projects including libwebsockets using add_subdirectory()

View file

@ -2284,6 +2284,68 @@ lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
LWS_VISIBLE LWS_EXTERN void
lws_set_allocator(void *(*realloc)(void *ptr, size_t size));
/* lws_email */
#ifdef LWS_WITH_SMTP
enum lwsgs_smtp_states {
LGSSMTP_IDLE,
LGSSMTP_CONNECTING,
LGSSMTP_CONNECTED,
LGSSMTP_SENT_HELO,
LGSSMTP_SENT_FROM,
LGSSMTP_SENT_TO,
LGSSMTP_SENT_DATA,
LGSSMTP_SENT_BODY,
LGSSMTP_SENT_QUIT,
};
/*
* struct lws_email - abstract context for performing SMTP operations
*
* @data: opaque pointer set by user code and available to the callbacks
* @email_smtp_ip: IP address to access for SMTP server (usually 127.0.0.1)
* @email_helo: Server name to use when greeting SMTP server
* @on_next: called when idle, 0 = another email to send, nonzero is idle.
* If you return 0, all of the email_* char arrays must be set
* to something useful.
* @on_sent: called when transfer of the email to the SMTP server was
* successful, your callback would remove the current email
* from its queue
* @on_get_body: called when the body part of the queued email is about to be
* sent to the SMTP server.
*/
struct lws_email {
uv_timer_t timeout_email;
enum lwsgs_smtp_states estate;
uv_connect_t email_connect_req;
uv_tcp_t email_client;
time_t email_connect_started;
char email_buf[256];
char *content;
void *data; /* Fill before init, useful in callbacks */
uv_loop_t *loop;
char email_smtp_ip[32]; /* Fill before init, eg, "127.0.0.1" */
char email_helo[32]; /* Fill before init, eg, "myserver.com" */
char email_from[100]; /* Fill before init or @on_next */
char email_to[100]; /* Fill before init or @on_next */
unsigned int max_content_size;
/* Fill all the callbacks before init */
int (*on_next)(struct lws_email *email);
int (*on_sent)(struct lws_email *email);
int (*on_get_body)(struct lws_email *email, char *buf, int len);
};
LWS_VISIBLE LWS_EXTERN int
lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content);
LWS_VISIBLE LWS_EXTERN void
lws_email_check(struct lws_email *email);
LWS_VISIBLE LWS_EXTERN void
lws_email_destroy(struct lws_email *email);
#endif
#ifdef __cplusplus
}
#endif

266
lib/smtp.c Normal file
View file

@ -0,0 +1,266 @@
/*
* 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 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_email_init() - Initialize a struct lws_email
*
* @email: struct lws_email to init
* @loop: libuv loop to use
* @max_content: max email content size
*
* Prepares a struct lws_email for use ending SMTP
*/
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_email_check() - Request check for new email
*
* @email: struct lws_email context to check
*
* Schedules a check for new emails in 1s... call this when you have queued an
* email for send.
*/
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_email_destroy() - stop using the struct lws_email
*
* @email: the struct lws_email context
*
* Stop sending email using @email and free allocations
*/
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);
}

View file

@ -102,4 +102,7 @@
/* Lightweight JSON Parser */
#cmakedefine LWS_WITH_LEJP
/* SMTP */
#cmakedefine LWS_WITH_SMTP
${LWS_SIZEOFPTR_CODE}