From 2b1454c956b568baccbe347d513123d187fce5a6 Mon Sep 17 00:00:00 2001 From: suwatchai Date: Thu, 16 Jun 2022 13:29:03 +0700 Subject: [PATCH] Add IMAP sendCustomData function and add IMAP append message example. --- .github/workflows/compile_examples.yml | 2 + .../Append_Message/Append_Message.ino | 177 ++++++++++++++++++ library.json | 2 +- library.properties | 2 +- src/ESP_Mail_Client.h | 34 +++- src/ESP_Mail_Client_Version.h | 2 +- src/ESP_Mail_IMAP.h | 83 +++++++- src/README.md | 38 ++++ 8 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 examples/IMAP/Custom_Command/Append_Message/Append_Message.ino diff --git a/.github/workflows/compile_examples.yml b/.github/workflows/compile_examples.yml index 0c3bf68e..8b5652e8 100644 --- a/.github/workflows/compile_examples.yml +++ b/.github/workflows/compile_examples.yml @@ -18,6 +18,7 @@ jobs: matrix: example: - "examples/IMAP/Copy_Messages/Copy_Messsages.ino" + - "examples/IMAP/Custom_Command/Append_Message/Append_Message.ino" - "examples/IMAP/Custom_Command/Basic_Auth/Basic_Auth.ino" - "examples/IMAP/Custom_Command/Custom_Auth/Custom_Auth.ino" - "examples/IMAP/DataStreamCallback/DataStreamCallback.ino" @@ -90,6 +91,7 @@ jobs: matrix: example: - "examples/IMAP/Copy_Messages/Copy_Messsages.ino" + - "examples/IMAP/Custom_Command/Append_Message/Append_Message.ino" - "examples/IMAP/Custom_Command/Basic_Auth/Basic_Auth.ino" - "examples/IMAP/Custom_Command/Custom_Auth/Custom_Auth.ino" - "examples/IMAP/DataStreamCallback/DataStreamCallback.ino" diff --git a/examples/IMAP/Custom_Command/Append_Message/Append_Message.ino b/examples/IMAP/Custom_Command/Append_Message/Append_Message.ino new file mode 100644 index 00000000..c08b3bfd --- /dev/null +++ b/examples/IMAP/Custom_Command/Append_Message/Append_Message.ino @@ -0,0 +1,177 @@ +/** + * This example shows how to append new message to mailbox using the custom IMAP command. + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2022 mobizt + * + */ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. + */ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#else + +// Other Client defined here +// To use custom Client, define ENABLE_CUSTOM_CLIENT in src/ESP_Mail_FS.h. +// See the example Custom_Client.ino for how to use. + +#endif + +#include + +// To use only IMAP functions, you can exclude the SMTP from compilation, see ESP_Mail_FS.h. + +#define WIFI_SSID "" +#define WIFI_PASSWORD "" + +/** For Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * + * Some Gmail user still not able to sign in using account password even above options were set up, + * for this case, use "App Password" to sign in instead. + * About Gmail "App Password", go to https://support.google.com/accounts/answer/185833?hl=en + * + * For Yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * + * To use Gmai and Yahoo's App Password to sign in, define the AUTHOR_PASSWORD with your App Password + * and AUTHOR_EMAIL with your account email. + */ + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 + */ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "" +#define AUTHOR_PASSWORD "" + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + +void customCommandCallback(IMAP_Response res) +{ + // The server responses will included tagged and/or untagged data. + + // Tagged data is the status which begins with command identifier (tag) i.e. "A01" in this case. + // Tagged status responses included OK, NO, BAD, PREAUTH and BYE. + + // Untagged data is the information or result of the request which begins with * + + // When you send multiple commands with different tag simultaneously, + // tag will be used as command identifier. + + Serial.print("> C: TAG "); + Serial.println(res.tag.c_str()); + Serial.print("< S: "); + Serial.println(res.text.c_str()); + + if (res.completed) + { + Serial.print("> C: Response finished with status "); + Serial.println(res.status.c_str()); + Serial.println(); + } +} + +void setup() +{ + + Serial.begin(115200); + +#if defined(ARDUINO_ARCH_SAMD) + while (!Serial) + ; + Serial.println(); + Serial.println("**** Custom built WiFiNINA firmware need to be installed.****\nTo install firmware, read the instruction here, https://github.com/mobizt/ESP-Mail-Client#install-custom-built-wifinina-firmware"); + +#endif + + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + + /* Connect to server with the session and config */ + if (!imap.customConnect(&session, customCommandCallback, F("A01") /* tag */)) + return; + + String cmd = F("LOGIN "); + cmd += AUTHOR_EMAIL; + cmd += F(" "); + cmd += AUTHOR_PASSWORD; + + // You can also assign tag to the begining of the command e.g. "A01 FETCH 1 UID" + // Do not assign tag to command when you assign tag to the last parameter of function. + + imap.sendCustomCommand(cmd, customCommandCallback, F("A02") /* tag */); + + imap.sendCustomCommand(F("SELECT \"INBOX\""), customCommandCallback, F("A03") /* tag */); + + imap.sendCustomCommand(F("LIST \"\" *"), customCommandCallback, F("A04") /* tag */); + + String appendMsg = "Date: Thu, 16 Jun 2022 12:30:25 -0800 (PST)\r\n"; + + appendMsg += "From: Jack \r\n"; + + appendMsg += "Subject: Greeting from ESP Mail\r\n"; + + appendMsg += "To: joe@host.com\r\n"; + + appendMsg += "Message-Id: \r\n"; + + appendMsg += "MIME-Version: 1.0\r\n"; + + appendMsg += "Content-Type: text/plain; charset=\"us-ascii\"\r\n"; + + appendMsg += "Content-transfer-encoding: 7bit\r\n"; + + appendMsg += "\r\n"; + + appendMsg += "Hello Joe, this is the append message\r\n"; + + String appendMsgCmd = "APPEND INBOX {" + String(appendMsg.length()) + "}"; + + imap.sendCustomCommand(appendMsgCmd, customCommandCallback, F("A05") /* tag */); + + imap.sendCustomData(appendMsg, true /* flag states the last data to send */); +} + +void loop() +{ +} diff --git a/library.json b/library.json index 938856a8..8ace6cc6 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "ESP Mail Client", - "version": "2.4.2", + "version": "2.4.3", "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": { diff --git a/library.properties b/library.properties index 3163bcb5..2786173d 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=ESP Mail Client -version=2.4.2 +version=2.4.3 author=Mobizt diff --git a/src/ESP_Mail_Client.h b/src/ESP_Mail_Client.h index 4071c460..9a8d23d4 100644 --- a/src/ESP_Mail_Client.h +++ b/src/ESP_Mail_Client.h @@ -4,7 +4,7 @@ /** * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 and SAMD21 with u-blox NINA-W102 WiFi/Bluetooth module * - * Created June 13, 2022 + * Created June 16, 2022 * * This library allows Espressif's ESP32, ESP8266 and SAMD devices to send and read Email through the SMTP and IMAP servers. * @@ -1067,6 +1067,9 @@ class ESP_Mail_Client // Send data size_t imapSend(IMAPSession *imap, int data, bool newline = false); +// Send data + size_t imapSend(IMAPSession *imap, uint8_t *data, size_t size); + // Log out bool imapLogout(IMAPSession *imap); @@ -1368,6 +1371,28 @@ class IMAPSession template bool sendCustomCommand(T1 cmd, imapResponseCallback callback, T2 tag = "") { return mSendCustomCommand(toStringPtr(cmd), callback, toStringPtr(tag)); } + /** Send the custom IMAP command data string. + * + * @param data The string data. + * @param last The flag represents the last data to send (optional). + * @return The boolean value which indicates the success of operation. + * + * @note Should be used after calling sendCustomCommand("APPEND xxxxxx"); + */ + template + bool sendCustomData(T data, bool lastData = false) { return mSendData(toStringPtr(data), lastData); } + + /** Send the custom IMAP command data. + * + * @param data The byte data. + * @param size The data size. + * @param lastData The flag represents the last data to send (optional). + * @return The boolean value which indicates the success of operation. + * + * @note Should be used after calling ssendCustomCommand("APPEND xxxxxx"); + */ + bool sendCustomData(uint8_t *data, size_t size, bool lastData = false) { return mSendData(data, size, lastData); } + /** Copy the messages to the defined mailbox folder. * * @param toCopy The pointer to the MessageListList class that contains the @@ -1506,6 +1531,12 @@ class IMAPSession // Send custom command bool mSendCustomCommand(MB_StringPtr cmd, imapResponseCallback callback, MB_StringPtr tag); + // Send data after sending APPEND command + bool mSendData(MB_StringPtr data, bool lastData); + + // Send data after sending APPEND command + bool mSendData(uint8_t *data, size_t size, bool lastData); + // Delete folder bool mDeleteFolder(MB_StringPtr folderName); @@ -1543,6 +1574,7 @@ class IMAPSession MB_VECTOR _headers; esp_mail_imap_command _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login; + MB_String _cmd; MB_VECTOR _multipart_levels; int _rfc822_part_count = 0; bool _unseen = false; diff --git a/src/ESP_Mail_Client_Version.h b/src/ESP_Mail_Client_Version.h index ebc4d434..b967b89e 100644 --- a/src/ESP_Mail_Client_Version.h +++ b/src/ESP_Mail_Client_Version.h @@ -3,6 +3,6 @@ #ifndef ESP_MAIL_VERSION -#define ESP_MAIL_VERSION "2.4.2" +#define ESP_MAIL_VERSION "2.4.3" #endif \ No newline at end of file diff --git a/src/ESP_Mail_IMAP.h b/src/ESP_Mail_IMAP.h index 09169231..cd7a4a53 100644 --- a/src/ESP_Mail_IMAP.h +++ b/src/ESP_Mail_IMAP.h @@ -5,7 +5,7 @@ /** * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 and SAMD21 with u-blox NINA-W102 WiFi/Bluetooth module * - * Created June 13, 2022 + * Created June 16, 2022 * * This library allows Espressif's ESP32, ESP8266 and SAMD devices to send and read Email through the SMTP and IMAP servers. * @@ -504,7 +504,7 @@ bool ESP_Mail_Client::readMail(IMAPSession *imap, bool closeSession) if (imap->_config->fetch.uid.length() > 0) { imap->_mbif._availableItems++; - + esp_mail_imap_msg_num_t msg_num; msg_num.type = esp_mail_imap_msg_num_type_uid; msg_num.value = (uint32_t)atoi(imap->_config->fetch.uid.c_str()); @@ -1305,6 +1305,39 @@ size_t ESP_Mail_Client::imapSend(IMAPSession *imap, int data, bool newline) return imapSendP(imap, s.c_str(), newline); } +size_t ESP_Mail_Client::imapSend(IMAPSession *imap, uint8_t *data, size_t size) +{ + int sent = 0; + + if (!reconnect(imap)) + { + closeTCPSession(imap); + return sent; + } + + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_CLOSED); + return sent; + } + + if (!imap->_tcpConnected) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return sent; + } + + sent = imap->client.write(data, size); + + if (sent != (int)size) + { + errorStatusCB(imap, sent); + sent = 0; + } + + return sent; +} + bool ESP_Mail_Client::mSetFlag(IMAPSession *imap, int msgUID, MB_StringPtr flag, uint8_t action, bool closeSession) { if (!reconnect(imap)) @@ -2540,6 +2573,9 @@ bool ESP_Mail_Client::handleIMAPResponse(IMAPSession *imap, int errCode, bool cl if (imapResp > esp_mail_imap_response_status::esp_mail_imap_resp_unknown) completedResponse = true; + if (strpos(imap->_cmd.c_str(), "APPEND", 0, false) > -1) + completedResponse = true; + imap->_imapStatus.text = response; imap->_customCmdResCallback(imap->_imapStatus); @@ -5170,7 +5206,7 @@ bool IMAPSession::mSendCustomCommand(MB_StringPtr cmd, imapResponseCallback call _customCmdResCallback = callback; - MB_String _cmd = cmd; + _cmd = cmd; _cmd.trim(); @@ -5224,6 +5260,47 @@ bool IMAPSession::mSendCustomCommand(MB_StringPtr cmd, imapResponseCallback call return true; } +bool IMAPSession::mSendData(MB_StringPtr data, bool lastData) +{ + + MB_String _data = data; + + if (MailClient.imapSend(this, _data.c_str(), lastData) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if(lastData) + { + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_custom; + _cmd.clear(); + + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + } + + return true; +} + +bool IMAPSession::mSendData(uint8_t *data, size_t size, bool lastData) +{ + + if (MailClient.imapSend(this, data, size) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (lastData) + { + if (MailClient.imapSend(this, "\r\n", false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_custom; + _cmd.clear(); + + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + } + + return true; +} + bool IMAPSession::mDeleteFolder(MB_StringPtr folderName) { if (_debug) diff --git a/src/README.md b/src/README.md index be8dcfca..018a8465 100644 --- a/src/README.md +++ b/src/README.md @@ -448,6 +448,44 @@ bool sendCustomCommand( cmd, imapResponseCallback callback, tag + +#### Send the custom IMAP command data string. + +param **`data`** The string data. + +param **`lastData`** The flag represents the last data to send (optional). + +return **`boolean`** The boolean value which indicates the success of operation. + +Should be used after calling sendCustomCommand("APPEND xxxxxx"); + +```cpp +bool sendCustomData(T data, bool lastData = false); +``` + + + + + +#### Send the custom IMAP command data. + +param **`data`** The byte data. + +param **`size`** The data size. + +param **`lastData`** The flag represents the last data to send (optional). + +return **`boolean`** The boolean value which indicates the success of operation. + +Should be used after calling ssendCustomCommand("APPEND xxxxxx"); + +```cpp +bool sendCustomData(uint8_t *data, size_t size, bool lastData = false); +``` + + + + #### Begin the IMAP server connection without authentication. param **`session`** The pointer to ESP_Mail_Session structured data that keeps the server and log in details.