smtp
authorAndy Green <andy@warmcat.com>
Tue, 7 Jun 2016 01:49:59 +0000 (09:49 +0800)
committerAndy Green <andy@warmcat.com>
Tue, 7 Jun 2016 01:49:59 +0000 (09:49 +0800)
Signed-off-by: Andy Green <andy@warmcat.com>
CMakeLists.txt
lib/libwebsockets.h
lib/smtp.c [new file with mode: 0644]
lws_config.h.in

index 2fbb180..da62dfd 100644 (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()
index 0426a9b..dcb3e64 100644 (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
diff --git a/lib/smtp.c b/lib/smtp.c
new file mode 100644 (file)
index 0000000..1ccf6ed
--- /dev/null
@@ -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);
+}
+
index 598969c..21f1b53 100644 (file)
 /* Lightweight JSON Parser */
 #cmakedefine LWS_WITH_LEJP
 
+/* SMTP */
+#cmakedefine LWS_WITH_SMTP
+
 ${LWS_SIZEOFPTR_CODE}