Skip to content

Commit

Permalink
Add support non-ascii characters (UTF-8) in SMTP message.
Browse files Browse the repository at this point in the history
  • Loading branch information
mobizt committed Nov 15, 2023
1 parent 1b22075 commit dcb32c4
Show file tree
Hide file tree
Showing 19 changed files with 74 additions and 54 deletions.
16 changes: 7 additions & 9 deletions examples/SMTP/Send_Text/Send_Text.ino
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void setup()
SMTP_Message message;

/* Set the message headers */
message.sender.name = F("ESP Mail");
message.sender.name = F("Me (我)");
message.sender.email = AUTHOR_EMAIL;

/** If author and sender are not identical
Expand All @@ -234,16 +234,14 @@ void setup()
// in form of `encoded-words` per RFC2047
// https://datatracker.ietf.org/doc/html/rfc2047

String subject = "Test sending message (中文电子邮件)";
String encoded_subject = "=?utf-8?B?";
encoded_subject += MailClient.toBase64(subject);
encoded_subject += "?=";
String subject = "Test sending a message (メッセージの送信をテストする)";
message.subject = subject;

message.subject = encoded_subject;
message.addRecipient(F("Someone (誰か)"), RECIPIENT_EMAIL);

message.addRecipient(F("Someone"), RECIPIENT_EMAIL);

String textMsg = "This is simple plain text message with Chinese words (中文电子邮件) in subject and body";
String textMsg = "This is simple plain text message which contains Chinese and Japanese words.\n";
textMsg += "这是简单的纯文本消息,包含中文和日文单词\n";
textMsg += "これは中国語と日本語を含む単純なプレーンテキストメッセージです\n";

message.text.content = textMsg;

Expand Down
2 changes: 1 addition & 1 deletion examples/SMTP/Send_Text_Flowed/Send_Text_Flowed.ino
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ void setup()
/* Set the message headers */
message.sender.name = F("ESP Mail");
message.sender.email = AUTHOR_EMAIL;
message.subject = F("Test sending plain text Email");
message.subject = F("Test sending a plain text message");
message.addRecipient(F("Someone"), RECIPIENT_EMAIL);

/** The option to add soft line break to to the message for
Expand Down
4 changes: 2 additions & 2 deletions examples/SMTP/Set_Time/Set_Time.ino
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ void setup()

RTC.begin();

// RTCTime startTime(30, Month::JUNE, 2023, 13, 37, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
// RTC.setTime(startTime);
RTCTime startTime(30, Month::JUNE, 2023, 13, 37, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(startTime);

RTCTime currentTime;

Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ESP Mail Client",
"version": "3.4.14",
"version": "3.4.15",
"keywords": "communication, email, imap, smtp, esp32, esp8266, samd, arduino",
"description": "Arduino E-Mail Client Library to send, read and get incoming email notification for ESP32, ESP8266 and SAMD21 devices. The library also supported other Arduino Devices using Clients interfaces e.g. WiFiClient, EthernetClient, and GSMClient.",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name=ESP Mail Client

version=3.4.14
version=3.4.15

author=Mobizt

Expand Down
21 changes: 18 additions & 3 deletions src/ESP_Mail_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down Expand Up @@ -798,7 +798,7 @@ void ESP_Mail_Client::appendHeaderField(MB_String &buf, const char *name, PGM_P
appendString(buf, value, comma, newLine, type);
}

void ESP_Mail_Client::appendAddressHeaderField(MB_String &buf, esp_mail_address_info_t &source, esp_mail_rfc822_header_field_types type, bool header, bool comma, bool newLine)
void ESP_Mail_Client::appendAddressHeaderField(MB_String &buf, esp_mail_address_info_t &source, esp_mail_rfc822_header_field_types type, bool header, bool comma, bool newLine, bool encode)
{
// Construct header field.
if (header)
Expand All @@ -807,7 +807,7 @@ void ESP_Mail_Client::appendAddressHeaderField(MB_String &buf, esp_mail_address_
if (type != esp_mail_rfc822_header_field_cc && type != esp_mail_rfc822_header_field_bcc &&
source.name.length() > 0)
{
appendString(buf, source.name.c_str(), false, false, esp_mail_string_mark_type_double_quote);
appendString(buf, encode ? encodeBUTF8(source.name.c_str()).c_str() : source.name.c_str(), false, false, esp_mail_string_mark_type_double_quote);
// Add white space after name for SMTP to fix iCloud Mail Service IMAP search compatibility issue #278
// This is not restricted by rfc2822.
appendSpace(buf);
Expand Down Expand Up @@ -976,6 +976,21 @@ MB_String ESP_Mail_Client::mGetBase64(MB_StringPtr str)
return encodeBase64Str((uint8_t *)(data.c_str()), data.length());
}

MB_String ESP_Mail_Client::encodeBUTF8(const char *src)
{
MB_String buff;
size_t len = strlen(src);
if (len > 4 && src[0] != '=' && src[1] != '?' && src[len - 1] != '=' && src[len - 2] != '?')
{
buff = "=?utf-8?B?";
buff += toBase64(src);
buff += "?=";
}
else
buff = src;
return buff;
}

int ESP_Mail_Client::readLine(ESP_Mail_TCPClient *client, char *buf, int bufLen, bool withLineBreak, int &count, bool &ovf, unsigned long timeoutSec, bool &isTimeout)
{
int ret = -1;
Expand Down
7 changes: 5 additions & 2 deletions src/ESP_Mail_Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define ESP_MAIL_CLIENT_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down Expand Up @@ -1189,6 +1189,9 @@ class ESP_Mail_Client

// Decode base64 encoded string
MB_String mGetBase64(MB_StringPtr str);

// RFC2047 encode word (UTF-8 only)
MB_String encodeBUTF8(const char* str);

// Sub string
char *subStr(const char *buf, PGM_P beginToken, PGM_P endToken, int beginPos, int endPos = 0, bool caseSensitive = true);
Expand Down Expand Up @@ -1245,7 +1248,7 @@ class ESP_Mail_Client
void appendHeaderField(MB_String &buf, const char *name, PGM_P value, bool comma, bool newLine, esp_mail_string_mark_type type = esp_mail_string_mark_type_none);

// Append SMTP address header field
void appendAddressHeaderField(MB_String &buf, esp_mail_address_info_t &source, esp_mail_rfc822_header_field_types type, bool header, bool comma, bool newLine);
void appendAddressHeaderField(MB_String &buf, esp_mail_address_info_t &source, esp_mail_rfc822_header_field_types type, bool header, bool comma, bool newLine, bool encode);

// Append header field name to buffer
void appendHeaderName(MB_String &buf, const char *name, bool clear = false, bool lowercase = false, bool space = true);
Expand Down
4 changes: 2 additions & 2 deletions src/ESP_Mail_Client_Version.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

#ifndef ESP_MAIL_VERSION

#define ESP_MAIL_VERSION "3.4.14"
#define ESP_MAIL_VERSION_NUM 30414
#define ESP_MAIL_VERSION "3.4.15"
#define ESP_MAIL_VERSION_NUM 30415

/* The inconsistent file version checking to prevent mixed versions compilation. */
#define VALID_VERSION_CHECK(ver) (ver == ESP_MAIL_VERSION_NUM)
Expand Down
2 changes: 1 addition & 1 deletion src/ESP_Mail_Const.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define ESP_MAIL_CONST_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/ESP_Mail_Error.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#define ESP_MAIL_ERROR_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/ESP_Mail_FS.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define ESP_MAIL_CONFIG_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/ESP_Mail_IMAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#define ESP_MAIL_IMAP_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
34 changes: 17 additions & 17 deletions src/ESP_Mail_SMTP.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#define ESP_MAIL_SMTP_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down Expand Up @@ -557,12 +557,12 @@ bool ESP_Mail_Client::sendContent(SMTPSession *smtp, SMTP_Message *msg, bool clo
if (msg->sender.email.length() > 0 && msg->author.email.length() > 0 &&
strcmp(msg->sender.email.c_str(), msg->author.email.c_str()) != 0)
{
appendAddressHeaderField(buf2, msg->author, esp_mail_rfc822_header_field_from, true, false, true);
appendAddressHeaderField(buf2, msg->sender, esp_mail_rfc822_header_field_sender, true, false, true);
appendAddressHeaderField(buf2, msg->author, esp_mail_rfc822_header_field_from, true, false, true, true);
appendAddressHeaderField(buf2, msg->sender, esp_mail_rfc822_header_field_sender, true, false, true, true);
}
// If author and transmitter (agent) are identical, send only 'From' header
else if (msg->sender.email.length() > 0)
appendAddressHeaderField(buf2, msg->sender, esp_mail_rfc822_header_field_from, true, false, true);
appendAddressHeaderField(buf2, msg->sender, esp_mail_rfc822_header_field_from, true, false, true, true);

if (!imap && smtp)
{
Expand Down Expand Up @@ -602,7 +602,7 @@ bool ESP_Mail_Client::sendContent(SMTPSession *smtp, SMTP_Message *msg, bool clo

for (uint8_t i = 0; i < msg->_rcp.size(); i++)
{
appendAddressHeaderField(buf2, msg->_rcp[i], esp_mail_rfc822_header_field_to, i == 0, i > 0, i == msg->_rcp.size() - 1);
appendAddressHeaderField(buf2, msg->_rcp[i], esp_mail_rfc822_header_field_to, i == 0, i > 0, i == msg->_rcp.size() - 1, true);
if (!imap && smtp)
{
// only address
Expand Down Expand Up @@ -652,7 +652,7 @@ bool ESP_Mail_Client::sendContent(SMTPSession *smtp, SMTP_Message *msg, bool clo
// Construct 'Cc' header field.
for (uint8_t i = 0; i < msg->_cc.size(); i++)
{
appendAddressHeaderField(buf2, msg->_cc[i], esp_mail_rfc822_header_field_cc, i == 0, i > 0, i == msg->_cc.size() - 1);
appendAddressHeaderField(buf2, msg->_cc[i], esp_mail_rfc822_header_field_cc, i == 0, i > 0, i == msg->_cc.size() - 1, true);
if (!imap)
{
// only address
Expand Down Expand Up @@ -718,7 +718,7 @@ bool ESP_Mail_Client::sendContent(SMTPSession *smtp, SMTP_Message *msg, bool clo
return false;

MB_String s;
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_subject].text, msg->subject.c_str(), false, true);
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_subject].text, MailClient.encodeBUTF8(msg->subject.c_str()).c_str(), false, true);

if (msg->timestamp.tag.length() && msg->timestamp.format.length())
s.replaceAll(msg->timestamp.tag, Time.getDateTimeString(Time.getCurrentTimestamp(), msg->timestamp.format.c_str()));
Expand Down Expand Up @@ -805,10 +805,10 @@ bool ESP_Mail_Client::sendContent(SMTPSession *smtp, SMTP_Message *msg, bool clo
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_references].text, msg->references.c_str(), false, true);

if (msg->comments.length() > 0)
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_comments].text, msg->comments.c_str(), false, true);
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_comments].text, MailClient.encodeBUTF8(msg->comments.c_str()).c_str(), false, true);

if (msg->keywords.length() > 0)
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_keywords].text, msg->keywords.c_str(), false, true);
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_keywords].text, MailClient.encodeBUTF8(msg->keywords.c_str()).c_str(), false, true);

if (msg->messageID.length() > 0)
appendHeaderField(s, rfc822_headers[esp_mail_rfc822_header_field_msg_id].text, msg->messageID.c_str(), false, true, esp_mail_string_mark_type_angle_bracket);
Expand Down Expand Up @@ -998,11 +998,11 @@ void ESP_Mail_Client::getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg,

// Construct 'From' header field.
if (msg->from.email.length() > 0)
appendAddressHeaderField(buf, msg->from, esp_mail_rfc822_header_field_from, true, false, true);
appendAddressHeaderField(buf, msg->from, esp_mail_rfc822_header_field_from, true, false, true, true);

// Construct 'Sender' header field.
if (msg->sender.email.length() > 0)
appendAddressHeaderField(buf, msg->sender, esp_mail_rfc822_header_field_sender, true, false, true);
appendAddressHeaderField(buf, msg->sender, esp_mail_rfc822_header_field_sender, true, false, true, true);

if (msg->response.reply_to.length() > 0)
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_reply_to].text, msg->response.reply_to.c_str(), false, true, esp_mail_string_mark_type_angle_bracket);
Expand All @@ -1012,22 +1012,22 @@ void ESP_Mail_Client::getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg,

// Construct 'To' header field.
for (uint8_t i = 0; i < msg->_rcp.size(); i++)
appendAddressHeaderField(buf, msg->_rcp[i], esp_mail_rfc822_header_field_to, i == 0, i > 0, i == msg->_rcp.size() - 1);
appendAddressHeaderField(buf, msg->_rcp[i], esp_mail_rfc822_header_field_to, i == 0, i > 0, i == msg->_rcp.size() - 1, true);

for (uint8_t i = 0; i < msg->_cc.size(); i++)
appendAddressHeaderField(buf, msg->_cc[i], esp_mail_rfc822_header_field_cc, i == 0, i > 0, i == msg->_cc.size() - 1);
appendAddressHeaderField(buf, msg->_cc[i], esp_mail_rfc822_header_field_cc, i == 0, i > 0, i == msg->_cc.size() - 1, true);

for (uint8_t i = 0; i < msg->_bcc.size(); i++)
appendAddressHeaderField(buf, msg->_bcc[i], esp_mail_rfc822_header_field_bcc, i == 0, i > 0, i == msg->_bcc.size() - 1);
appendAddressHeaderField(buf, msg->_bcc[i], esp_mail_rfc822_header_field_bcc, i == 0, i > 0, i == msg->_bcc.size() - 1, true);

if (msg->subject.length() > 0)
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_subject].text, msg->subject.c_str(), false, true);
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_subject].text, MailClient.encodeBUTF8(msg->subject.c_str()).c_str(), false, true);

if (msg->keywords.length() > 0)
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_keywords].text, msg->keywords.c_str(), false, true);
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_keywords].text, MailClient.encodeBUTF8(msg->keywords.c_str()).c_str(), false, true);

if (msg->comments.length() > 0)
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_comments].text, msg->comments.c_str(), false, true);
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_comments].text, MailClient.encodeBUTF8(msg->comments.c_str()).c_str(), false, true);

if (msg->in_reply_to.length() > 0)
appendHeaderField(buf, rfc822_headers[esp_mail_rfc822_header_field_in_reply_to].text, msg->in_reply_to.c_str(), false, true);
Expand Down
6 changes: 3 additions & 3 deletions src/ESP_Mail_TCPClient.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/**
*
* The Network Upgradable Arduino Secure TCP Client Class, ESP_Mail_TCPClient.h v1.0.4
* The Network Upgradable Arduino Secure TCP Client Class, ESP_Mail_TCPClient.h v3.4.15
*
* Created September 14, 2023
* Created November 15, 2023
*
* The MIT License (MIT)
* Copyright (c) 2023 K. Suwatchai (Mobizt)
Expand Down Expand Up @@ -30,7 +30,7 @@
#define ESP_MAIL_TCPCLIENT_H

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
14 changes: 9 additions & 5 deletions src/extras/MB_String.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@

/**
* Mobizt's SRAM/PSRAM supported String, version 1.2.9
* Mobizt's SRAM/PSRAM supported String, version 1.2.10
*
* Created December 3, 2022
* Created November 15, 2023
*
* Changes Log
*
*
* v1.2.10
* - add support Arduino UNO WiFi R4
*
* v1.2.9
* - substring optimization
*
Expand Down Expand Up @@ -900,15 +904,15 @@ class MB_String
s = boolStr(value);
else if (is_num_neg_int<T>::value)
{
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_UNOWIFIR4)
s = int32Str(value);
#else
s = int64Str(value);
#endif
}
else if (is_num_pos_int<T>::value)
{
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_UNOWIFIR4)
s = uint32Str(value);
#else
s = uint64Str(value);
Expand Down Expand Up @@ -1475,7 +1479,7 @@ class MB_String
static const size_t npos = -1;

private:
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT)
#if defined(ARDUINO_ARCH_SAMD) || defined(__AVR_ATmega4809__) || defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_UNOWIFIR4)

char *int32Str(signed long value)
{
Expand Down
2 changes: 1 addition & 1 deletion src/extras/MB_Time.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define MB_Time_H

#include "./ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/extras/Networks.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "./ESP_Mail_FS.h"

#include "./ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
2 changes: 1 addition & 1 deletion src/extras/RFC2047.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define RFC2047_CPP

#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30414)
#if !VALID_VERSION_CHECK(30415)
#error "Mixed versions compilation."
#endif

Expand Down
Loading

0 comments on commit dcb32c4

Please sign in to comment.