2016-06-07 09:49:59 +08:00
|
|
|
/*
|
|
|
|
* SMTP support for libwebsockets
|
|
|
|
*
|
|
|
|
* Copyright (C) 2016 Andy Green <andy@warmcat.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
2017-04-06 23:01:34 +08:00
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
2016-06-07 09:49:59 +08:00
|
|
|
* 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;
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
|
2016-06-07 09:49:59 +08:00
|
|
|
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);
|
context deprecation
1) This makes lwsws run a parent process with the original permissions.
But this process is only able to respond to SIGHUP, it doesn't do anything
else.
2) You can send this parent process a SIGHUP now to cause it to
- close listening sockets in existing lwsws processes
- mark those processes as to exit when the number of active connections
on the falls to zero
- spawn a fresh child process from scratch, using latest configuration
file content, latest plugins, etc. It can now reopen listening sockets
if it chooses to, or open different listen ports or whatever.
Notes:
1) lws_context_destroy() has been split into two pieces... the reason for
the split is the first part closes the per-vhost protocols, but since
they may have created libuv objects in the per-vhost protocol storage,
these cannot be freed until after the loop has been run.
That's the purpose of the second part of the context destruction,
lws_context_destroy2().
For compatibility, if you are not using libuv, the first part calls the
second part. However if you are using libuv, you must now call the
second part from your own main.c after the first part.
2016-12-16 07:37:43 +08:00
|
|
|
uv_close((uv_handle_t *)&email->timeout_email, NULL);
|
2016-06-07 09:49:59 +08:00
|
|
|
}
|
|
|
|
|