From 405186c86642568b1b4028e27a909a16fd90e78a Mon Sep 17 00:00:00 2001 From: Vikram Dattu Date: Mon, 13 Jan 2020 18:23:55 +0530 Subject: [PATCH] Added simple SMTP email client. This is based on `ssl_mail_client` from mbedtls. The client also adds functionality to send attachments. Signed-off-by: Vikram Dattu --- .../main/openssl_client_example_main.c | 7 +- examples/protocols/smtp_client/CMakeLists.txt | 9 + examples/protocols/smtp_client/Makefile | 10 + examples/protocols/smtp_client/README.md | 78 +++ .../protocols/smtp_client/main/CMakeLists.txt | 2 + .../smtp_client/main/Kconfig.projbuild | 33 ++ .../protocols/smtp_client/main/component.mk | 5 + .../protocols/smtp_client/main/esp_logo.png | Bin 0 -> 30977 bytes .../smtp_client/main/server_root_cert.pem | 25 + .../main/smtp_client_example_main.c | 513 ++++++++++++++++++ 10 files changed, 678 insertions(+), 4 deletions(-) create mode 100644 examples/protocols/smtp_client/CMakeLists.txt create mode 100644 examples/protocols/smtp_client/Makefile create mode 100644 examples/protocols/smtp_client/README.md create mode 100644 examples/protocols/smtp_client/main/CMakeLists.txt create mode 100644 examples/protocols/smtp_client/main/Kconfig.projbuild create mode 100644 examples/protocols/smtp_client/main/component.mk create mode 100644 examples/protocols/smtp_client/main/esp_logo.png create mode 100644 examples/protocols/smtp_client/main/server_root_cert.pem create mode 100644 examples/protocols/smtp_client/main/smtp_client_example_main.c diff --git a/examples/protocols/openssl_client/main/openssl_client_example_main.c b/examples/protocols/openssl_client/main/openssl_client_example_main.c index 8e917e66d4..bd808fa346 100644 --- a/examples/protocols/openssl_client/main/openssl_client_example_main.c +++ b/examples/protocols/openssl_client/main/openssl_client_example_main.c @@ -37,10 +37,10 @@ static void openssl_example_task(void *p) struct sockaddr_in sock_addr; struct hostent *hp; struct ip4_addr *ip4_addr; - + int recv_bytes = 0; char recv_buf[OPENSSL_EXAMPLE_RECV_BUF_LEN]; - + const char send_data[] = OPENSSL_EXAMPLE_REQUEST; const int send_bytes = sizeof(send_data); @@ -134,7 +134,7 @@ static void openssl_example_task(void *p) recv_bytes += ret; ESP_LOGI(TAG, "%s", recv_buf); } while (1); - + ESP_LOGI(TAG, "totally read %d bytes data from %s ......", recv_bytes, OPENSSL_EXAMPLE_TARGET_NAME); failed5: @@ -172,7 +172,6 @@ static void openssl_example_client_init(void) void app_main(void) { - ESP_ERROR_CHECK( nvs_flash_init() ); ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); diff --git a/examples/protocols/smtp_client/CMakeLists.txt b/examples/protocols/smtp_client/CMakeLists.txt new file mode 100644 index 0000000000..bc6c2b8954 --- /dev/null +++ b/examples/protocols/smtp_client/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(smtp_client) diff --git a/examples/protocols/smtp_client/Makefile b/examples/protocols/smtp_client/Makefile new file mode 100644 index 0000000000..b0d979618a --- /dev/null +++ b/examples/protocols/smtp_client/Makefile @@ -0,0 +1,10 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := smtp_client + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/smtp_client/README.md b/examples/protocols/smtp_client/README.md new file mode 100644 index 0000000000..55e8ec3ed7 --- /dev/null +++ b/examples/protocols/smtp_client/README.md @@ -0,0 +1,78 @@ +# SMTP Client Example + +The Example is SMTP client demo. It sends and email with attachment to recipient. + + +## How to use the example + + +### Configure the project + +``` +idf.py menuconfig +``` + +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../README.md) for more details. + +Please set the following parameters under example config for SMTP client demo to work: + - Email server, port, sender's email ID, password, recipient's email ID. + + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + + + +## Example output + +``` +I (3212) smtp_example: Loading the CA root certificate... +I (3212) smtp_example: Setting hostname for TLS session... +I (3222) smtp_example: Setting up the SSL/TLS structure... +I (3232) smtp_example: Connecting to smtp.googlemail.com:587... +I (3542) smtp_example: Connected. + +220 smtp.googlemail.com ESMTP r62sm20390571pfc.89 - gsmtp +I (3952) smtp_example: Writing EHLO to server... + +250-smtp.googlemail.com at your service, [182.75.158.118] +250-SIZE 35882577 +250-8BITMIME +250-STARTTLS +250-ENHANCEDSTATUSCOD +ES +250-PIPELINING +250-CHUNKING +250 SMTPUTF8 +I (4262) smtp_example: Writing STARTTLS to server... + +220 2.0.0 Ready to start TLS +I (4562) smtp_example: Performing the SSL/TLS handshake... +I (5692) smtp_example: Verifying peer X.509 certificate... +I (5692) smtp_example: Certificate verified. +I (5692) smtp_example: Cipher suite is TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256 +I (5702) smtp_example: Authentication... +I (5702) smtp_example: Write AUTH LOGIN +I (6002) smtp_example: Write USER NAME +I (6302) smtp_example: Write PASSWORD +I (6822) smtp_example: Write MAIL FROM +I (7132) smtp_example: Write RCPT +I (7432) smtp_example: Write DATA +I (8252) smtp_example: Write Content +I (10202) smtp_example: Email sent! +``` + + +## Note: + - You might need to enable permission for less secure apps from email provider. This is because these email providers (e.g. gmail) insist on OAUTH authorization. diff --git a/examples/protocols/smtp_client/main/CMakeLists.txt b/examples/protocols/smtp_client/main/CMakeLists.txt new file mode 100644 index 0000000000..4a0228594c --- /dev/null +++ b/examples/protocols/smtp_client/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "smtp_client_example_main.c" + EMBED_TXTFILES server_root_cert.pem esp_logo.png) diff --git a/examples/protocols/smtp_client/main/Kconfig.projbuild b/examples/protocols/smtp_client/main/Kconfig.projbuild new file mode 100644 index 0000000000..1d8d11cf74 --- /dev/null +++ b/examples/protocols/smtp_client/main/Kconfig.projbuild @@ -0,0 +1,33 @@ +menu "Example Configuration" + + config SMTP_SERVER + string "Mail Server" + default "smtp.googlemail.com" + help + Target domain for the example to connect to. + + config SMTP_PORT_NUMBER + string "Mail port number" + default "587" + help + Mail port number for the example to connect to. + + config SMTP_SENDER_MAIL + string "Sender email" + default "sender.email@gmail.com" + help + Sender's Email address + + config SMTP_SENDER_PASSWORD + string "Sender password" + default "password@123" + help + Sender's email password + + config SMTP_RECIPIENT_MAIL + string "Recipient email" + default "recipient.email@gmail.com" + help + Recipient's email + +endmenu diff --git a/examples/protocols/smtp_client/main/component.mk b/examples/protocols/smtp_client/main/component.mk new file mode 100644 index 0000000000..f52e1c7db4 --- /dev/null +++ b/examples/protocols/smtp_client/main/component.mk @@ -0,0 +1,5 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# + +COMPONENT_EMBED_TXTFILES := server_root_cert.pem esp_logo.png diff --git a/examples/protocols/smtp_client/main/esp_logo.png b/examples/protocols/smtp_client/main/esp_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5a0514f7b12e9c3a4779decd57f222bc5b316efa GIT binary patch literal 30977 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 X;?by +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "protocol_examples_common.h" + +#include "mbedtls/platform.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/esp_debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" +#include +#include + + +/* Constants that are configurable in menuconfig */ +#define MAIL_SERVER CONFIG_SMTP_SERVER +#define MAIL_PORT CONFIG_SMTP_PORT_NUMBER +#define SENDER_MAIL CONFIG_SMTP_SENDER_MAIL +#define SENDER_PASSWORD CONFIG_SMTP_SENDER_PASSWORD +#define RECIPIENT_MAIL CONFIG_SMTP_RECIPIENT_MAIL + +#define SERVER_USES_STARTSSL 1 + +static const char *TAG = "smtp_example"; + +#define TASK_STACK_SIZE (8 * 1024) +#define BUF_SIZE 512 + +#define VALIDATE_MBEDTLS_RETURN(ret, min_valid_ret, max_valid_ret, goto_label) \ + do { \ + if (ret < min_valid_ret || ret > max_valid_ret) { \ + goto goto_label; \ + } \ + } while (0) \ + +/** + * Root cert for smtp.googlemail.com, taken from server_root_cert.pem + * + * The PEM file was extracted from the output of this command: + * openssl s_client -showcerts -connect smtp.googlemail.com:587 -starttls smtp + * + * The CA root cert is the last cert given in the chain of certs. + * + * To embed it in the app binary, the PEM file is named + * in the component.mk COMPONENT_EMBED_TXTFILES variable. + */ + +extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start"); +extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end"); + +extern const uint8_t esp_logo_png_start[] asm("_binary_esp_logo_png_start"); +extern const uint8_t esp_logo_png_end[] asm("_binary_esp_logo_png_end"); + +static int write_and_get_response(mbedtls_net_context *sock_fd, unsigned char *buf, size_t len) +{ + int ret; + const size_t DATA_SIZE = 128; + unsigned char data[DATA_SIZE]; + char code[4]; + size_t i, idx = 0; + + if (len) { + ESP_LOGD(TAG, "%s", buf); + } + + if (len && (ret = mbedtls_net_send(sock_fd, buf, len)) <= 0) { + ESP_LOGE(TAG, "mbedtls_net_send failed with error -0x%x", -ret); + return ret; + } + + do { + len = DATA_SIZE - 1; + ret = mbedtls_net_recv(sock_fd, data, len); + + if (ret <= 0) { + ESP_LOGE(TAG, "mbedtls_net_recv failed with error -0x%x", -ret); + goto exit; + } + + data[len] = '\0'; + printf("\n%s", data); + len = ret; + for (i = 0; i < len; i++) { + if (data[i] != '\n') { + if (idx < 4) { + code[idx++] = data[i]; + } + continue; + } + + if (idx == 4 && code[0] >= '0' && code[0] <= '9' && code[3] == ' ') { + code[3] = '\0'; + ret = atoi(code); + goto exit; + } + + idx = 0; + } + } while (1); + +exit: + return ret; +} + +static int write_ssl_and_get_response(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len) +{ + int ret; + const size_t DATA_SIZE = 128; + unsigned char data[DATA_SIZE]; + char code[4]; + size_t i, idx = 0; + + if (len) { + ESP_LOGD(TAG, "%s", buf); + } + + while (len && (ret = mbedtls_ssl_write(ssl, buf, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "mbedtls_ssl_write failed with error -0x%x", -ret); + goto exit; + } + } + + do { + len = DATA_SIZE - 1; + ret = mbedtls_ssl_read(ssl, data, len); + + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + continue; + } + + if (ret <= 0) { + ESP_LOGE(TAG, "mbedtls_ssl_read failed with error -0x%x", -ret); + goto exit; + } + + ESP_LOGD(TAG, "%s", data); + + len = ret; + for (i = 0; i < len; i++) { + if (data[i] != '\n') { + if (idx < 4) { + code[idx++] = data[i]; + } + continue; + } + + if (idx == 4 && code[0] >= '0' && code[0] <= '9' && code[3] == ' ') { + code[3] = '\0'; + ret = atoi(code); + goto exit; + } + + idx = 0; + } + } while (1); + +exit: + return ret; +} + +static int write_ssl_data(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len) +{ + int ret; + + if (len) { + ESP_LOGD(TAG, "%s", buf); + } + + while (len && (ret = mbedtls_ssl_write(ssl, buf, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "mbedtls_ssl_write failed with error -0x%x", -ret); + return ret; + } + } + + return 0; +} + +static int perform_tls_handshake(mbedtls_ssl_context *ssl) +{ + int ret = -1; + uint32_t flags; + char *buf = NULL; + buf = (char *) calloc(1, BUF_SIZE); + if (buf == NULL) { + ESP_LOGE(TAG, "calloc failed for size %d", BUF_SIZE); + goto exit; + } + + ESP_LOGI(TAG, "Performing the SSL/TLS handshake..."); + + fflush(stdout); + while ((ret = mbedtls_ssl_handshake(ssl)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret); + goto exit; + } + } + + ESP_LOGI(TAG, "Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(ssl)) != 0) { + /* In real life, we probably want to close connection if ret != 0 */ + ESP_LOGW(TAG, "Failed to verify peer certificate!"); + mbedtls_x509_crt_verify_info(buf, BUF_SIZE, " ! ", flags); + ESP_LOGW(TAG, "verification info: %s", buf); + } else { + ESP_LOGI(TAG, "Certificate verified."); + } + + ESP_LOGI(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(ssl)); + ret = 0; /* No error */ + +exit: + if (buf) { + free(buf); + } + return ret; +} + +static void smtp_client_task(void *pvParameters) +{ + char *buf = NULL; + unsigned char base64_buffer[128]; + int ret, len; + size_t base64_len; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_x509_crt cacert; + mbedtls_ssl_config conf; + mbedtls_net_context server_fd; + + mbedtls_ssl_init(&ssl); + mbedtls_x509_crt_init(&cacert); + mbedtls_ctr_drbg_init(&ctr_drbg); + ESP_LOGI(TAG, "Seeding the random number generator"); + + mbedtls_ssl_config_init(&conf); + + mbedtls_entropy_init(&entropy); + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + NULL, 0)) != 0) { + ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%x", -ret); + goto exit; + } + + ESP_LOGI(TAG, "Loading the CA root certificate..."); + + ret = mbedtls_x509_crt_parse(&cacert, server_root_cert_pem_start, + server_root_cert_pem_end - server_root_cert_pem_start); + + if (ret < 0) { + ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret); + goto exit; + } + + ESP_LOGI(TAG, "Setting hostname for TLS session..."); + + /* Hostname set here should match CN in server certificate */ + if ((ret = mbedtls_ssl_set_hostname(&ssl, MAIL_SERVER)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret); + goto exit; + } + + ESP_LOGI(TAG, "Setting up the SSL/TLS structure..."); + + if ((ret = mbedtls_ssl_config_defaults(&conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned -0x%x", -ret); + goto exit; + } + + mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); + mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); +#ifdef CONFIG_MBEDTLS_DEBUG + mbedtls_esp_enable_debug_log(&conf, 4); +#endif + + if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { + ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x", -ret); + goto exit; + } + + mbedtls_net_init(&server_fd); + + ESP_LOGI(TAG, "Connecting to %s:%s...", MAIL_SERVER, MAIL_PORT); + + if ((ret = mbedtls_net_connect(&server_fd, MAIL_SERVER, + MAIL_PORT, MBEDTLS_NET_PROTO_TCP)) != 0) { + ESP_LOGE(TAG, "mbedtls_net_connect returned -0x%x", -ret); + goto exit; + } + + ESP_LOGI(TAG, "Connected."); + + mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + buf = (char *) calloc(1, BUF_SIZE); + if (buf == NULL) { + ESP_LOGE(TAG, "calloc failed for size %d", BUF_SIZE); + goto exit; + } +#if SERVER_USES_STARTSSL + /* Get response */ + ret = write_and_get_response(&server_fd, (unsigned char *) buf, 0); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + + ESP_LOGI(TAG, "Writing EHLO to server..."); + len = snprintf((char *) buf, BUF_SIZE, "EHLO %s\r\n", "ESP32"); + ret = write_and_get_response(&server_fd, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + + ESP_LOGI(TAG, "Writing STARTTLS to server..."); + len = snprintf((char *) buf, BUF_SIZE, "STARTTLS\r\n"); + ret = write_and_get_response(&server_fd, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + + ret = perform_tls_handshake(&ssl); + if (ret != 0) { + goto exit; + } + +#else /* SERVER_USES_STARTSSL */ + ret = perform_tls_handshake(&ssl); + if (ret != 0) { + goto exit; + } + + /* Get response */ + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, 0); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + ESP_LOGI(TAG, "Writing EHLO to server..."); + + len = snprintf((char *) buf, BUF_SIZE, "EHLO %s\r\n", "ESP32"); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + +#endif /* SERVER_USES_STARTSSL */ + + /* Authentication */ + ESP_LOGI(TAG, "Authentication..."); + + ESP_LOGI(TAG, "Write AUTH LOGIN"); + len = snprintf( (char *) buf, BUF_SIZE, "AUTH LOGIN\r\n" ); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 399, exit); + + ESP_LOGI(TAG, "Write USER NAME"); + ret = mbedtls_base64_encode((unsigned char *) base64_buffer, sizeof(base64_buffer), + &base64_len, (unsigned char *) SENDER_MAIL, strlen(SENDER_MAIL)); + if (ret != 0) { + ESP_LOGE(TAG, "Error in mbedtls encode! ret = -0x%x", -ret); + goto exit; + } + len = snprintf((char *) buf, BUF_SIZE, "%s\r\n", base64_buffer); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 300, 399, exit); + + ESP_LOGI(TAG, "Write PASSWORD"); + ret = mbedtls_base64_encode((unsigned char *) base64_buffer, sizeof(base64_buffer), + &base64_len, (unsigned char *) SENDER_PASSWORD, strlen(SENDER_PASSWORD)); + if (ret != 0) { + ESP_LOGE(TAG, "Error in mbedtls encode! ret = -0x%x", -ret); + goto exit; + } + len = snprintf((char *) buf, BUF_SIZE, "%s\r\n", base64_buffer); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 399, exit); + + /* Compose email */ + ESP_LOGI(TAG, "Write MAIL FROM"); + len = snprintf((char *) buf, BUF_SIZE, "MAIL FROM:<%s>\r\n", SENDER_MAIL); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + + ESP_LOGI(TAG, "Write RCPT"); + len = snprintf((char *) buf, BUF_SIZE, "RCPT TO:<%s>\r\n", RECIPIENT_MAIL); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + + ESP_LOGI(TAG, "Write DATA"); + len = snprintf((char *) buf, BUF_SIZE, "DATA\r\n"); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 300, 399, exit); + + ESP_LOGI(TAG, "Write Content"); + /* We do not take action if message sending is partly failed. */ + len = snprintf((char *) buf, BUF_SIZE, + "From: %s\r\nSubject: mbed TLS Test mail\r\n" + "To: %s\r\n" + "MIME-Version: 1.0 (mime-construct 1.9)\n", + "ESP32 SMTP Client", RECIPIENT_MAIL); + + /** + * Note: We are not validating return for some ssl_writes. + * If by chance, it's failed; at worst email will be incomplete! + */ + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + + /* Multipart boundary */ + len = snprintf((char *) buf, BUF_SIZE, + "Content-Type: multipart/mixed;boundary=XYZabcd1234\n" + "--XYZabcd1234\n"); + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + + /* Text */ + len = snprintf((char *) buf, BUF_SIZE, + "Content-Type: text/plain\n" + "This is a simple test mail from the SMTP client example.\r\n" + "\r\n" + "Enjoy!\n\n--XYZabcd1234\n"); + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + + /* Attachment */ + len = snprintf((char *) buf, BUF_SIZE, + "Content-Type: image/image/png;name=esp_logo.png\n" + "Content-Transfer-Encoding: base64\n" + "Content-Disposition:attachment;filename=\"esp_logo.png\"\n"); + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + + /* Image contents... */ + const uint8_t *offset = esp_logo_png_start; + while (offset < esp_logo_png_end - 1) { + int read_bytes = MIN(((sizeof (base64_buffer) - 1) / 4) * 3, esp_logo_png_end - offset - 1); + ret = mbedtls_base64_encode((unsigned char *) base64_buffer, sizeof(base64_buffer), + &base64_len, (unsigned char *) offset, read_bytes); + if (ret != 0) { + ESP_LOGE(TAG, "Error in mbedtls encode! ret = -0x%x", -ret); + goto exit; + } + offset += read_bytes; + len = snprintf((char *) buf, BUF_SIZE, "%s\r\n", base64_buffer); + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + } + + len = snprintf((char *) buf, BUF_SIZE, "\n--XYZabcd1234\n"); + ret = write_ssl_data(&ssl, (unsigned char *) buf, len); + + len = snprintf((char *) buf, BUF_SIZE, "\r\n.\r\n"); + ret = write_ssl_and_get_response(&ssl, (unsigned char *) buf, len); + VALIDATE_MBEDTLS_RETURN(ret, 200, 299, exit); + ESP_LOGI(TAG, "Email sent!"); + + /* Close connection */ + mbedtls_ssl_close_notify(&ssl); + ret = 0; /* No errors */ + +exit: + mbedtls_ssl_session_reset(&ssl); + mbedtls_net_free(&server_fd); + + if (ret != 0) { + mbedtls_strerror(ret, buf, 100); + ESP_LOGE(TAG, "Last error was: -0x%x - %s", -ret, buf); + } + + putchar('\n'); /* Just a new line */ + if (buf) { + free(buf); + } + vTaskDelete(NULL); +} + +void app_main() +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /** + * This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + xTaskCreate(&smtp_client_task, "smtp_client_task", TASK_STACK_SIZE, NULL, 5, NULL); +}