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 0000000000..5a0514f7b1 Binary files /dev/null and b/examples/protocols/smtp_client/main/esp_logo.png differ diff --git a/examples/protocols/smtp_client/main/server_root_cert.pem b/examples/protocols/smtp_client/main/server_root_cert.pem new file mode 100644 index 0000000000..e683c8374f --- /dev/null +++ b/examples/protocols/smtp_client/main/server_root_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIESjCCAzKgAwIBAgINAeO0mqGNiqmBJWlQuDANBgkqhkiG9w0BAQsFADBMMSAw +HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs +U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy +MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg +U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxTzEwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDQGM9F1IvN05zkQO9+tN1pIRvJzzyOTHW5DzEZhD2ePCnv +UA0Qk28FgICfKqC9EksC4T2fWBYk/jCfC3R3VZMdS/dN4ZKCEPZRrAzDsiKUDzRr +mBBJ5wudgzndIMYcLe/RGGFl5yODIKgjEv/SJH/UL+dEaltN11BmsK+eQmMF++Ac +xGNhr59qM/9il71I2dN8FGfcddwuaej4bXhp0LcQBbjxMcI7JP0aM3T4I+DsaxmK +FsbjzaTNC9uzpFlgOIg7rR25xoynUxv8vNmkq7zdPGHXkxWY7oG9j+JkRyBABk7X +rJfoucBZEqFJJSPk7XA0LKW0Y3z5oz2D0c1tJKwHAgMBAAGjggEzMIIBLzAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud +EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJjR+G4Q68+b7GCfGJAboOt9Cf0rMB8G +A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl +BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp +MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g +BDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y +ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAGoA+Nnn78y6pRjd9XlQWNa7H +TgiZ/r3RNGkmUmYHPQq6Scti9PEajvwRT2iWTHQr02fesqOqBY2ETUwgZQ+lltoN +FvhsO9tvBCOIazpswWC9aJ9xju4tWDQH8NVU6YZZ/XteDSGU9YzJqPjY8q3MDxrz +mqepBCf5o8mw/wJ4a2G6xzUr6Fb6T8McDO22PLRL6u3M4Tzs3A2M1j6bykJYi8wW +IRdAvKLWZu/axBVbzYmqmwkm5zLSDW5nIAJbELCQCZwMH56t2Dvqofxs6BBcCFIZ +USpxu6x6td0V7SvJCCosirSmIatj/9dSSVDQibet8q/7UK4v4ZUN80atnZz1yg== +-----END CERTIFICATE----- diff --git a/examples/protocols/smtp_client/main/smtp_client_example_main.c b/examples/protocols/smtp_client/main/smtp_client_example_main.c new file mode 100644 index 0000000000..36b19b6ee4 --- /dev/null +++ b/examples/protocols/smtp_client/main/smtp_client_example_main.c @@ -0,0 +1,513 @@ +/** + * SMTP email client + * + * Adapted from the `ssl_mail_client` example in mbedtls. + * + * Original Copyright (C) 2006-2016, ARM Limited, All Rights Reserved, Apache 2.0 License. + * Additions Copyright (C) Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD, Apache 2.0 License. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#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); +}