From 48800504be8314430fa315e72e5c4f73ff38f426 Mon Sep 17 00:00:00 2001 From: cpq Date: Tue, 20 Sep 2022 11:34:22 +0100 Subject: [PATCH] Add smtp client example --- examples/smtp-client/Makefile | 20 +++++++++ examples/smtp-client/README.md | 5 +++ examples/smtp-client/ca.pem | 1 + examples/smtp-client/main.c | 80 ++++++++++++++++++++++++++++++++++ mongoose.c | 4 +- mongoose.h | 1 + src/event.h | 1 + src/tls_mbed.c | 3 +- src/tls_openssl.c | 1 + 9 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 examples/smtp-client/Makefile create mode 100644 examples/smtp-client/README.md create mode 120000 examples/smtp-client/ca.pem create mode 100644 examples/smtp-client/main.c diff --git a/examples/smtp-client/Makefile b/examples/smtp-client/Makefile new file mode 100644 index 00000000..88306c71 --- /dev/null +++ b/examples/smtp-client/Makefile @@ -0,0 +1,20 @@ +PROG ?= example +CFLAGS ?= -W -Wall -Wextra -O2 $(EXTRA_CFLAGS) +SSL ?= MBEDTLS + +ifeq "$(SSL)" "MBEDTLS" +CFLAGS += -DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509 -L$(MBEDTLS)/lib -I$(MBEDTLS)/include +endif + +ifeq "$(SSL)" "OPENSSL" +CFLAGS += -DMG_ENABLE_OPENSSL=1 -lssl -lcrypto -L$(OPENSSL)/lib -I$(OPENSSL)/include +endif + +all: $(PROG) + $(RUN) ./$(PROG) $(ARGS) + +$(PROG): main.c + $(CC) ../../mongoose.c -I../.. $(CFLAGS) -o $(PROG) main.c + +clean: + rm -rf $(PROG) _CL* *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb mbedtls diff --git a/examples/smtp-client/README.md b/examples/smtp-client/README.md new file mode 100644 index 00000000..0b511335 --- /dev/null +++ b/examples/smtp-client/README.md @@ -0,0 +1,5 @@ +# SMTP client example + +This example shows how to send emails via Gmail. +Before running this example, open main.c and modify settings at the top +of the file. diff --git a/examples/smtp-client/ca.pem b/examples/smtp-client/ca.pem new file mode 120000 index 00000000..8addd9e2 --- /dev/null +++ b/examples/smtp-client/ca.pem @@ -0,0 +1 @@ +../../test/data/ca.pem \ No newline at end of file diff --git a/examples/smtp-client/main.c b/examples/smtp-client/main.c new file mode 100644 index 00000000..79fac887 --- /dev/null +++ b/examples/smtp-client/main.c @@ -0,0 +1,80 @@ +#include "mongoose.h" + +static const char *user = "aaa@gmail.com"; // Change this! Your gmail account +static const char *pass = "xxxxxxxxxxxxx"; // Change this! Your gmail password +static const char *to = "bbb@gmail.com"; // Change this! Destination email + +static const char *from = "My Mail Sender"; +static const char *subj = "test email from mongoose library!"; +static const char *mesg = "Hi!\nThis is a test message.\nBye."; + +static bool s_quit; + +enum { EHLO, STARTTLS, STARTTLS_WAIT, AUTH, FROM, TO, DATA, BODY, QUIT, END }; + +static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + uint8_t *state = (uint8_t *) c->label; + if (ev == MG_EV_OPEN) { + // c->is_hexdumping = 1; + } else if (ev == MG_EV_READ) { + if (c->recv.len > 0 && c->recv.buf[c->recv.len - 1] == '\n') { + MG_INFO(("<-- %.*s", (int) c->recv.len - 2, c->recv.buf)); + c->recv.len = 0; + if (*state == EHLO) { + mg_printf(c, "EHLO gmail.com\r\n"); + *state = c->is_tls ? AUTH : STARTTLS; + } else if (*state == STARTTLS) { + mg_printf(c, "STARTTLS\r\n"); + *state = STARTTLS_WAIT; + } else if (*state == STARTTLS_WAIT) { + struct mg_tls_opts opts = {.ca = "/tmp/ca.pem"}; + mg_tls_init(c, &opts); + *state = EHLO; + } else if (*state == AUTH) { + char a[100], b[300] = ""; + size_t n = mg_snprintf(a, sizeof(a), "%c%s%c%s", 0, user, 0, pass); + mg_base64_encode((uint8_t *) a, n, b); + mg_printf(c, "AUTH PLAIN %s\r\n", b); + *state = FROM; + } else if (*state == FROM) { + mg_printf(c, "MAIL FROM: <%s>\r\n", user); + *state = TO; + } else if (*state == TO) { + mg_printf(c, "RCPT TO: <%s>\r\n", to); + *state = DATA; + } else if (*state == DATA) { + mg_printf(c, "DATA\r\n"); + *state = BODY; + } else if (*state == BODY) { + mg_printf(c, "From: %s <%s>\r\n", from, user); // Mail header + mg_printf(c, "Subject: %s\r\n", subj); // Mail header + mg_printf(c, "\r\n"); // End of headers + mg_printf(c, "%s\r\n", mesg); // Mail body + mg_printf(c, ".\r\n"); // End of body + *state = QUIT; + } else if (*state == QUIT) { + mg_printf(c, "QUIT\r\n"); + *state = END; + } else { + c->is_draining = 1; + MG_INFO(("end")); + } + MG_INFO(("--> %.*s", (int) c->send.len - 2, c->send.buf)); + } + } else if (ev == MG_EV_CLOSE) { + s_quit = true; + } else if (ev == MG_EV_TLS_HS) { + MG_INFO(("TLS handshake done!")); + mg_printf(c, "EHLO gmail.com\r\n"); + } + (void) fn_data, (void) ev_data; +} + +int main(void) { + struct mg_mgr mgr; + mg_mgr_init(&mgr); + // mg_log_set(MG_LL_VERBOSE); + mg_connect(&mgr, "tcp://smtp.gmail.com:587", fn, NULL); + while (s_quit == false) mg_mgr_poll(&mgr, 1000); + return 0; +} diff --git a/mongoose.c b/mongoose.c index 50c99cd6..f125bd52 100644 --- a/mongoose.c +++ b/mongoose.c @@ -5101,6 +5101,7 @@ void mg_tls_handshake(struct mg_connection *c) { if (rc == 0) { // Success MG_DEBUG(("%lu success", c->id)); c->is_tls_hs = 0; + mg_call(c, MG_EV_TLS_HS, NULL); } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting, @@ -5238,7 +5239,7 @@ size_t mg_tls_pending(struct mg_connection *c) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); - if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) + if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_IO_WAIT; if (n <= 0) return MG_IO_ERR; return n; @@ -5375,6 +5376,7 @@ void mg_tls_handshake(struct mg_connection *c) { if (rc == 1) { MG_DEBUG(("%lu success", c->id)); c->is_tls_hs = 0; + mg_call(c, MG_EV_TLS_HS, NULL); } else { int code = mg_tls_err(tls, rc); if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code); diff --git a/mongoose.h b/mongoose.h index 4004d24e..f4799f2b 100644 --- a/mongoose.h +++ b/mongoose.h @@ -993,6 +993,7 @@ enum { MG_EV_RESOLVE, // Host name is resolved NULL MG_EV_CONNECT, // Connection established NULL MG_EV_ACCEPT, // Connection accepted NULL + MG_EV_TLS_HS, // TLS handshake succeeded NULL MG_EV_READ, // Data received from socket long *bytes_read MG_EV_WRITE, // Data written to socket long *bytes_written MG_EV_CLOSE, // Connection closed NULL diff --git a/src/event.h b/src/event.h index 583f81bc..e50fdae8 100644 --- a/src/event.h +++ b/src/event.h @@ -13,6 +13,7 @@ enum { MG_EV_RESOLVE, // Host name is resolved NULL MG_EV_CONNECT, // Connection established NULL MG_EV_ACCEPT, // Connection accepted NULL + MG_EV_TLS_HS, // TLS handshake succeeded NULL MG_EV_READ, // Data received from socket long *bytes_read MG_EV_WRITE, // Data written to socket long *bytes_written MG_EV_CLOSE, // Connection closed NULL diff --git a/src/tls_mbed.c b/src/tls_mbed.c index edfa91a4..1663709f 100644 --- a/src/tls_mbed.c +++ b/src/tls_mbed.c @@ -48,6 +48,7 @@ void mg_tls_handshake(struct mg_connection *c) { if (rc == 0) { // Success MG_DEBUG(("%lu success", c->id)); c->is_tls_hs = 0; + mg_call(c, MG_EV_TLS_HS, NULL); } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { // Still pending MG_VERBOSE(("%lu pending, %d%d %d (-%#x)", c->id, c->is_connecting, @@ -185,7 +186,7 @@ size_t mg_tls_pending(struct mg_connection *c) { long mg_tls_recv(struct mg_connection *c, void *buf, size_t len) { struct mg_tls *tls = (struct mg_tls *) c->tls; long n = mbedtls_ssl_read(&tls->ssl, (unsigned char *) buf, len); - if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) + if (n == MBEDTLS_ERR_SSL_WANT_READ || n == MBEDTLS_ERR_SSL_WANT_WRITE) return MG_IO_WAIT; if (n <= 0) return MG_IO_ERR; return n; diff --git a/src/tls_openssl.c b/src/tls_openssl.c index 2459ccac..7f40137a 100644 --- a/src/tls_openssl.c +++ b/src/tls_openssl.c @@ -116,6 +116,7 @@ void mg_tls_handshake(struct mg_connection *c) { if (rc == 1) { MG_DEBUG(("%lu success", c->id)); c->is_tls_hs = 0; + mg_call(c, MG_EV_TLS_HS, NULL); } else { int code = mg_tls_err(tls, rc); if (code != 0) mg_error(c, "tls hs: rc %d, err %d", rc, code);