/* * SMTP support for libwebsockets * * Copyright (C) 2016 Andy Green * * 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); }