From c0241dd1d0b5b50bdfa8c1cb259a0453b7a6a1db Mon Sep 17 00:00:00 2001 From: suwatchai Date: Thu, 31 Oct 2019 22:49:45 +0700 Subject: [PATCH] Add support to set, add and remove flags --- README.md | 58 ++++- examples/Set_flag/Set_flag.ino | 70 ++++++ keywords.txt | 3 + library.properties | 2 +- src/ESP32_MailClient.cpp | 424 ++++++++++++++++++++++++++++++++- src/ESP32_MailClient.h | 74 +++++- 6 files changed, 620 insertions(+), 11 deletions(-) create mode 100644 examples/Set_flag/Set_flag.ino diff --git a/README.md b/README.md index 81762aa..8718e8e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Mail Client Arduino Library for ESP32 v 2.0.5 +# Mail Client Arduino Library for ESP32 v 2.0.6 This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download via SMTP and IMAP servers. @@ -254,6 +254,62 @@ bool readMail(IMAPData &imapData); + +**Set the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool setFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + +**Add the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool addFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + + +**Remove the argument from the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + **Get the Email sending error details.** return - *`Error details string (String object).`* diff --git a/examples/Set_flag/Set_flag.ino b/examples/Set_flag/Set_flag.ino new file mode 100644 index 0000000..e9ff771 --- /dev/null +++ b/examples/Set_flag/Set_flag.ino @@ -0,0 +1,70 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +#include +#include "ESP32_MailClient.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + +//The Email Reading data object contains config and data that received +IMAPData imapData; + +void readEmail(); + +unsigned long lastTime = 0; + +void setup() +{ + + Serial.begin(115200); + 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(); + + Serial.println(); + + imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + imapData.setFolder("INBOX"); + imapData.setDebug(true); + + //Set \Seen and \Answered to flags for message with UID 100 + MailClient.setFlag(imapData, 100, "\\Seen \\Answered"); + + //Add \Seen and \Answered to flags for message with UID 100 + //MailClient.addFlag(imapData, 100, "\\Seen \\Answered"); + + //Remove \Seen and \Answered from flags for message with UID 100 + //MailClient.removeFlag(imapData, 100, "\\Seen \\Answered"); +} + + + +void loop() +{ + +} + diff --git a/keywords.txt b/keywords.txt index c483b14..4d5e2a4 100644 --- a/keywords.txt +++ b/keywords.txt @@ -24,6 +24,9 @@ readMail KEYWORD2 smtpErrorReason KEYWORD2 imapErrorReason KEYWORD2 sdBegin KEYWORD2 +setFlag KEYWORD2 +addFlag KEYWORD2 +removeFlag KEYWORD2 ######################################### # Methods for IMAP Data object (KEYWORD2) diff --git a/library.properties b/library.properties index f42fed1..3c9833a 100644 --- a/library.properties +++ b/library.properties @@ -1,6 +1,6 @@ name=ESP32 Mail Client -version=2.0.5 +version=2.0.6 author=Mobizt diff --git a/src/ESP32_MailClient.cpp b/src/ESP32_MailClient.cpp index ab06947..af1c836 100644 --- a/src/ESP32_MailClient.cpp +++ b/src/ESP32_MailClient.cpp @@ -1,7 +1,7 @@ /* - *Mail Client Arduino Library for ESP32, version 2.0.5 + *Mail Client Arduino Library for ESP32, version 2.0.6 * - * October 25, 2019 + * October 31, 2019 * * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. * @@ -930,6 +930,384 @@ bool ESP32_MailClient::readMail(IMAPData &imapData) return false; } +bool ESP32_MailClient::setFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 0); +} + +bool ESP32_MailClient::addFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 1); +} + +bool ESP32_MailClient::removeFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 2); +} + +bool ESP32_MailClient::_setFlag(IMAPData &imapData, int msgUID, const String &flag, uint8_t action) +{ + + std::string buf; + + bool starttls = imapData._starttls; + bool connected = false; + + int bufSize = 50; + + char *_val = new char[bufSize]; + char *_part = new char[bufSize]; + + unsigned long dataTime = 0; + + int count = 0; + + imapData._net->setDebugCallback(NULL); + + if (imapData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_225); + ESP32MailDebug(imapData._host.c_str()); + ESP32MailDebug(String(imapData._port).c_str()); + } + + if (WiFi.status() != WL_CONNECTED) + WiFi.reconnect(); + + //Try to reconnect WiFi if lost connection + if (WiFi.status() != WL_CONNECTED) + { + uint8_t tryCount = 0; + WiFi.reconnect(); + while (WiFi.status() != WL_CONNECTED) + { + tryCount++; + delay(50); + if (tryCount > 60) + break; + } + } + + //If WiFi is not connected, return false + if (WiFi.status() != WL_CONNECTED) + { + _imapStatus = MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_226); + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_50; + imapData._cbData._status = ESP32_MAIL_STR_51; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + imapData._net->setDebugCallback(ESP32MailDebug); + + if (imapData._rootCA.size() > 0) + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)imapData._rootCA.front()); + else + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + while (!imapData._net->connected() && count < 10) + { + + count++; + + if (!imapData._net->connect(starttls)) + { + + _imapStatus = IMAP_STATUS_SERVER_CONNECT_FAILED; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + else + { + break; + } + } + + if (!imapData._net->connect(starttls)) + { + goto out; + } + + connected = true; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_54; + imapData._cbData._status = ESP32_MAIL_STR_55; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_228); + + //Don't expect handshake from some servers + dataTime = millis(); + + while (imapData._net->connected() && !imapData._net->getStreamPtr()->available() && millis() - 500 < dataTime) + delay(1); + + if (imapData._net->connected() && imapData._net->getStreamPtr()->available()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData.clearMessageData(); + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_56; + imapData._cbData._status = ESP32_MAIL_STR_57; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_229); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_130); + imapData._net->getStreamPtr()->print(imapData._loginEmail.c_str()); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_131); + imapData._net->getStreamPtr()->println(imapData._loginPassword.c_str()); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LOGIN)) + { + _imapStatus = IMAP_STATUS_LOGIN_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_58; + imapData._cbData._status = ESP32_MAIL_STR_59; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_230); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_133); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LIST)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + imapData._cbData.empty(); + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_60; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._folders.size(); i++) + { + imapData._cbData._info = imapData._folders[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_61 + imapData._currentFolder + ESP32_MAIL_STR_97; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_248); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_247); + imapData._net->getStreamPtr()->print(imapData._currentFolder.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_136); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::EXAMINE)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._debug) + { + if (action == 0) + ESP32MailDebugInfo(ESP32_MAIL_STR_253); + else if (action == 1) + ESP32MailDebugInfo(ESP32_MAIL_STR_254); + else + ESP32MailDebugInfo(ESP32_MAIL_STR_255); + } + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_249); + imapData._net->getStreamPtr()->print(msgUID); + if (action == 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_250); + else if (action == 1) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_251); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_252); + imapData._net->getStreamPtr()->print(flag); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_192); + + if (!getIMAPResponse(imapData)) + { + _imapStatus = IMAP_STATUS_PARSE_FLAG_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_85; + imapData._cbData._status = ESP32_MAIL_STR_86; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_234); + + if (imapData._net->connected()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_146); + + if (!waitIMAPResponse(imapData, 0)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_98; + imapData._cbData._status = ESP32_MAIL_STR_96; + imapData._cbData._success = true; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_235); + + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->stop(); + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + + return true; + +out: + + if (connected) + { + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + imapData._net->getStreamPtr()->stop(); + } + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + return false; +} + bool ESP32_MailClient::reconnect() { if (WiFi.status() != WL_CONNECTED) @@ -1703,6 +2081,9 @@ String ESP32_MailClient::imapErrorReason() case IMAP_STATUS_BAD_COMMAND: res = ESP32_MAIL_STR_46; break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: res = ESP32_MAIL_STR_221; break; @@ -1730,6 +2111,9 @@ std::string ESP32_MailClient::imapErrorReasonStr() case IMAP_STATUS_BAD_COMMAND: res = ESP32_MAIL_STR_46; break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: res = ESP32_MAIL_STR_221; break; @@ -1890,6 +2274,42 @@ int ESP32_MailClient::waitSMTPResponse(SMTPData &smtpData) return resCode; } +bool ESP32_MailClient::getIMAPResponse(IMAPData &imapData) +{ + long dataTime = millis(); + char c = 0; + bool success = false; + std::string str = ""; + while (imapClientAvailable(imapData, false) && millis() - dataTime < imapData._net->tcpTimeout) + delay(1); + + dataTime = millis(); + if (imapClientAvailable(imapData, true)) + { + while (imapClientAvailable(imapData, true)) + { + int r = imapData._net->getStreamPtr()->read(); + if (r < 0) + continue; + c = (char)r; + if (c == '\n') + { + if (imapData._debug) + ESP32MailDebug(str.c_str()); + str.clear(); + } + else + str += c; + + if(str.find(ESP32_MAIL_STR_132) != std::string::npos) + success = true; + } + } + + std::string().swap(str); + return success; +} + bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType, int maxChar, int mailIndex, int messageDataIndex, std::string part) { diff --git a/src/ESP32_MailClient.h b/src/ESP32_MailClient.h index 19ca2e0..fb631e4 100644 --- a/src/ESP32_MailClient.h +++ b/src/ESP32_MailClient.h @@ -1,7 +1,7 @@ /* - *Mail Client Arduino Library for ESP32, version 2.0.5 + *Mail Client Arduino Library for ESP32, version 2.0.6 * - * October 25, 2019 + * October 31, 2019 * * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. * @@ -65,6 +65,7 @@ using namespace std; #define IMAP_STATUS_IMAP_RESPONSE_FAILED 2 #define IMAP_STATUS_LOGIN_FAILED 3 #define IMAP_STATUS_BAD_COMMAND 4 +#define IMAP_STATUS_PARSE_FLAG_FAILED 5 #define MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL 100 @@ -333,10 +334,16 @@ static const char ESP32_MAIL_STR_243[] PROGMEM = "INFO: send email body"; static const char ESP32_MAIL_STR_244[] PROGMEM = "INFO: send attachment..."; static const char ESP32_MAIL_STR_245[] PROGMEM = "INFO: finalize..."; static const char ESP32_MAIL_STR_246[] PROGMEM = "INFO: email sent successfully"; -static const char ESP32_MAIL_STR_247[] PROGMEM = ""; -static const char ESP32_MAIL_STR_248[] PROGMEM = ""; -static const char ESP32_MAIL_STR_249[] PROGMEM = ""; -static const char ESP32_MAIL_STR_250[] PROGMEM = ""; +static const char ESP32_MAIL_STR_247[] PROGMEM = "$ SELECT \""; +static const char ESP32_MAIL_STR_248[] PROGMEM = "INFO: send imap command SELECT"; +static const char ESP32_MAIL_STR_249[] PROGMEM = "$ UID STORE "; +static const char ESP32_MAIL_STR_250[] PROGMEM = " FLAGS ("; +static const char ESP32_MAIL_STR_251[] PROGMEM = " +FLAGS ("; +static const char ESP32_MAIL_STR_252[] PROGMEM = " -FLAGS ("; +static const char ESP32_MAIL_STR_253[] PROGMEM = "INFO: set FLAG"; +static const char ESP32_MAIL_STR_254[] PROGMEM = "INFO: add FLAG"; +static const char ESP32_MAIL_STR_255[] PROGMEM = "INFO: remove FLAG"; +static const char ESP32_MAIL_STR_256[] PROGMEM = "could not parse flag"; __attribute__((used)) static bool compFunc(uint32_t i, uint32_t j) { @@ -404,6 +411,54 @@ class ESP32_MailClient */ bool readMail(IMAPData &imapData); + /* + + Set the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool setFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Add the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool addFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Remove the argument from the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); + /* Get the Email sending error details. @@ -448,6 +503,8 @@ class ESP32_MailClient struct IMAP_COMMAND_TYPE; struct IMAP_HEADER_TYPE; + + protected: int _smtpStatus = 0; int _imapStatus = 0; @@ -457,6 +514,8 @@ class ESP32_MailClient unsigned long _lastReconnectMillis = 0; uint16_t _reconnectTimeout = 10000; + + std::string smtpErrorReasonStr(); std::string imapErrorReasonStr(); void ESP32MailDebugError(); @@ -471,12 +530,13 @@ class ESP32_MailClient void send_base64_encode_file(WiFiClient *client, File file); int waitSMTPResponse(SMTPData &smtpData); bool waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType = 0, int maxChar = 0, int mailIndex = -1, int messageDataIndex = -1, std ::string part = ""); + bool _setFlag(IMAPData &imapData, int msgUID, const String &flags, uint8_t action); + bool getIMAPResponse(IMAPData &imapData); void createDirs(std::string dirs); bool smtpClientAvailable(SMTPData &smtpData, bool available); bool imapClientAvailable(IMAPData &imapData, bool available); bool reconnect(); bool sdTest(); - }; class messageBodyData