Tasmota/lib/libesp32/ESP-Mail-Client/src/ESP_Mail_IMAP.h
s-hadinger c2f8821b2f
Sendmail upgraded to ESP-Mail-Client v3.4.9 from v1.2.0, using BearSSL instead of MbedTLS (#19460)
* `Sendmail` upgraded to ESP-Mail-Client v3.4.9 from v1.2.0, using BearSSL instead of MbedTLS

* Fix compilation on ESP8266

* Fix compilation

* fix compilation
2023-09-04 23:00:37 +02:00

6783 lines
254 KiB
C++

#ifndef ESP_MAIL_IMAP_H
#define ESP_MAIL_IMAP_H
#include "ESP_Mail_Client_Version.h"
#if !VALID_VERSION_CHECK(30409)
#error "Mixed versions compilation."
#endif
/**
* Mail Client Arduino Library for Espressif's ESP32 and ESP8266, Raspberry Pi RP2040 Pico, and SAMD21 with u-blox NINA-W102 WiFi/Bluetooth module
*
* Created August 28, 2023
*
* This library allows Espressif's ESP32, ESP8266, SAMD and RP2040 Pico devices to send and read Email through the SMTP and IMAP servers.
*
* The MIT License (MIT)
* Copyright (c) 2023 K. Suwatchai (Mobizt)
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ESP_Mail_Client_Version.h"
#include "ESP_Mail_Client.h"
#if defined(ENABLE_IMAP)
#if defined(MB_ARDUINO_PICO)
extern uint8_t _FS_start;
extern uint8_t _FS_end;
#endif
int ESP_Mail_Client::decodeChar(const char *s)
{
return 16 * hexval(*(s + 1)) + hexval(*(s + 2));
}
void ESP_Mail_Client::decodeQP_UTF8(const char *buf, char *out)
{
char *tmp = strP(esp_mail_str_47 /* "0123456789ABCDEF" */);
int idx = 0;
while (*buf)
{
if (*buf != '=')
out[idx++] = *buf++;
else if (*(buf + 1) == '\r' && *(buf + 2) == '\n')
buf += 3;
else if (*(buf + 1) == '\n')
buf += 2;
else if (!strchr(tmp, *(buf + 1)))
out[idx++] = *buf++;
else if (!strchr(tmp, *(buf + 2)))
out[idx++] = *buf++;
else
{
out[idx++] = decodeChar(buf);
buf += 3;
}
}
// release memory
freeMem(&tmp);
}
char *ESP_Mail_Client::decode7Bit_UTF8(char *buf)
{
MB_String s;
// only non NULL and 7-bit ASCII are allowed
// rfc2045 section 2.7
size_t len = buf ? strlen(buf) : 0;
for (size_t i = 0; i < len; i++)
{
if (buf[i] > 0 && buf[i] < 128 && i < 998)
s.append(1, buf[i]);
}
// some special chars can't send in 7bit unless encoded as queoted printable string
char *decoded = allocMem<char *>(s.length() + 10);
decodeQP_UTF8(s.c_str(), decoded);
s.clear();
return decoded;
}
char *ESP_Mail_Client::decode8Bit_UTF8(char *buf)
{
MB_String s;
// only non NULL and less than 998 octet length are allowed
// rfc2045 section 2.8
size_t len = buf ? strlen(buf) : 0;
for (size_t i = 0; i < len; i++)
{
if (buf[i] > 0 && i < 998)
s.append(1, buf[i]);
}
char *decoded = allocMem<char *>(s.length() + 1);
strcpy(decoded, s.c_str());
s.clear();
return decoded;
}
void ESP_Mail_Client::decodeString(IMAPSession *imap, MB_String &str, const char *enc)
{
size_t p1 = 0, p2 = 0;
MB_String headerEnc;
if (strlen(enc) == 0)
{
while (str[p1] == ' ' && p1 < str.length() - 1)
p1++;
if (str[p1] == '=' && str[p1 + 1] == '?')
{
p2 = str.find('?', p1 + 2);
if (p2 != MB_String::npos)
headerEnc = str.substr(p1 + 2, p2 - p1 - 2);
}
}
else
headerEnc = enc;
int bufSize = str.length() + 10;
char *buf = allocMem<char *>(bufSize);
// Content Q and B decodings
RFC2047Decoder.decode(mbfs, buf, str.c_str(), bufSize);
// Char set decoding
esp_mail_char_decoding_scheme scheme = getEncodingFromCharset(headerEnc.c_str());
if (imap->_charDecCallback)
{
IMAP_Decoding_Info decoding;
decoding.charset = headerEnc.c_str();
decoding.data = buf;
decoding.type = IMAP_Decoding_Info::message_part_type_header;
imap->_charDecCallback(&decoding);
if (decoding.decodedString.length() > 0)
{
char *buf2 = allocMem<char *>(decoding.decodedString.length() + 1);
strcpy(buf2, decoding.decodedString.c_str());
// release memory and point to new buffer
freeMem(&buf);
buf = buf2;
}
}
else if (scheme == esp_mail_char_decoding_scheme_iso8859_1)
{
int len = strlen(buf);
int olen = (len + 1) * 2;
unsigned char *out = allocMem<unsigned char *>(olen);
decodeLatin1_UTF8(out, &olen, (unsigned char *)buf, &len);
// release memory and point to new buffer
freeMem(&buf);
buf = (char *)out;
}
else if (scheme == esp_mail_char_decoding_scheme_tis_620 || scheme == esp_mail_char_decoding_scheme_iso8859_11 || scheme == esp_mail_char_decoding_scheme_windows_874)
{
size_t len2 = strlen(buf);
char *tmp = allocMem<char *>((len2 + 1) * 3);
decodeTIS620_UTF8(tmp, buf, len2);
// release memory and point to new buffer
freeMem(&buf);
buf = tmp;
}
str = buf;
// release memory
freeMem(&buf);
}
esp_mail_char_decoding_scheme ESP_Mail_Client::getEncodingFromCharset(const char *enc)
{
esp_mail_char_decoding_scheme scheme = esp_mail_char_decoding_scheme_default;
for (int i = esp_mail_char_decoding_utf8; i < esp_mail_char_decoding_maxType; i++)
{
if (strpos(enc, char_decodings[i].text, 0, false) > -1)
scheme = (esp_mail_char_decoding_scheme)i;
}
return scheme;
}
int ESP_Mail_Client::encodeUnicode_UTF8(char *out, uint32_t utf)
{
if (utf <= 0x7F)
{
// Plain ASCII
out[0] = (char)utf;
out[1] = 0;
return 1;
}
else if (utf <= 0x07FF)
{
// 2-byte unicode
out[0] = (char)(((utf >> 6) & 0x1F) | 0xC0);
out[1] = (char)(((utf >> 0) & 0x3F) | 0x80);
out[2] = 0;
return 2;
}
else if (utf <= 0xFFFF)
{
// 3-byte unicode
out[0] = (char)(((utf >> 12) & 0x0F) | 0xE0);
out[1] = (char)(((utf >> 6) & 0x3F) | 0x80);
out[2] = (char)(((utf >> 0) & 0x3F) | 0x80);
out[3] = 0;
return 3;
}
else if (utf <= 0x10FFFF)
{
// 4-byte unicode
out[0] = (char)(((utf >> 18) & 0x07) | 0xF0);
out[1] = (char)(((utf >> 12) & 0x3F) | 0x80);
out[2] = (char)(((utf >> 6) & 0x3F) | 0x80);
out[3] = (char)(((utf >> 0) & 0x3F) | 0x80);
out[4] = 0;
return 4;
}
else
{
// error - use replacement character
out[0] = (char)0xEF;
out[1] = (char)0xBF;
out[2] = (char)0xBD;
out[3] = 0;
return 0;
}
}
void ESP_Mail_Client::decodeTIS620_UTF8(char *out, const char *in, size_t len)
{
// output is the 3-byte value UTF-8
int j = 0;
for (size_t i = 0; i < len; i++)
{
if (in[i] < 0x80)
out[j++] = in[i];
else if ((in[i] >= 0xa0 && in[i] < 0xdb) || (in[i] > 0xde && in[i] < 0xfc))
{
int unicode = 0x0e00 + in[i] - 0xa0;
char o[5];
memset(o, 0, 5);
int r = encodeUnicode_UTF8(o, unicode);
for (int x = 0; x < r; x++)
out[j++] = o[x];
}
}
}
int ESP_Mail_Client::decodeLatin1_UTF8(unsigned char *out, int *outlen, const unsigned char *in, int *inlen)
{
unsigned char *outstart = out;
const unsigned char *base = in;
const unsigned char *processed = in;
unsigned char *outend = out + *outlen;
const unsigned char *inend;
unsigned int c;
int bits;
inend = in + (*inlen);
while ((in < inend) && (out - outstart + 5 < *outlen))
{
c = *in++;
/* assertion: c is a single UTF-4 value */
if (out >= outend)
break;
if (c < 0x80)
{
*out++ = c;
bits = -6;
}
else
{
*out++ = ((c >> 6) & 0x1F) | 0xC0;
bits = 0;
}
for (; bits >= 0; bits -= 6)
{
if (out >= outend)
break;
*out++ = ((c >> bits) & 0x3F) | 0x80;
}
processed = (const unsigned char *)in;
}
*outlen = out - outstart;
*inlen = processed - base;
return (0);
}
bool ESP_Mail_Client::sendFetchCommand(IMAPSession *imap, int msgIndex, esp_mail_imap_command cmdCase)
{
MB_String cmd, cmd2, cmd3;
appendHeadersFetchCommand(imap, cmd, msgIndex, false);
if (cmdCase == esp_mail_imap_cmd_fetch_body_mime)
{
joinStringDot(cmd2, 2, imap_commands[esp_mail_imap_command_header].text, imap_commands[esp_mail_imap_command_fields].text);
appendSpace(cmd2);
joinStringSpace(cmd3, false, 2, message_headers[esp_mail_message_header_field_content_type].text, message_headers[esp_mail_message_header_field_content_transfer_encoding].text);
appendString(cmd2, cmd3.c_str(), false, false, esp_mail_string_mark_type_round_bracket);
}
else if (cmdCase == esp_mail_imap_cmd_fetch_body_text)
cmd2 = cPart(imap)->partNumFetchStr.length() > 0 ? cPart(imap)->partNumFetchStr.c_str() : imap_commands[esp_mail_imap_command_text].text;
else if (cmdCase == esp_mail_imap_cmd_fetch_body_attachment)
cmd2 = cPart(imap)->partNumFetchStr;
if (cmd2.length() > 0)
appendString(cmd, cmd2.c_str(), false, false, esp_mail_string_mark_type_square_bracket);
bool allowPartialFetch = (cmdCase == esp_mail_imap_cmd_fetch_body_attachment && cPart(imap)->is_firmware_file) ? false : true;
if (imap->_mimeDataStreamCallback)
allowPartialFetch = false;
if (allowPartialFetch)
{
// Apply partial fetch in case download was disabled.
if (!imap->_storageReady && imap->_attDownload && cmdCase == esp_mail_imap_cmd_fetch_body_attachment)
cmd += esp_mail_str_48; /* "<0.0>" */ // This case should not happen because the memory storage was previousely checked.
else if ((!imap->_msgDownload && cmdCase == esp_mail_imap_cmd_fetch_body_text) || (imap->_msgDownload && !imap->_storageReady))
{
cmd += esp_mail_str_49; /* "<0." */
cmd += imap->_imap_data->limit.msg_size;
cmd += esp_mail_str_20; /* ">" */
}
}
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
return true;
}
bool ESP_Mail_Client::readMail(IMAPSession *imap, bool closeSession)
{
if (!imap || !sessionExisted<IMAPSession *>(imap))
return false;
imap->checkUID();
imap->checkPath();
imap->_cbData._success = false;
if (!imap->connected())
imap->_mailboxOpened = false;
imap->_isFirmwareUpdated = false;
MB_String buf, command, _uid;
size_t readCount = 0;
imap->_multipart_levels.clear();
if (!reconnect(imap))
return false;
imap->_msgDownload = imap->_imap_data->download.text || imap->_imap_data->download.html;
imap->_attDownload = imap->_imap_data->download.attachment || imap->_imap_data->download.inlineImg;
if (!imap->_storageChecked)
{
imap->_storageChecked = true;
imap->_storageReady = imap->_imap_data->download.header || (!imap->_imap_data->fetch.headerOnly && (imap->_msgDownload || imap->_attDownload)) ? mbfs->checkStorageReady(mbfs_type imap->_imap_data->storage.type) : true;
}
bool readyToDownload = (imap->_msgDownload || imap->_attDownload) && imap->_storageReady;
if (!imap->_storageReady)
sendStorageNotReadyError(imap, imap->_imap_data->storage.type);
#if defined(MB_ARDUINO_ESP) || defined(MB_ARDUINO_PICO)
int cmem = MailClient.getFreeHeap();
if (cmem < ESP_MAIL_MIN_MEM)
{
#if !defined(SILENT_MODE)
if (imap->_debug && imap->_statusCallback && !imap->_customCmdResCallback)
{
esp_mail_debug_print();
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_OUT_OF_MEMORY, true);
}
#endif
goto out;
}
#endif
if (!imap->connected() && !imap->_loginStatus)
{
#if !defined(SILENT_MODE)
if (imap->_debug && imap->_statusCallback && !imap->_customCmdResCallback)
{
esp_mail_debug_print();
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_NOT_YET_LOGIN, true);
}
#endif
return false;
}
// new session
if (!imap->connected())
{
// authenticate new
bool ssl = false;
if (!imap->connect(ssl))
{
closeTCPSession<IMAPSession *>(imap);
return false;
}
if (!imapAuth(imap, ssl))
{
closeTCPSession<IMAPSession *>(imap);
return false;
}
}
else
{
// If time config changed, we will update time
MailClient.prepareTime<IMAPSession *>(imap->_session_cfg, imap);
// reuse session
for (size_t i = 0; i < imap->_headers.size(); i++)
imap->_headers[i].part_headers.clear();
imap->_headers.clear();
if (imap->_imap_data->fetch.sequence_set.string.length() > 0 || imap->_imap_data->fetch.uid.length() > 0 || imap->_imap_data->fetch.number.length() > 0)
imap->_headerOnly = false;
else
imap->_headerOnly = true;
}
imap->_rfc822_part_count = 0;
imap->_mbif._availableItems = 0;
imap->_imap_msg_num.clear();
imap->_uidSearch = false;
imap->_mbif._searchCount = 0;
if (imap->_currentFolder.length() == 0)
return handleIMAPError(imap, IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED, false);
if (!imap->_mailboxOpened || (imap->_imap_data->fetch.set_seen && !imap->_headerOnly && imap->_readOnlyMode))
{
if (!imap->openFolder(imap->_currentFolder.c_str(), imap->_readOnlyMode && !imap->_imap_data->fetch.set_seen))
return handleIMAPError(imap, IMAP_STATUS_OPEN_MAILBOX_FAILED, false);
}
if (imap->_headerOnly)
{
if (imap->_imap_data->search.criteria.length() > 0)
{
command = esp_mail_imap_tag_str;
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_cb_str_18 /* "Searching messages..." */,
esp_mail_dbg_str_36 /* "searching messages" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (strposP(imap->_imap_data->search.criteria.c_str(), imap_cmd_post_tokens[esp_mail_imap_command_uid].c_str(), 0) != -1)
{
imap->_uidSearch = true;
command += imap_cmd_pre_tokens[esp_mail_imap_command_uid];
}
command += imap_cmd_pre_tokens[esp_mail_imap_command_search];
imap->_imap_data->search.criteria.trim();
MB_String tag;
appendTagSpace(tag);
// Remove internal used reserved tag
if (strpos(imap->_imap_data->search.criteria.c_str(), tag.c_str(), 0, true) == 0)
imap->_imap_data->search.criteria.erase(0, tag.length());
for (size_t i = 0; i < imap->_imap_data->search.criteria.length(); i++)
{
if (imap->_imap_data->search.criteria[i] != ' ' && imap->_imap_data->search.criteria[i] != '\r' && imap->_imap_data->search.criteria[i] != '\n')
buf.append(1, imap->_imap_data->search.criteria[i]);
if (imap->_imap_data->search.criteria[i] == ' ')
{
if ((imap->_uidSearch && strcmp(buf.c_str(), imap_commands[esp_mail_imap_command_uid].text) == 0) || (imap->_unseen && buf.find(imap_commands[esp_mail_imap_command_all].text) != MB_String::npos))
buf.clear();
if (strcmp(buf.c_str(), imap_commands[esp_mail_imap_command_search].text) != 0 && buf.length() > 0)
prependSpace(command, buf.c_str());
buf.clear();
}
}
if (imap->_unseen && strpos(imap->_imap_data->search.criteria.c_str(), imap_cmd_pre_tokens[esp_mail_imap_command_new].c_str(), 0) == -1)
command += imap_cmd_pre_tokens[esp_mail_imap_command_new];
if (buf.length() > 0)
prependSpace(command, buf.c_str());
if (!imap->isModseqSupported() && strpos(imap->_imap_data->search.criteria.c_str(), imap_cmd_pre_tokens[esp_mail_imap_command_modsec].c_str(), 0, false) != -1)
{
imap->_responseStatus.errorCode = IMAP_STATUS_MODSEQ_WAS_NOT_SUPPORTED;
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
sendErrorCB<IMAPSession *>(imap, imap->errorReason().c_str(), false, false);
if (imap->_debug)
esp_mail_debug_print_tag(imap->errorReason().c_str(), esp_mail_debug_tag_type_error, true);
#endif
return false;
}
if (imapSend(imap, command.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
command.clear();
imap->_imap_cmd = esp_mail_imap_cmd_search;
if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession))
return false;
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
{
callBackSendNewLine<IMAPSession *>(imap, false);
if (imap->_imap_msg_num.size() > 0)
{
int bufLen = 100;
char *buf = allocMem<char *>(bufLen);
snprintf(buf, bufLen, pgm2Str(esp_mail_str_50 /* "Search limit: %d\nFound %d messages\nShow %d messages\n" */), (int)imap->_imap_data->limit.search, imap->_mbif._searchCount, (int)imap->_imap_msg_num.size());
sendCallback<IMAPSession *>(imap, buf, false, false);
// release memory
freeMem(&buf);
}
else
sendCallback<IMAPSession *>(imap, esp_mail_error_imap_str_9 /* "no messages found for the specified search criteria" */, false, false);
}
#endif
}
else
{
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
sendCallback<IMAPSession *>(imap, esp_mail_error_imap_str_10 /* "no search criteria provided, then fetching the latest message" */, false, false);
#endif
imap->_mbif._availableItems++;
esp_mail_imap_msg_num_t msg_num;
msg_num.type = esp_mail_imap_msg_num_type_number;
msg_num.value = (uint32_t)imap->_mbif._msgCount;
imap->_imap_msg_num.push_back(msg_num);
imap->_headerOnly = false;
imap->_imap_data->fetch.number = imap->_mbif._msgCount;
}
}
else
{
if (imap->_imap_data->fetch.sequence_set.string.length() > 0)
{
imap->_headerOnly = imap->_imap_data->fetch.sequence_set.headerOnly;
imap->mFetchSequenceSet();
imap->_mbif._availableItems = imap->_imap_msg_num.size();
}
else
{
if (imap->_imap_data->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->_imap_data->fetch.uid.c_str());
imap->_imap_msg_num.push_back(msg_num);
}
if (imap->_imap_data->fetch.number.length() > 0)
{
imap->_mbif._availableItems++;
esp_mail_imap_msg_num_t msg_num;
msg_num.type = esp_mail_imap_msg_num_type_number;
msg_num.value = (uint32_t)atoi(imap->_imap_data->fetch.number.c_str());
imap->_imap_msg_num.push_back(msg_num);
}
}
}
if (imap->_imap_data->fetch.headerOnly)
imap->_headerOnly = true;
for (size_t i = 0; i < imap->_imap_msg_num.size(); i++)
{
imap->_cMsgIdx = i;
imap->_totalRead++;
#if defined(MB_ARDUINO_ESP) || defined(MB_ARDUINO_PICO)
if (MailClient.getFreeHeap() - (imap->_imap_data->limit.msg_size * (i + 1)) < ESP_MAIL_MIN_MEM)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_OUT_OF_MEMORY, true);
goto out;
}
#endif
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
{
readCount++;
int bufLen = 100;
PGM_P p = imap->_uidSearch || imap->_imap_msg_num[i].type == esp_mail_imap_msg_num_type_uid ? esp_mail_str_52 /* "Fetch message %d, UID: %d" */ : esp_mail_str_53 /* "Fetch message %d, Number: %d" */;
char *buf = allocMem<char *>(bufLen);
snprintf(buf, bufLen, pgm2Str(p), imap->_totalRead, (int)imap->_imap_msg_num[i].value);
sendCallback<IMAPSession *>(imap, buf, true, false);
// release memory
freeMem(&buf);
}
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_37 /* "send IMAP command, FETCH" */, esp_mail_debug_tag_type_client, true);
#endif
MB_String cmd;
appendHeadersFetchCommand(imap, cmd, i, true);
// We fetch only known RFC822 headers because
// using Fetch RFC822.HEADER reurns all included unused headers
// which required more memory and network bandwidth.
MB_String cmd2;
appendRFC822HeadersFetchCommand(cmd2);
appendString(cmd, cmd2.c_str(), false, false, esp_mail_string_mark_type_square_bracket);
imap->addModifier(cmd, esp_mail_imap_command_changedsince, imap->_imap_data->fetch.modsequence);
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
{
if (i < imap->_imap_msg_num.size() - 1)
continue;
return false;
}
imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_header;
int err = imap->_headerOnly ? IMAP_STATUS_IMAP_RESPONSE_FAILED : IMAP_STATUS_BAD_COMMAND;
if (!handleIMAPResponse(imap, err, closeSession))
{
if (i < imap->_imap_msg_num.size() - 1)
continue;
return false;
}
if (!cHeader(imap))
continue;
if (imap->_imap_msg_num[i].type == esp_mail_imap_msg_num_type_number)
cHeader(imap)->message_uid = imap->mGetUID(cHeader(imap)->message_no);
cHeader(imap)->flags = imap->getFlags(cHeader(imap)->message_no);
if (!imap->_headerOnly)
{
imap->_cPartIdx = 0;
// Reset attachment state if it was set by "multipart/mixed" content type header
cHeader(imap)->hasAttachment = false;
#if !defined(SILENT_MODE)
if (imap->_debug)
debugPrintNewLine();
#endif
// multipart
if (cHeader(imap)->multipart)
{
struct esp_mail_imap_multipart_level_t mlevel;
mlevel.level = 1;
mlevel.fetch_rfc822_header = false;
mlevel.append_body_text = false;
imap->_multipart_levels.push_back(mlevel);
if (!fetchMultipartBodyHeader(imap, i))
return false;
}
else
{
// single part
if (imap->_debug)
printBodyPartFechingDubug(imap, "1", false);
cHeader(imap)->partNumStr.clear();
if (!sendFetchCommand(imap, i, esp_mail_imap_cmd_fetch_body_mime))
return false;
imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_mime;
if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession))
return false;
}
if (readyToDownload && imap->_imap_data->storage.saved_path.length() == 0)
imap->_imap_data->storage.saved_path = esp_mail_str_10; /* "/" */
if (cHeader(imap)->part_headers.size() > 0)
{
cHeader(imap)->sd_alias_file_count = 0;
imap->_sdFileList.clear();
if (!mbfs->longNameSupported())
imap->_sdFileList = esp_mail_str_40; /* "[" */
#if !defined(SILENT_MODE)
if (cHeader(imap)->attachment_count > 0 && imap->_statusCallback)
{
int bufLen = 100;
char *buf = allocMem<char *>(bufLen);
snprintf(buf, bufLen, pgm2Str(esp_mail_str_54 /* "Attachments (%d)" */), cHeader(imap)->attachment_count);
callBackSendNewLine<IMAPSession *>(imap, false);
sendCallback<IMAPSession *>(imap, buf, false, false);
// release memory
freeMem(&buf);
int count = 0;
for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++)
{
imap->_cPartIdx = j;
if (!cPart(imap)->rfc822_part && cPart(imap)->attach_type != esp_mail_att_type_none)
{
count++;
MB_String str = count;
appendDot(str);
prependSpace(str, cPart(imap)->filename.c_str());
sendCallback<IMAPSession *>(imap, str.c_str(), false, false);
}
}
}
#endif
MB_String s1, s2;
int _idx1 = 0;
for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++)
{
imap->_cPartIdx = j;
if (cPart(imap)->rfc822_part)
{
s1 = cPart(imap)->partNumStr;
_idx1 = cPart(imap)->rfc822_msg_Idx;
}
else if (s1.length() > 0)
{
if (multipartMember(s1, cPart(imap)->partNumStr))
{
cPart(imap)->message_sub_type = esp_mail_imap_message_sub_type_rfc822;
cPart(imap)->rfc822_msg_Idx = _idx1;
}
}
if (cPart(imap)->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel)
s2 = cPart(imap)->partNumStr;
else if (s2.length() > 0)
{
if (multipartMember(s2, cPart(imap)->partNumStr))
{
cPart(imap)->attach_type = esp_mail_att_type_attachment;
if (cPart(imap)->filename.length() == 0)
{
if (cPart(imap)->name.length() > 0)
cPart(imap)->filename = cPart(imap)->name;
else
{
char *uid = getRandomUID();
cPart(imap)->filename = uid;
cPart(imap)->filename += mimeinfo[esp_mail_file_extension_dat].endsWith;
freeMem(&uid);
}
}
}
}
checkFirmwareFile(imap, cPart(imap)->filename.c_str(), *cPart(imap), true);
}
int attach_count = 0;
int ccnt = 0;
for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++)
{
imap->_cPartIdx = j;
if (cPart(imap)->rfc822_part || cPart(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_none)
continue;
bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822 && cPart(imap)->attach_type != esp_mail_att_type_attachment;
if (cPart(imap)->attach_type == esp_mail_att_type_none && (cPart(imap)->msg_type == esp_mail_msg_type_html || cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched))
{
bool ret = ((imap->_imap_data->enable.rfc822 || imap->_imap_data->download.rfc822) && rfc822_body_subtype) || (!rfc822_body_subtype && ((imap->_imap_data->enable.text && (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)) || (imap->_imap_data->enable.html && cPart(imap)->msg_type == esp_mail_msg_type_html) || (cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->download.text)));
if (!ret)
continue;
#if !defined(SILENT_MODE)
if ((imap->_imap_data->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->download.text))))
{
if (ccnt == 0 && imap->_statusCallback)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_43 /* "Downloading messages..." */, true, false);
if (imap->_debug)
{
debugPrintNewLine();
if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
esp_mail_debug_print_tag(esp_mail_dbg_str_74 /* "download plain TEXT message" */, esp_mail_debug_tag_type_client, true);
else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
esp_mail_debug_print_tag(esp_mail_dbg_str_71 /* "download HTML message" */, esp_mail_debug_tag_type_client, true);
}
}
else
{
if (ccnt == 0)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_28 /* "Reading messages..." */, true, false);
if (imap->_debug)
{
debugPrintNewLine();
if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
esp_mail_debug_print_tag(esp_mail_dbg_str_46 /* "reading plain TEXT message" */, esp_mail_debug_tag_type_client, true);
else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
esp_mail_debug_print_tag(esp_mail_dbg_str_47 /* "reading HTML message" */, esp_mail_debug_tag_type_client, true);
}
}
#endif
ccnt++;
if (!sendFetchCommand(imap, i, esp_mail_imap_cmd_fetch_body_text))
return false;
imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_text;
if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession))
return false;
}
else if (cPart(imap)->attach_type != esp_mail_att_type_none && (imap->_storageReady || cPart(imap)->is_firmware_file))
{
if (cPart(imap)->is_firmware_file || (imap->_imap_data->download.attachment && cPart(imap)->attach_type == esp_mail_att_type_attachment) || (imap->_imap_data->download.inlineImg && cPart(imap)->attach_type == esp_mail_att_type_inline))
{
#if !defined(SILENT_MODE)
if (cPart(imap)->save_to_file)
{
if (attach_count == 0 && imap->_statusCallback)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_19 /* "Downloading attachments..." */, true, false);
if (imap->_debug)
{
debugPrintNewLine();
int bufLen = 100;
char *buf = allocMem<char *>(bufLen);
snprintf(buf, bufLen, pgm2Str(esp_mail_dbg_str_70 /* "download attachment %d of %d" */), attach_count + 1, (int)cHeader(imap)->attachment_count);
esp_mail_debug_print_tag(buf, esp_mail_debug_tag_type_client, true);
// release memory
freeMem(&buf);
MB_String filePath = imap->_imap_data->storage.saved_path;
filePath += esp_mail_str_10; /* "/" */
filePath += cHeader(imap)->message_uid;
filePath += esp_mail_str_10; /* "/" */
filePath += cPart(imap)->filename;
esp_mail_debug_print_tag(filePath.c_str(), esp_mail_debug_tag_type_client, true);
}
}
#endif
attach_count++;
if (cPart(imap)->octetLen <= (int)imap->_imap_data->limit.attachment_size)
{
if (imap->_storageReady || cPart(imap)->is_firmware_file)
{
if ((int)j < (int)cHeader(imap)->part_headers.size() - 1)
if (cHeader(imap)->part_headers[j + 1].octetLen > (int)imap->_imap_data->limit.attachment_size)
cHeader(imap)->downloaded_bytes += cHeader(imap)->part_headers[j + 1].octetLen;
if (!sendFetchCommand(imap, i, esp_mail_imap_cmd_fetch_body_attachment))
return false;
imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_attachment;
if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession))
return false;
yield_impl();
}
}
else
{
if ((int)j == (int)cHeader(imap)->part_headers.size() - 1)
cHeader(imap)->downloaded_bytes += cPart(imap)->octetLen;
}
}
}
}
}
if (imap->_storageReady && imap->_imap_data->download.header && !imap->_headerSaved)
{
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_21 /* "Saving message header to file..." */, true, false);
else if (imap->_debug)
debugPrintNewLine();
#endif
saveHeader(imap, false);
saveHeader(imap, true);
}
// save files list to file
if (imap->_storageReady && imap->_sdFileList.length() > 0)
{
MB_String filepath = cHeader(imap)->message_uid;
filepath += mimeinfo[esp_mail_file_extension_txt].endsWith;
if (mbfs->open(filepath, mbfs_type imap->_imap_data->storage.type, mb_fs_open_mode_write) > -1)
{
mbfs->print(mbfs_type imap->_imap_data->storage.type, imap->_sdFileList.c_str());
mbfs->close(mbfs_type imap->_imap_data->storage.type);
}
}
imap->_cMsgIdx++;
}
#if !defined(SILENT_MODE)
if (imap->_debug)
{
MB_String str = esp_mail_str_55; /* "Free Heap: " */
str += MailClient.getFreeHeap();
esp_mail_debug_print_tag(str.c_str(), esp_mail_debug_tag_type_client, true);
}
#endif
}
#if defined(MB_ARDUINO_ESP) || defined(MB_ARDUINO_PICO)
out:
#endif
if (readCount < imap->_imap_msg_num.size())
{
imap->_mbif._availableItems = readCount;
imap->_imap_msg_num.erase(imap->_imap_msg_num.begin() + readCount, imap->_imap_msg_num.end());
}
if (closeSession)
{
if (!imap->closeSession())
return false;
}
else
{
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_cb_str_46 /* "Finished reading Email" */,
esp_mail_dbg_str_29 /* "finished reading Email" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
}
imap->_cbData._success = true;
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
callBackSendNewLine<IMAPSession *>(imap, true);
#endif
return true;
}
void ESP_Mail_Client::appendHeadersFetchCommand(IMAPSession *imap, MB_String &cmd, int index, bool debug)
{
if (imap->_uidSearch || imap->_imap_msg_num[index].type == esp_mail_imap_msg_num_type_uid)
appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_fetch].text);
else
appendSpace(cmd, true, imap_commands[esp_mail_imap_command_fetch].text);
#if !defined(SILENT_MODE)
if (debug && imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_26 /* "fetch message header" */, esp_mail_debug_tag_type_client, true);
#endif
joinStringSpace(cmd, false, 2, MB_String((int)imap->_imap_msg_num[index].value).c_str(), imap_commands[esp_mail_imap_command_body].text);
if (!imap->_imap_data->fetch.set_seen)
prependDot(cmd, imap_commands[esp_mail_imap_command_peek].text);
}
void ESP_Mail_Client::appendRFC822HeadersFetchCommand(MB_String &cmd)
{
joinStringDot(cmd, 2, imap_commands[esp_mail_imap_command_header].text, imap_commands[esp_mail_imap_command_fields].text);
appendSpace(cmd);
MB_String cmd2;
for (int i = 0; i < esp_mail_rfc822_header_field_maxType; i++)
appendSpace(cmd2, false, rfc822_headers[i].text);
joinStringSpace(cmd2, false, 4, message_headers[esp_mail_message_header_field_content_type].text,
message_headers[esp_mail_message_header_field_content_transfer_encoding].text,
message_headers[esp_mail_message_header_field_content_language].text,
message_headers[esp_mail_message_header_field_accept_language].text);
appendString(cmd, cmd2.c_str(), false, false, esp_mail_string_mark_type_round_bracket);
}
bool ESP_Mail_Client::getMultipartFechCmd(IMAPSession *imap, int msgIdx, MB_String &partText)
{
if (imap->_multipart_levels.size() == 0)
return false;
int cLevel = imap->_multipart_levels.size() - 1;
cHeader(imap)->partNumStr.clear();
appendHeadersFetchCommand(imap, partText, msgIdx, false);
MB_String cmd1;
for (size_t i = 0; i < imap->_multipart_levels.size(); i++)
{
if (i > 0)
{
cmd1 += esp_mail_str_27; /* "." */
cHeader(imap)->partNumStr += esp_mail_str_27; /* "." */
}
cmd1 += imap->_multipart_levels[i].level;
cHeader(imap)->partNumStr += imap->_multipart_levels[i].level;
}
if (imap->_multipart_levels[cLevel].fetch_rfc822_header)
{
MB_String cmd2;
appendRFC822HeadersFetchCommand(cmd2);
prependDot(cmd1, cmd2.c_str());
imap->_multipart_levels[cLevel].append_body_text = true;
}
else
prependDot(cmd1, imap_commands[esp_mail_imap_command_mime].text);
appendString(partText, cmd1.c_str(), false, false, esp_mail_string_mark_type_square_bracket);
imap->_multipart_levels[cLevel].fetch_rfc822_header = false;
return true;
}
bool ESP_Mail_Client::multipartMember(const MB_String &parent, const MB_String &child)
{
if (parent.length() > child.length())
return false;
for (size_t i = 0; i < parent.length(); i++)
if (parent[i] != child[i])
return false;
return true;
}
bool ESP_Mail_Client::fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx)
{
bool ret = true;
if (!connected<IMAPSession *>(imap))
{
closeTCPSession<IMAPSession *>(imap);
return false;
}
int cLevel = 0;
// slower than BODYSTRUCTURE parsing but sure
do
{
#if defined(MB_ARDUINO_ESP) || defined(MB_ARDUINO_PICO)
// Prevent stack overflow
if (MailClient.getFreeHeap() < ESP_MAIL_MIN_MEM)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_OUT_OF_MEMORY, true);
break;
}
#endif
struct esp_mail_message_part_info_t *_cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1];
bool rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822 && _cpart->attach_type != esp_mail_att_type_attachment;
MB_String cmd;
if (!getMultipartFechCmd(imap, msgIdx, cmd))
return true;
if (imap->_debug)
printBodyPartFechingDubug(imap, cHeader(imap)->partNumStr.c_str(), imap->_multipart_levels.size() > 1);
// Try fetching the part and its sub parts hierarchically
// Some sub part may not exist at the current multipart level
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_mime;
ret = handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, false);
_cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1];
rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822 && _cpart->attach_type != esp_mail_att_type_attachment;
cLevel = imap->_multipart_levels.size() - 1;
if (ret)
{
if (_cpart->multipart)
{
if (_cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_alternative || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_related || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_mixed)
{
struct esp_mail_imap_multipart_level_t mlevel;
mlevel.level = 1;
mlevel.fetch_rfc822_header = false;
mlevel.append_body_text = false;
imap->_multipart_levels.push_back(mlevel);
fetchMultipartBodyHeader(imap, msgIdx);
}
else
imap->_multipart_levels[cLevel].level++;
}
else
{
if (rfc822_body_subtype)
{
// Get additional rfc822 message header
imap->_multipart_levels[cLevel].fetch_rfc822_header = true;
fetchMultipartBodyHeader(imap, msgIdx);
}
else
{
if (imap->_multipart_levels[cLevel].append_body_text)
{
// single part rfc822 message body, append TEXT to the body fetch command
prependDot(_cpart->partNumFetchStr, imap_commands[esp_mail_imap_command_text].text);
imap->_multipart_levels[cLevel].append_body_text = false;
}
imap->_multipart_levels[cLevel].level++;
}
}
}
} while (ret);
imap->_multipart_levels.pop_back();
if (imap->_multipart_levels.size() > 0)
{
cLevel = imap->_multipart_levels.size() - 1;
imap->_multipart_levels[cLevel].level++;
}
return true;
}
void ESP_Mail_Client::printBodyPartFechingDubug(IMAPSession *imap, const char *partNum, bool multiLevel)
{
#if !defined(SILENT_MODE)
MB_String str = multiLevel ? esp_mail_dbg_str_28 /* "fetch body sub part header, " */ : esp_mail_dbg_str_27; /* "fetch body part header, " */
str += partNum;
esp_mail_debug_print_tag(str.c_str(), esp_mail_debug_tag_type_client, true);
#endif
}
bool ESP_Mail_Client::imapAuth(IMAPSession *imap, bool &ssl)
{
if (!sessionExisted<IMAPSession *>(imap))
return false;
imap->_auth_capability[esp_mail_auth_capability_login] = false;
imap->_session_cfg->int_start_tls = imap->_session_cfg->secure.startTLS;
imap->_session_cfg->int_mode = imap->_session_cfg->secure.mode;
#if !defined(ESP_MAIL_DISABLE_SSL)
unauthenticate:
#endif
// capabilities may change after TLS negotiation
if (!imap->checkCapabilities())
return false;
#if !defined(ESP_MAIL_DISABLE_SSL)
if (imap->_session_cfg->int_mode != esp_mail_secure_mode_nonsecure)
{
// start TLS when needed or the server issues
if ((imap->_auth_capability[esp_mail_auth_capability_starttls] || imap->_session_cfg->int_start_tls || imap->_session_cfg->int_mode == esp_mail_secure_mode_ssl_tls) && !ssl)
{
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_cb_str_2 /* "Sending STARTTLS command..." */,
esp_mail_dbg_str_1 /* "send command, STARTTLS" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
imapSend(imap, imap->prependTag(imap_commands[esp_mail_imap_command_starttls].text).c_str(), true);
// rfc2595 section 3.1
imap->_imap_cmd = esp_mail_imap_cmd_starttls;
if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false))
return false;
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_22 /* "perform SSL/TLS handshake" */, esp_mail_debug_tag_type_client, true);
#endif
// connect in secure mode
// do TLS handshake
if (!imap->client.connectSSL(imap->_session_cfg->certificate.verify))
return handleIMAPError(imap, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP, false);
// set the secure mode
imap->_session_cfg->int_start_tls = false;
imap->_session_cfg->int_mode = esp_mail_secure_mode_undefined;
ssl = true;
imap->_secure = true;
// check the capabilitiy again to prevent the man in the middle attack
goto unauthenticate;
}
}
#endif
imap->clearMessageData();
imap->_mailboxOpened = false;
bool creds = imap->_session_cfg->login.email.length() > 0 && imap->_session_cfg->login.password.length() > 0;
bool sasl_auth_oauth = imap->_session_cfg->login.accessToken.length() > 0 && imap->_auth_capability[esp_mail_auth_capability_xoauth2];
bool sasl_login = creds;
bool sasl_auth_plain = imap->_auth_capability[esp_mail_auth_capability_plain] && creds;
bool supported_sasl = sasl_auth_oauth || sasl_login || sasl_auth_plain;
if (!supported_sasl)
return handleIMAPError(imap, IMAP_STATUS_NO_SUPPORTED_AUTH, false);
// rfc4959
if (supported_sasl)
{
#if !defined(SILENT_MODE)
if (imap->_statusCallback)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_14 /* "Logging in..." */, true, false);
else if (imap->_debug)
debugPrintNewLine();
#endif
}
if (sasl_auth_oauth)
{
if (!imap->_auth_capability[esp_mail_auth_capability_xoauth2])
{
handleIMAPError(imap, IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false);
return false;
}
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_45 /* "send IMAP command, AUTH XOAUTH2" */, esp_mail_debug_tag_type_client, true);
#endif
MB_String cmd;
joinStringSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_authenticate].text, imap_commands[esp_mail_imap_command_xoauth2].text);
if (imap->_auth_capability[esp_mail_auth_capability_sasl_ir])
{
prependSpace(cmd, getXOAUTH2String(imap->_session_cfg->login.email, imap->_session_cfg->login.accessToken).c_str());
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
}
else
{
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_sasl_auth_oauth;
if (!handleIMAPResponse(imap, IMAP_STATUS_AUTHENTICATE_FAILED, true))
return false;
cmd = getXOAUTH2String(imap->_session_cfg->login.email, imap->_session_cfg->login.accessToken);
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
}
imap->_imap_cmd = esp_mail_imap_cmd_sasl_auth_oauth;
if (!handleIMAPResponse(imap, IMAP_STATUS_AUTHENTICATE_FAILED, false))
return false;
}
else if (sasl_auth_plain)
{
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_44 /* "send IMAP command, AUTHENTICATE PLAIN" */, esp_mail_debug_tag_type_client, true);
#endif
int len = imap->_session_cfg->login.email.length() + imap->_session_cfg->login.password.length() + 2;
uint8_t *tmp = allocMem<uint8_t *>(len);
memset(tmp, 0, len);
int p = 1;
memcpy(tmp + p, imap->_session_cfg->login.email.c_str(), imap->_session_cfg->login.email.length());
p += imap->_session_cfg->login.email.length() + 1;
memcpy(tmp + p, imap->_session_cfg->login.password.c_str(), imap->_session_cfg->login.password.length());
p += imap->_session_cfg->login.password.length();
MB_String cmd;
joinStringSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_authenticate].text, imap_commands[esp_mail_imap_command_plain].text);
if (imap->_auth_capability[esp_mail_auth_capability_sasl_ir])
{
prependSpace(cmd, encodeBase64Str(tmp, p).c_str());
// release memory
freeMem(&tmp);
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
}
else
{
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_sasl_auth_plain;
if (!handleIMAPResponse(imap, IMAP_STATUS_AUTHENTICATE_FAILED, true))
return false;
cmd = encodeBase64Str(tmp, p);
// release memory
freeMem(&tmp);
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
}
imap->_imap_cmd = esp_mail_imap_cmd_sasl_auth_plain;
if (!handleIMAPResponse(imap, IMAP_STATUS_AUTHENTICATE_FAILED, true))
return false;
}
else if (sasl_login)
{
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_dbg_str_34 /* "send IMAP command, LOGIN" */, esp_mail_debug_tag_type_client, true);
#endif
MB_String cmd;
joinStringSpace(cmd, true, 3, imap_commands[esp_mail_imap_command_login].text, imap->_session_cfg->login.email.c_str(), imap->_session_cfg->login.password.c_str());
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_sasl_login;
if (!handleIMAPResponse(imap, IMAP_STATUS_AUTHENTICATE_FAILED, true))
return false;
}
// auto capabilities after login?
if (!imap->_feature_capability[esp_mail_imap_read_capability_auto_caps])
{
if (!imap->checkCapabilities())
return false;
}
if (imap->_feature_capability[esp_mail_imap_read_capability_id])
{
if (!imap->id(&imap->_imap_data->identification))
return false;
}
if (supported_sasl)
imap->_authenticated = true;
return true;
}
bool ESP_Mail_Client::imapLogout(IMAPSession *imap)
{
#if defined(ESP8266)
return false;
#endif
if (!sessionExisted<IMAPSession *>(imap))
return false;
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_cb_str_20 /* "Logging out..." */,
esp_mail_dbg_str_38 /* "send IMAP command, LOGOUT" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (imapSend(imap, imap->prependTag(imap_commands[esp_mail_imap_command_logout].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_logout;
if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false))
return false;
imap->_authenticated = false;
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_cb_str_47 /* "Log out completed" */,
esp_mail_dbg_str_31 /* "log out completed" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
return true;
}
size_t ESP_Mail_Client::imapSend(IMAPSession *imap, PGM_P data, bool newline)
{
if (!imap || !sessionReady<IMAPSession *>(imap))
return 0;
int sent = 0;
MB_String s = data;
int toSend = newline ? s.length() + 2 : s.length();
if (imap->_debug && imap->_debugLevel > esp_mail_debug_level_maintainer && !imap->_customCmdResCallback)
esp_mail_debug_print(s.c_str(), newline);
sent = newline ? imap->client.println(s.c_str()) : imap->client.print(s.c_str());
if (sent != toSend)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, sent, true);
sent = 0;
}
return sent;
}
size_t ESP_Mail_Client::imapSend(IMAPSession *imap, int data, bool newline)
{
MB_String s = data;
return imapSend(imap, s.c_str(), newline);
}
size_t ESP_Mail_Client::imapSend(IMAPSession *imap, uint8_t *data, size_t size)
{
if (!imap || !sessionReady<IMAPSession *>(imap))
return 0;
int sent = 0;
sent = imap->client.write(data, size);
if (sent != (int)size)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, sent, true);
sent = 0;
}
return sent;
}
bool ESP_Mail_Client::mSetFlag(IMAPSession *imap, MB_StringPtr sequenceSet, MB_StringPtr flag, esp_mail_imap_store_flag_type type, bool closeSession, bool silent, bool UID, int32_t modsequence)
{
if (!reconnect(imap))
return false;
if (!imap->connected())
{
imap->_mailboxOpened = false;
return false;
}
if (imap->_currentFolder.length() == 0)
{
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
esp_mail_error_imap_str_11 /* "no mailbox opened" */,
esp_mail_error_imap_str_11 /* "no mailbox opened" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
}
else
{
if (imap->_readOnlyMode || !imap->_mailboxOpened)
{
if (!imap->selectFolder(imap->_currentFolder.c_str(), false))
return false;
}
}
#if !defined(SILENT_MODE)
PGM_P p1 = NULL;
PGM_P p2 = NULL;
if (type == esp_mail_imap_store_flag_type_set)
{
p1 = esp_mail_cb_str_26; /* "Setting FLAG..." */
p2 = esp_mail_dbg_str_41; /* "setting FLAG" */
}
else if (type == esp_mail_imap_store_flag_type_add)
{
p1 = esp_mail_cb_str_24; /* "Adding FLAG..." */
p2 = esp_mail_dbg_str_42; /* "adding FLAG" */
}
else
{
p1 = esp_mail_cb_str_23; /* "Removing FLAG..." */
p2 = esp_mail_dbg_str_43; /* "removing FLAG" */
}
printDebug<IMAPSession *>(imap,
p1,
p2,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!sessionExisted<IMAPSession *>(imap))
return false;
MB_String cmd;
if (UID)
appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_store].text);
else
appendSpace(cmd, true, imap_commands[esp_mail_imap_command_store].text);
cmd += sequenceSet;
imap->addModifier(cmd, esp_mail_imap_command_unchangedsince, modsequence);
if (type == esp_mail_imap_store_flag_type_set)
cmd += imap_cmd_pre_tokens[esp_mail_imap_command_flags];
else if (type == esp_mail_imap_store_flag_type_add)
cmd += imap_cmd_pre_tokens[esp_mail_imap_command_plus_flags];
else
cmd += imap_cmd_pre_tokens[esp_mail_imap_command_minus_flags];
if (silent)
prependDot(cmd, imap_commands[esp_mail_imap_command_silent].text);
appendSpace(cmd);
appendString(cmd, MB_String(flag).c_str(), false, false, esp_mail_string_mark_type_round_bracket);
if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
imap->_imap_cmd = esp_mail_imap_cmd_store;
if (!handleIMAPResponse(imap, IMAP_STATUS_STORE_FAILED, false))
return false;
if (closeSession)
imap->closeSession();
return true;
}
int ESP_Mail_Client::parseSearchResponse(IMAPSession *imap, esp_mail_imap_response_data &res, PGM_P tag, const char *key)
{
int bufLen = res.chunkBufSize;
int ret = -1;
char c = 0;
int idx = 0;
int num = 0;
size_t tagLen = strlen_P(tag);
MB_String _tag = tag;
while (imap->client.available() > 0 && idx < bufLen)
{
yield_impl();
ret = imap->client.read();
if (ret > -1)
{
if (idx >= bufLen - 1)
return idx;
c = (char)ret;
if (c == '\n')
c = ' ';
res.response[idx++] = c;
if (res.chunkIdx == 0)
{
// Search response parsing
if (strcmp(res.response, key) == 0)
{
res.chunkIdx++;
return 0;
}
else
{
// Status response parsing
res.imapResp = imapResponseStatus(imap, res.response, esp_mail_imap_tag_str);
// Exit if error or complete (no messages found)
if (res.imapResp != esp_mail_imap_resp_unknown)
goto end_search;
}
}
else
{
if (c == ' ')
{
imap->_mbif._searchCount++;
if (imap->_imap_data->enable.recent_sort)
{
esp_mail_imap_msg_num_t msg_num;
msg_num.type = imap->_uidSearch ? esp_mail_imap_msg_num_type_uid : esp_mail_imap_msg_num_type_number;
msg_num.value = (uint32_t)atoi(res.response);
imap->_imap_msg_num.push_back(msg_num);
if (imap->_imap_msg_num.size() > imap->_imap_data->limit.search)
imap->_imap_msg_num.erase(imap->_imap_msg_num.begin());
}
else
{
if (imap->_imap_msg_num.size() < imap->_imap_data->limit.search)
{
esp_mail_imap_msg_num_t msg_num;
msg_num.type = imap->_uidSearch ? esp_mail_imap_msg_num_type_uid : esp_mail_imap_msg_num_type_number;
msg_num.value = (uint32_t)atoi(res.response);
imap->_imap_msg_num.push_back(msg_num);
}
}
if (imap->_debug)
{
num = (float)(100.0f * imap->_mbif._searchCount / imap->_mbif._msgCount);
if (res.searchCount != num)
{
res.searchCount = num;
searchReport(imap, num);
}
}
res.chunkIdx++;
return idx;
}
else if (idx >= (int)tagLen)
{
if (strpos(res.response, _tag.c_str(), 0, false) > -1)
{
#if defined(MB_USE_STD_VECTOR)
if (imap->_imap_data->enable.recent_sort)
std::sort(imap->_imap_msg_num.begin(), imap->_imap_msg_num.end(), compareMore);
#else
if (imap->_imap_data->enable.recent_sort)
numDecSort(imap->_imap_msg_num);
#endif
goto end_search;
}
}
}
}
}
return idx;
end_search:
res.endSearch = true;
int read = imap->client.available();
read = imap->client.readBytes(res.response + idx, read);
return idx + read;
}
#if !defined(MB_USE_STD_VECTOR)
void ESP_Mail_Client::numDecSort(_vectorImpl<struct esp_mail_imap_msg_num_t> &arr)
{
struct esp_mail_imap_msg_num_t tmp;
for (size_t i = 0; i < arr.size(); ++i)
{
for (size_t j = i + 1; j < arr.size(); ++j)
{
if (arr[i].value < arr[j].value)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
#endif
struct esp_mail_message_part_info_t *ESP_Mail_Client::cPart(IMAPSession *imap)
{
if (cHeader(imap) && imap->_cPartIdx < (int)cHeader(imap)->part_headers.size())
return &cHeader(imap)->part_headers[imap->_cPartIdx];
return nullptr;
}
struct esp_mail_message_header_t *ESP_Mail_Client::cHeader(IMAPSession *imap)
{
if (cIdx(imap) < (int)imap->_headers.size())
return &imap->_headers[cIdx(imap)];
return nullptr;
}
bool ESP_Mail_Client::parseHeaderField(IMAPSession *imap, const char *buf, PGM_P beginToken, bool caseSensitive, struct esp_mail_message_header_t &header, int &headerState, int state)
{
if (strcmpP(buf, 0, beginToken, caseSensitive))
{
headerState = state;
char *tmp = subStr(buf, beginToken, NULL, 0, -1, caseSensitive);
if (tmp)
{
collectHeaderField(imap, tmp, header, headerState);
// release memory
freeMem(&tmp);
return true;
}
}
return false;
}
void ESP_Mail_Client::parseHeaderResponse(IMAPSession *imap, esp_mail_imap_response_data &res, bool caseSensitive)
{
char *tmp = nullptr;
if (res.chunkIdx == 0)
{
MB_String str;
joinStringDot(str, 2, imap_commands[esp_mail_imap_command_header].text, imap_commands[esp_mail_imap_command_fields].text);
if (!res.isUntaggedResponse && strposP(res.response, str.c_str(), 0, caseSensitive) != -1 && res.response[0] == '*')
res.isUntaggedResponse = true;
if (res.isUntaggedResponse && res.response[strlen(res.response) - 1] == '}')
res.untaggedRespCompleted = true;
if (res.isUntaggedResponse && res.header.message_no == 0)
{
tmp = subStr(res.response, imap_responses[esp_mail_imap_response_untagged].text, imap_responses[esp_mail_imap_response_fetch].text, 0);
if (tmp)
{
res.header.message_no = atoi(tmp);
// release memory
freeMem(&tmp);
}
}
if (res.isUntaggedResponse && res.untaggedRespCompleted)
{
tmp = subStr(res.response, esp_mail_str_36 /* "{" */, esp_mail_str_37 /* "}" */, 0, 0, caseSensitive);
if (tmp)
{
res.octetCount = 2;
res.header.header_data_len = atoi(tmp);
// release memory
freeMem(&tmp);
res.chunkIdx++;
}
}
}
else
{
if (res.octetCount > res.header.header_data_len + 2)
return;
res.chunkIdx++;
MB_String field;
for (int i = esp_mail_rfc822_header_field_from; i < esp_mail_rfc822_header_field_maxType; i++)
{
appendHeaderName(field, rfc822_headers[i].text, true, false, false);
if (parseHeaderField(imap, res.response, field.c_str(), caseSensitive, res.header, res.headerState, i))
return;
}
appendHeaderName(field, message_headers[esp_mail_message_header_field_content_transfer_encoding].text, true, true, false);
if (parseHeaderField(imap, res.response, field.c_str(), caseSensitive, res.header, res.headerState, esp_mail_imap_state_content_transfer_encoding))
return;
appendHeaderName(field, message_headers[esp_mail_message_header_field_accept_language].text, true, true, false);
if (parseHeaderField(imap, res.response, field.c_str(), caseSensitive, res.header, res.headerState, esp_mail_imap_state_accept_language))
return;
appendHeaderName(field, message_headers[esp_mail_message_header_field_content_language].text, true, true, false);
if (parseHeaderField(imap, res.response, field.c_str(), caseSensitive, res.header, res.headerState, esp_mail_imap_state_content_language))
return;
MB_String contentTypeName;
appendHeaderName(contentTypeName, message_headers[esp_mail_message_header_field_content_type].text, false, false, false);
if (strcmpP(res.response, 0, contentTypeName.c_str(), caseSensitive))
{
res.headerState = esp_mail_imap_state_content_type;
tmp = subStr(res.response, contentTypeName.c_str(), esp_mail_str_35 /* ";" */, 0, 0, caseSensitive);
if (tmp)
{
// We set attachment status here as attachment should be included in multipart/mixed message,
// unless no real attachments included which we don't know until fetching the sub part.
if (strpos(tmp, esp_mail_imap_multipart_sub_type_t::mixed, 0, caseSensitive) != -1)
res.header.hasAttachment = true;
collectHeaderField(imap, res.response, res.header, res.headerState);
// release memory
freeMem(&tmp);
}
}
}
}
void ESP_Mail_Client::collectHeaderField(IMAPSession *imap, char *buf, struct esp_mail_message_header_t &header, int state)
{
size_t i = 0;
while (buf[i] == ' ')
{
i++;
if (strlen(buf) <= i)
return;
}
if (state < esp_mail_rfc822_header_field_maxType)
{
int ptr = getRFC822HeaderPtr(state, &header.header_fields);
if (ptr > 0)
{
*(addrTo<MB_String *>(ptr)) += &buf[i];
}
return;
}
switch (state)
{
case esp_mail_imap_state_content_type:
header.content_type += &buf[i];
break;
case esp_mail_imap_state_content_transfer_encoding:
header.content_transfer_encoding += &buf[i];
break;
case esp_mail_imap_state_accept_language:
header.accept_language += &buf[i];
break;
case esp_mail_imap_state_content_language:
header.content_language += &buf[i];
break;
case esp_mail_imap_state_char_set:
header.char_set += &buf[i];
break;
case esp_mail_imap_state_boundary:
header.boundary += &buf[i];
break;
default:
break;
}
}
bool ESP_Mail_Client::getDecodedHeader(IMAPSession *imap, const char *buf, PGM_P beginToken, MB_String &out, bool caseSensitive)
{
if (getHeader(buf, beginToken, out, caseSensitive))
{
// decode header text
decodeString(imap, out);
return true;
}
return false;
}
void ESP_Mail_Client::checkFirmwareFile(IMAPSession *imap, const char *filename, struct esp_mail_message_part_info_t &part, bool defaultSize)
{
if (strcmp(filename, imap->_imap_data->firmware_update.attach_filename.c_str()) == 0 && part.attach_type == esp_mail_att_type_attachment)
{
part.is_firmware_file = true;
// If no file size prop from Content-Disposition header
if (part.attach_data_size == 0 && defaultSize)
{
#if defined(ESP32) || defined(ESP8266)
int sketchFreeSpace = ESP.getFreeSketchSpace();
part.attach_data_size = sketchFreeSpace ? sketchFreeSpace : 1024000;
#elif defined(MB_ARDUINO_PICO)
size_t spiffsSize = ((size_t)&_FS_end - (size_t)&_FS_start);
part.attach_data_size = spiffsSize ? spiffsSize / 2 : 1024000;
#endif
}
if (!imap->_imap_data->firmware_update.save_to_file)
part.save_to_file = false;
}
}
void ESP_Mail_Client::parsePartHeaderResponse(IMAPSession *imap, esp_mail_imap_response_data &res, bool caseSensitive)
{
char *tmp = nullptr;
if (res.chunkIdx == 0)
{
tmp = subStr(res.response, imap_responses[esp_mail_imap_response_fetch].text, NULL, 0, -1);
if (tmp)
{
// release memory
freeMem(&tmp);
tmp = subStr(res.response, esp_mail_str_36 /* "{" */, esp_mail_str_37 /* "}" */, 0);
if (tmp)
{
res.chunkIdx++;
res.part.octetLen = atoi(tmp);
res.octetCount = 2;
// release memory
freeMem(&tmp);
}
}
}
else
{
MB_String value, old_value;
bool valueStored = false;
res.chunkIdx++;
// if all octets read
if (res.octetCount > res.part.octetLen)
{
// Is inline attachment without content id or name or filename?
// It is supposed to be the inline message txt content, reset attach type to none
if (res.part.attach_type == esp_mail_att_type_inline && res.part.CID.length() == 0)
res.part.attach_type = esp_mail_att_type_none;
// Is attachment file extension missing?
// append extension
if (res.part.attach_type == esp_mail_att_type_inline || res.part.attach_type == esp_mail_att_type_attachment)
{
if (res.part.filename.length() > 0 && res.part.filename.find('.') == MB_String::npos)
{
MB_String ext;
getExtfromMIME(res.part.content_type.c_str(), ext);
res.part.filename += ext;
}
checkFirmwareFile(imap, res.part.filename.c_str(), res.part);
}
return;
}
// Content header field parse
if (strcmpP(res.response, 0, esp_mail_str_56 /* "content-" */, caseSensitive))
{
// Content-Type
MB_String contentTypeName;
appendHeaderName(contentTypeName, message_headers[esp_mail_message_header_field_content_type].text, false, false, false);
if (strcmpP(res.response, 0, contentTypeName.c_str(), caseSensitive))
{
res.part.cur_content_hdr = esp_mail_message_part_info_t::content_header_field_type;
resetStringPtr(res.part);
tmp = subStr(res.response, contentTypeName.c_str(), esp_mail_str_35 /* ";" */, 0, 0, caseSensitive);
if (tmp)
{
res.part.content_type = tmp;
// release memory
freeMem(&tmp);
int p1 = strposP(res.part.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0, caseSensitive);
if (p1 != -1)
{
p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1;
res.part.multipart = true;
// inline or embedded images
if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_related;
// multiple text formats e.g. plain, html, enriched
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative;
// medias
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel;
// rfc822 encapsulated
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_report;
// others can be attachments
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1, caseSensitive) != -1)
res.part.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed;
}
p1 = strposP(res.part.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0, caseSensitive);
if (p1 != -1)
{
p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1;
if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1, caseSensitive) != -1)
res.part.message_sub_type = esp_mail_imap_message_sub_type_rfc822;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1, caseSensitive) != -1)
res.part.message_sub_type = esp_mail_imap_message_sub_type_partial;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1, caseSensitive) != -1)
res.part.message_sub_type = esp_mail_imap_message_sub_type_external_body;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1, caseSensitive) != -1)
res.part.message_sub_type = esp_mail_imap_message_sub_type_delivery_status;
}
p1 = strpos(res.part.content_type.c_str(), esp_mail_imap_descrete_media_type_t::text, 0, caseSensitive);
if (p1 != -1)
{
p1 += strlen(esp_mail_imap_descrete_media_type_t::text) + 1;
if (strpos(res.part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::plain, p1, caseSensitive) != -1)
res.part.msg_type = esp_mail_msg_type_plain;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::enriched, p1, caseSensitive) != -1)
res.part.msg_type = esp_mail_msg_type_enriched;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::html, p1, caseSensitive) != -1)
res.part.msg_type = esp_mail_msg_type_html;
else
res.part.msg_type = esp_mail_msg_type_plain;
}
}
}
// Content-Description
MB_String contentDescrName;
appendHeaderName(contentDescrName, message_headers[esp_mail_message_header_field_content_description].text, false, true, false);
if (getDecodedHeader(imap, res.response, contentDescrName.c_str(), res.part.descr, caseSensitive))
{
res.part.cur_content_hdr = esp_mail_message_part_info_t::content_header_field_description;
tmp = subStr(res.response, contentDescrName.c_str(), NULL, 0, -1, caseSensitive);
if (tmp)
{
value = tmp;
// release memory
freeMem(&tmp);
res.part.stringPtr = toAddr(res.part.content_description);
value.trim();
if (value.length() == 0)
return;
}
}
// Content-ID
MB_String contentIdName;
appendHeaderName(contentIdName, message_headers[esp_mail_message_header_field_content_id].text, false, true, false);
if (strcmpP(res.response, 0, contentIdName.c_str(), caseSensitive))
{
tmp = subStr(res.response, contentIdName.c_str(), NULL, 0, -1, caseSensitive);
if (tmp)
{
res.part.CID = tmp;
// release memory
freeMem(&tmp);
res.part.CID.trim();
if (res.part.CID[0] == '<')
res.part.CID.erase(0, 1);
if (res.part.CID[res.part.CID.length() - 1] == '>')
res.part.CID.erase(res.part.CID.length() - 1, 1);
// if inline attachment file name was not assigned
if (res.part.attach_type == esp_mail_att_type_inline && res.part.filename.length() == 0)
{
// set filename from content id and append extension later
res.part.filename = res.part.CID;
res.part.name = res.part.filename;
}
}
res.part.cur_content_hdr = esp_mail_message_part_info_t::content_header_field_id;
resetStringPtr(res.part);
}
// Content-Disposition
MB_String contentDispositionName;
appendHeaderName(contentDispositionName, message_headers[esp_mail_message_header_field_content_disposition].text, false, true, false);
if (strcmpP(res.response, 0, contentDispositionName.c_str(), caseSensitive))
{
res.part.cur_content_hdr = esp_mail_message_part_info_t::content_header_field_disposition;
resetStringPtr(res.part);
tmp = subStr(res.response, contentDispositionName.c_str(), esp_mail_str_35 /* ";" */, 0, 0, caseSensitive);
if (tmp)
{
// don't count altenative part text and html as embedded contents
if (cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative)
{
res.part.content_disposition = tmp;
if (caseSensitive)
{
if (strcmp(tmp, esp_mail_content_disposition_type_t::attachment) == 0)
res.part.attach_type = esp_mail_att_type_attachment;
else if (strcmp(tmp, esp_mail_content_disposition_type_t::inline_) == 0)
res.part.attach_type = esp_mail_att_type_inline;
}
else
{
if (strcasecmp(tmp, esp_mail_content_disposition_type_t::attachment) == 0)
res.part.attach_type = esp_mail_att_type_attachment;
else if (strcasecmp(tmp, esp_mail_content_disposition_type_t::inline_) == 0)
res.part.attach_type = esp_mail_att_type_inline;
}
}
// release memory
freeMem(&tmp);
}
}
// Content-Transfer-Encoding
MB_String contentTEName;
appendHeaderName(contentTEName, message_headers[esp_mail_message_header_field_content_transfer_encoding].text, false, true, false);
if (strcmpP(res.response, 0, contentTEName.c_str(), caseSensitive))
{
// store last text field
res.part.cur_content_hdr = esp_mail_message_part_info_t::content_header_field_transfer_enc;
resetStringPtr(res.part);
tmp = subStr(res.response, contentTEName.c_str(), NULL, 0, -1, caseSensitive);
if (tmp)
{
res.part.content_transfer_encoding = tmp;
if (strcmpP(tmp, 0, esp_mail_transfer_encoding_t::enc_base64))
res.part.xencoding = esp_mail_msg_xencoding_base64;
else if (strcmpP(tmp, 0, esp_mail_transfer_encoding_t::enc_qp))
res.part.xencoding = esp_mail_msg_xencoding_qp;
else if (strcmpP(tmp, 0, esp_mail_transfer_encoding_t::enc_7bit))
res.part.xencoding = esp_mail_msg_xencoding_7bit;
else if (strcmpP(tmp, 0, esp_mail_transfer_encoding_t::enc_8bit))
res.part.xencoding = esp_mail_msg_xencoding_8bit;
else if (strcmpP(tmp, 0, esp_mail_transfer_encoding_t::enc_binary))
res.part.xencoding = esp_mail_msg_xencoding_binary;
// release memory
freeMem(&tmp);
}
}
}
else
{
if (res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_none)
{
resetStringPtr(res.part);
MB_String field;
for (int i = esp_mail_rfc822_header_field_from; i < esp_mail_rfc822_header_field_maxType; i++)
{
field = rfc822_headers[i].text;
field += esp_mail_str_34; /* ":" */
;
int ptr = getRFC822HeaderPtr(i, &res.part.rfc822_header);
if (ptr > 0)
{
if (getDecodedHeader(imap, res.response, field.c_str(), *(addrTo<MB_String *>(ptr)), caseSensitive))
return;
}
}
}
}
// parse content type header sub type properties
if (res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_type)
{
if (res.part.msg_type == esp_mail_msg_type_plain || res.part.msg_type == esp_mail_msg_type_enriched)
{
MB_String charset;
appendLowerCaseString(charset, message_headers[esp_mail_message_header_field_charset].text, false);
// We have to check for both quotes string or non quote string
if (getPartHeaderProperties(imap, res.response, charset.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.charset = value;
resetStringPtr(res.part);
}
else if (getPartHeaderProperties(imap, res.response, charset.c_str(), esp_mail_str_35 /* ";" */, true, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.charset = value;
resetStringPtr(res.part);
}
if (strposP(res.response, esp_mail_str_59 /* "format=flowed" */, 0, caseSensitive) > -1 || strposP(res.response, esp_mail_str_58 /* "format=\"flowed\"" */, 0, caseSensitive) > -1)
{
res.part.plain_flowed = true;
resetStringPtr(res.part);
}
if (strposP(res.response, esp_mail_str_61 /* "delsp=yes" */, 0, caseSensitive) > -1 || strposP(res.response, esp_mail_str_60 /* "delsp=\"yes\"" */, 0, caseSensitive) > -1)
{
res.part.plain_delsp = true;
resetStringPtr(res.part);
}
}
if (res.part.charset.length() == 0)
{
MB_String charset;
appendLowerCaseString(charset, message_headers[esp_mail_message_header_field_charset].text, false);
// We have to check for both quotes string or non quote string
if (getPartHeaderProperties(imap, res.response, charset.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.charset = value;
resetStringPtr(res.part);
}
else if (getPartHeaderProperties(imap, res.response, charset.c_str(), esp_mail_str_35 /* ";" */, true, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.charset = value;
resetStringPtr(res.part);
}
}
MB_String name;
appendLowerCaseString(name, message_headers[esp_mail_message_header_field_name].text, false);
// We have to check for both quotes string or non quote string
if (getPartHeaderProperties(imap, res.response, name.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.stringPtr = toAddr(res.part.name);
value.trim();
if (value.length() == 0)
return;
}
else if (getPartHeaderProperties(imap, res.response, name.c_str(), esp_mail_str_35 /* ";" */, true, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.stringPtr = toAddr(res.part.name);
value.trim();
if (value.length() == 0)
return;
}
}
// parse content disposition header sub type properties
if (res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_disposition && res.part.content_disposition.length() > 0)
{
// filename prop
MB_String filename;
appendLowerCaseString(filename, message_headers[esp_mail_message_header_field_filename].text, false);
// We have to check for both quotes string or non quote string
if (getPartHeaderProperties(imap, res.response, filename.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.stringPtr = toAddr(res.part.filename);
value.trim();
if (value.length() == 0)
return;
}
else if (getPartHeaderProperties(imap, res.response, filename.c_str(), esp_mail_str_35 /* ";" */, true, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.stringPtr = toAddr(res.part.filename);
value.trim();
if (value.length() == 0)
return;
}
// size prop
MB_String size;
appendLowerCaseString(size, message_headers[esp_mail_message_header_field_size].text, false);
if (getPartHeaderProperties(imap, res.response, size.c_str(), esp_mail_str_35 /* ";" */, true, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.attach_data_size = atoi(value.c_str());
cHeader(imap)->total_attach_data_size += res.part.attach_data_size;
res.part.sizeProp = true;
if (!valueStored && old_value.length() > 0)
valueStored = storeStringPtr(imap, res.part.stringPtr, old_value, res.response);
resetStringPtr(res.part);
}
// creation date prop
MB_String creationDate;
appendLowerCaseString(creationDate, message_headers[esp_mail_message_header_field_creation_date].text, false);
if (getPartHeaderProperties(imap, res.response, creationDate.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.creation_date = value;
if (!valueStored && old_value.length() > 0)
valueStored = storeStringPtr(imap, res.part.stringPtr, old_value, res.response);
resetStringPtr(res.part);
}
// mod date prop
MB_String modDate;
appendLowerCaseString(modDate, message_headers[esp_mail_message_header_field_modification_date].text, false);
if (getPartHeaderProperties(imap, res.response, modDate.c_str(), esp_mail_str_11 /* "\"" */, false, value, old_value, res.part.stringEnc, caseSensitive))
{
res.part.modification_date = value;
if (!valueStored && old_value.length() > 0)
valueStored = storeStringPtr(imap, res.part.stringPtr, old_value, res.response);
resetStringPtr(res.part);
}
}
if (!valueStored && (res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_description || res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_type || res.part.cur_content_hdr == esp_mail_message_part_info_t::content_header_field_disposition))
storeStringPtr(imap, res.part.stringPtr, value, res.response);
}
}
void ESP_Mail_Client::resetStringPtr(struct esp_mail_message_part_info_t &part)
{
part.stringPtr = 0;
part.stringEnc = esp_mail_char_decoding_scheme_default;
}
int ESP_Mail_Client::countChar(const char *buf, char find)
{
if (!buf)
return 0;
int count = 0;
for (size_t i = 0; i < strlen(buf); i++)
{
if (buf[i] == find)
count++;
}
return count;
}
bool ESP_Mail_Client::storeStringPtr(IMAPSession *imap, uint32_t addr, MB_String &value, const char *buf)
{
if (addr)
{
MB_String *a = addrTo<MB_String *>(addr);
MB_String s = value.length() > 0 ? value : buf;
// is value string contains double quotes?
// trim it
if (countChar(s.c_str(), '"') == 2)
s.trim();
if (s[0] == '"')
s.erase(0, 1);
if (s[s.length() - 1] == '"')
s.erase(s.length() - 1, 1);
decodeString(imap, s);
*a += s;
return true;
}
return false;
}
bool ESP_Mail_Client::getPartHeaderProperties(IMAPSession *imap, const char *buf, PGM_P p, PGM_P e, bool num, MB_String &value, MB_String &old_value, esp_mail_char_decoding_scheme &scheme, bool caseSensitive)
{
MB_String str = p;
str += esp_mail_str_7; /* "=" */
if (!num)
str += esp_mail_str_11; /* "\"" */
char *tmp = subStr(buf, str.c_str(), e, 0, 0, caseSensitive);
if (!tmp)
{
str = p;
str += esp_mail_str_7; /* "=" */
tmp = subStr(buf, str.c_str(), e, 0, 0, caseSensitive);
if (tmp)
{
// other sub headers found?
int p2 = strposP(tmp, esp_mail_str_35 /* ";" */, 0, caseSensitive);
if (p2 > -1)
{
// release memory
freeMem(&tmp);
tmp = subStr(buf, str.c_str(), esp_mail_str_35 /* ";" */, 0, 0, caseSensitive);
}
// release memory in case above condition does not match
freeMem(&tmp);
}
else
{
// Extended notation rfc5987
str = p;
str += esp_mail_str_3; /* "*" */
int p2 = strpos(buf, str.c_str(), 0, caseSensitive);
if (p2 > -1)
{
int p3 = strposP(buf, esp_mail_str_3 /* "*" */, p2 + str.length() + 1, caseSensitive);
if (p3 > -1 && p3 < (int)strlen(buf))
{
MB_String charset;
p3 += 2;
int p4 = strpos(buf, "'", p3, caseSensitive);
if (p4 > -1)
{
scheme = getEncodingFromCharset(buf);
int c1 = p4 + 1;
p4 = strpos(buf, "'", p4 + 1, caseSensitive);
int c2 = p4;
if (c2 > -1)
{
charset.append(buf + c1, c2 - c1);
}
p3 = p4 + 1;
}
int len = strlen(buf) - p3;
tmp = allocMem<char *>(len + 1);
if (buf[strlen(buf) - 1] == ';')
len--;
memcpy(tmp, &buf[p3], len);
if (scheme == esp_mail_char_decoding_scheme_utf_8)
{
char *buf2 = urlDecode(tmp);
// release memory and point to new buffer
freeMem(&tmp);
tmp = buf2;
}
else if (imap->_charDecCallback)
{
IMAP_Decoding_Info decoding;
decoding.charset = charset.c_str();
decoding.data = tmp;
decoding.type = IMAP_Decoding_Info::message_part_type_header;
imap->_charDecCallback(&decoding);
if (decoding.decodedString.length() > 0)
{
char *buf2 = allocMem<char *>(decoding.decodedString.length() + 1);
strcpy(buf2, decoding.decodedString.c_str());
// release memory and point to new buffer
freeMem(&tmp);
tmp = buf2;
}
}
else if (scheme == esp_mail_char_decoding_scheme_iso8859_1)
{
int ilen = strlen(tmp);
int olen = (ilen + 1) * 2;
char *buf2 = allocMem<char *>(olen);
decodeLatin1_UTF8((unsigned char *)buf2, &olen, (unsigned char *)tmp, &ilen);
// release memory and point to new buffer
freeMem(&tmp);
tmp = buf2;
}
else if (scheme == esp_mail_char_decoding_scheme_tis_620 || scheme == esp_mail_char_decoding_scheme_iso8859_11 || scheme == esp_mail_char_decoding_scheme_windows_874)
{
int ilen = strlen(tmp);
char *buf2 = allocMem<char *>((ilen + 1) * 3);
decodeTIS620_UTF8(buf2, tmp, ilen);
// release memory and point to new buffer
freeMem(&tmp);
tmp = buf2;
}
}
}
}
}
if (tmp)
{
old_value = value;
value = tmp;
// release memory
freeMem(&tmp);
return true;
}
return false;
}
char *ESP_Mail_Client::urlDecode(const char *str)
{
int d = 0; /* whether or not the string is decoded */
char *dStr = allocMem<char *>(strlen(str) + 1);
char eStr[] = "00"; /* for a hex code */
strcpy(dStr, str);
while (!d)
{
d = 1;
size_t i; /* the counter for the string */
for (i = 0; i < strlen(dStr); ++i)
{
if (dStr[i] == '%')
{
if (dStr[i + 1] == 0)
return dStr;
if (isxdigit(dStr[i + 1]) && isxdigit(dStr[i + 2]))
{
d = 0;
/* combine the next to numbers into one */
eStr[0] = dStr[i + 1];
eStr[1] = dStr[i + 2];
/* convert it to decimal */
long int x = strtol(eStr, NULL, 16);
/* remove the hex */
memmove(&dStr[i + 1], &dStr[i + 3], strlen(&dStr[i + 3]) + 1);
dStr[i] = x;
}
}
}
}
return dStr;
}
bool ESP_Mail_Client::handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession)
{
if (!reconnect(imap))
return false;
esp_mail_imap_response_data res(imap->client.available());
imap->_lastProgress = -1;
// Flag used for CRLF inclusion in response reading in case 8bit/binary attachment and base64 encoded and binary messages
bool withLineBreak = imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text && (cPart(imap)->xencoding == esp_mail_msg_xencoding_base64 || cPart(imap)->xencoding == esp_mail_msg_xencoding_binary);
withLineBreak |= imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment && cPart(imap)->xencoding != esp_mail_msg_xencoding_base64;
// custom cmd IDLE?, waiting incoming server response
if (res.chunkBufSize == 0 && imap->_prev_imap_custom_cmd == imap->_imap_custom_cmd && imap->_imap_custom_cmd == esp_mail_imap_cmd_idle)
{
if (!reconnect(imap))
return false;
return true;
}
while (imap->connected() && res.chunkBufSize <= 0)
{
if (!reconnect(imap, res.dataTime))
return false;
if (!connected<IMAPSession *>(imap))
{
#if defined(ESP32)
if (imap->_imap_cmd == esp_mail_imap_cmd_logout) // suppress the error due to server closes the connection immediately in ESP32 core v2.0.4
return true;
#endif
if (millis() - imap->_last_network_error_ms > 1000)
{
imap->_last_network_error_ms = millis();
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_CONNECTION_CLOSED, true);
}
return false;
}
res.chunkBufSize = imap->client.available();
yield_impl();
}
res.dataTime = millis();
if (res.chunkBufSize > 0)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_examine)
{
imap->_mbif.clear();
imap->_nextUID.clear();
imap->_unseenMsgIndex.clear();
}
if (imap->_imap_cmd == esp_mail_imap_cmd_search)
{
imap->_mbif._searchCount = 0;
imap->_imap_msg_num.clear();
}
// response buffer
res.chunkBufSize = ESP_MAIL_CLIENT_RESPONSE_BUFFER_SIZE;
res.response = allocMem<char *>(res.chunkBufSize + 1);
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
res.lastBuf = allocMem<char *>(BASE64_CHUNKED_LEN + 1);
while (!res.completedResponse) // looking for operation finishing
{
yield_impl();
if (imap->_imap_cmd == esp_mail_imap_cmd_append || (imap->_imap_custom_cmd == esp_mail_imap_cmd_append && imap->_imap_cmd == esp_mail_imap_cmd_custom && imap->_customCmdResCallback))
{
// No waiting time out for APPEND
res.dataTime = millis();
}
if (!reconnect(imap, res.dataTime) || !connected<IMAPSession *>(imap))
{
if (!connected<IMAPSession *>(imap))
{
if (cPart(imap) && cPart(imap)->file_open_write)
mbfs->close(mbfs_type imap->_imap_data->storage.type);
#if defined(ESP32)
if (imap->_imap_cmd == esp_mail_imap_cmd_logout) // suppress the error due to server closes the connection immediately in ESP32 core v2.0.4
return true;
#endif
if (millis() - imap->_last_network_error_ms > 1000)
{
imap->_last_network_error_ms = millis();
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_CONNECTION_CLOSED, true);
}
return false;
}
return false;
}
res.chunkBufSize = imap->client.available();
if (res.chunkBufSize > 0)
{
res.chunkBufSize = ESP_MAIL_CLIENT_RESPONSE_BUFFER_SIZE;
if (imap->_imap_cmd == esp_mail_imap_cmd_search)
{
res.readLen = parseSearchResponse(imap, res, esp_mail_imap_tag_str, imap_responses[esp_mail_imap_response_search].text);
imap->_mbif._availableItems = imap->_imap_msg_num.size();
if (imap->_mbif._availableItems == 0)
{
res.imapResp = imapResponseStatus(imap, res.response, esp_mail_imap_tag_str);
if (res.imapResp != esp_mail_imap_resp_unknown)
res.endSearch = true;
if (res.imapResp == esp_mail_imap_resp_bad)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, IMAP_STATUS_BAD_COMMAND, false);
return false;
}
}
}
else
{
// response read as chunk ended with CRLF or complete buffer size
int o = res.octetCount;
res.readLen = 0;
MB_String ovfBuf;
if (!readResponse<IMAPSession *>(imap, res.response, res.chunkBufSize, res.readLen, withLineBreak, res.octetCount, ovfBuf))
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_READ_TIMEOUT, true);
return false;
}
// If buffer overflown, copy from overflow buffer
if (ovfBuf.length() > 0)
{
// release memory
freeMem(&res.response);
res.response = allocMem<char *>(ovfBuf.length() + 1);
strcpy(res.response, ovfBuf.c_str());
ovfBuf.clear();
}
if (res.readLen == 0 && o != res.octetCount && res.octetCount <= res.octetLength && cPart(imap)->xencoding != esp_mail_msg_xencoding_base64 && cPart(imap)->xencoding != esp_mail_msg_xencoding_binary && cPart(imap)->xencoding != esp_mail_msg_xencoding_qp)
{
strcpy_P(res.response, esp_mail_str_42 /* "\r\n" */);
res.readLen = 2;
}
}
if (res.readLen)
{
if (imap->_debug && imap->_debugLevel > esp_mail_debug_level_basic && !imap->_customCmdResCallback)
{
if (imap->_imap_cmd != esp_mail_imap_cmd_search && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_text && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_attachment && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_inline)
esp_mail_debug_print((const char *)res.response, true);
}
if (imap->_imap_cmd != esp_mail_imap_cmd_search || (imap->_imap_cmd == esp_mail_imap_cmd_search && res.endSearch))
res.imapResp = imapResponseStatus(imap, res.response, esp_mail_imap_tag_str);
if (res.imapResp != esp_mail_imap_resp_unknown)
{
// We've got the right response,
// prepare to exit
res.completedResponse = true;
if (imap->_debug && imap->_debugLevel > esp_mail_debug_level_basic && !imap->_customCmdResCallback)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
esp_mail_debug_print((const char *)res.response, true);
}
MB_String ovfBuf;
while (imap->client.available())
{
if (!readResponse<IMAPSession *>(imap, res.response, res.chunkBufSize, res.readLen, true, res.octetCount, ovfBuf))
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, MAIL_CLIENT_ERROR_READ_TIMEOUT, true);
return false;
}
// If buffer overflown, copy from overflow buffer
if (ovfBuf.length() > 0)
{
// release memory
freeMem(&res.response);
res.response = allocMem<char *>(ovfBuf.length() + 1);
strcpy(res.response, ovfBuf.c_str());
ovfBuf.clear();
}
if (res.readLen)
{
if (imap->_debug && imap->_debugLevel > esp_mail_debug_level_basic && !imap->_customCmdResCallback)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
esp_mail_debug_print((const char *)res.response, true);
}
}
}
}
else
{
// No response ever parsed
if (imap->_imap_cmd == esp_mail_imap_cmd_sasl_auth_plain || imap->_imap_cmd == esp_mail_imap_cmd_sasl_auth_oauth)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_sasl_auth_oauth)
{
if (isOAuthError(res.response, res.readLen, res.chunkIdx, 2))
res.completedResponse = true;
}
// In case SASL-IR extension does not support, check for initial zero-length server challenge first "+ "
if (!imap->_auth_capability[esp_mail_auth_capability_sasl_ir] && strcmp(res.response, pgm2Str(esp_mail_str_63 /* "+ " */)) == 0)
{
res.imapResp = esp_mail_imap_resp_ok;
res.completedResponse = true;
}
}
if (imap->_imap_cmd == esp_mail_imap_cmd_sasl_login || imap->_imap_cmd == esp_mail_imap_cmd_sasl_auth_oauth || imap->_imap_cmd == esp_mail_imap_cmd_sasl_auth_plain)
{
int i = 0;
if (parseCapabilityResponse(imap, res.response, i))
imap->_feature_capability[esp_mail_imap_read_capability_auto_caps] = true;
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_custom && imap->_customCmdResCallback)
{
res.imapResp = imapResponseStatus(imap, res.response, imap->_responseStatus.tag.c_str());
// get response or custom cmd APPEND or custom cmd IDLE?
if (res.imapResp > esp_mail_imap_resp_unknown || strposP(imap->_cmd.c_str(), imap_commands[esp_mail_imap_command_append].text, 0, false) > -1 || imap->_imap_custom_cmd == esp_mail_imap_cmd_idle)
res.completedResponse = true;
imap->_responseStatus.text = res.response;
imap->_customCmdResCallback(imap->_responseStatus);
if (res.completedResponse)
return true;
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_append)
{
res.imapResp = esp_mail_imap_resp_ok;
res.completedResponse = true;
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_capability)
parseCapabilityResponse(imap, res.response, res.chunkIdx);
else if (imap->_imap_cmd == esp_mail_imap_cmd_list)
parseFoldersResponse(imap, res.response, true);
else if (imap->_imap_cmd == esp_mail_imap_cmd_lsub)
parseFoldersResponse(imap, res.response, false);
else if (imap->_imap_cmd == esp_mail_imap_cmd_select || imap->_imap_cmd == esp_mail_imap_cmd_examine)
parseExamineResponse(imap, res.response);
else if (imap->_imap_cmd == esp_mail_imap_cmd_get_uid)
{
MB_String str;
appendFetchString(str, true);
parseCmdResponse(imap, res.response, str.c_str());
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_get_flags)
{
MB_String str;
appendFetchString(str, false);
parseCmdResponse(imap, res.response, str.c_str());
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_get_quota)
parseCmdResponse(imap, res.response, imap_responses[esp_mail_imap_response_quota].text);
else if (imap->_imap_cmd == esp_mail_imap_cmd_id)
parseCmdResponse(imap, res.response, imap_responses[esp_mail_imap_response_id].text);
else if (imap->_imap_cmd == esp_mail_imap_cmd_get_quota_root)
{
parseCmdResponse(imap, res.response, imap_responses[esp_mail_imap_response_quotaroot].text);
imap->_quota_tmp.clear();
parseCmdResponse(imap, res.response, imap_responses[esp_mail_imap_response_quota].text);
if (imap->_quota_tmp.length() > 0)
{
imap->_quota_root_tmp += (char)':';
imap->_quota_root_tmp += imap->_quota_tmp;
}
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_get_acl || imap->_imap_cmd == esp_mail_imap_cmd_my_rights)
parseCmdResponse(imap, res.response, imap->_imap_cmd == esp_mail_imap_cmd_get_acl ? imap_responses[esp_mail_imap_response_acl].text : imap_responses[esp_mail_imap_response_myrights].text);
else if (imap->_imap_cmd == esp_mail_imap_cmd_namespace)
parseCmdResponse(imap, res.response, imap_responses[esp_mail_imap_response_namespace].text);
else if (imap->_imap_cmd == esp_mail_imap_cmd_idle)
{
res.completedResponse = res.response[0] == '+';
res.imapResp = esp_mail_imap_resp_ok;
imap->_last_host_check_ms = millis();
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_sequence_set)
{
MB_String str;
appendFetchString(str, true);
parseCmdResponse(imap, res.response, str.c_str());
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header)
{
if (res.headerState == 0 && cMSG(imap).type == esp_mail_imap_msg_num_type_uid)
res.header.message_uid = cMSG(imap).value;
int _st = res.headerState;
parseHeaderResponse(imap, res, imap->_imap_data->enable.header_case_sensitive);
if (_st == res.headerState && res.headerState > 0 && res.octetCount <= res.header.header_data_len)
collectHeaderField(imap, res.response, res.header, res.headerState);
}
else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime)
parsePartHeaderResponse(imap, res, imap->_imap_data->enable.header_case_sensitive);
else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text)
decodeText(imap, res);
else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
{
if (cPart(imap)->xencoding == esp_mail_msg_xencoding_base64)
{
// Multi-line chunked base64 string attachment handle
if (res.octetCount < res.octetLength && res.readLen < BASE64_CHUNKED_LEN)
{
if (strlen(res.lastBuf) > 0)
{
res.buf = allocMem<char *>(res.readLen + strlen(res.lastBuf) + 2);
strcpy(res.buf, res.lastBuf);
strcat(res.buf, res.response);
res.readLen = strlen(res.buf);
res.tmo = parseAttachmentResponse(imap, res.buf, res);
// release memory
freeMem(&res.buf);
memset(res.lastBuf, 0, BASE64_CHUNKED_LEN + 1);
if (!res.tmo)
break;
}
else if (res.readLen < BASE64_CHUNKED_LEN + 1)
strcpy(res.lastBuf, res.response);
}
else
{
res.tmo = parseAttachmentResponse(imap, res.response, res);
if (!res.tmo)
break;
}
}
else
res.tmo = parseAttachmentResponse(imap, res.response, res);
}
res.dataTime = millis();
}
}
memset(res.response, 0, res.chunkBufSize);
}
}
if (imap->_imap_cmd == esp_mail_imap_cmd_search)
{
if (imap->_debug && res.searchCount > 0 && res.searchCount < 100)
{
searchReport(imap, 100);
}
}
}
if ((res.imapResp != esp_mail_imap_resp_ok && imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header && res.header.header_data_len == 0) || res.imapResp == esp_mail_imap_resp_no)
{
// We don't get any response
if (res.imapResp == esp_mail_imap_resp_no)
imap->_responseStatus.errorCode = IMAP_STATUS_IMAP_RESPONSE_FAILED;
else if (imap->_imap_data->fetch.modsequence > -1 && imap->isModseqSupported() && res.imapResp == esp_mail_imap_resp_ok && res.header.header_data_len == 0)
imap->_responseStatus.errorCode = IMAP_STATUS_CHANGEDSINC_MODSEQ_TEST_FAILED;
else
imap->_responseStatus.errorCode = IMAP_STATUS_NO_MESSAGE;
#if !defined(SILENT_MODE)
if (imap->_statusCallback && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_mime)
sendErrorCB<IMAPSession *>(imap, imap->errorReason().c_str(), false, false);
if (imap->_debug && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_mime)
esp_mail_debug_print_tag(imap->errorReason().c_str(), esp_mail_debug_tag_type_error, true);
#endif
return false;
}
// We've got OK or NO responses
if (res.imapResp == esp_mail_imap_resp_ok)
{
// Response OK
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header)
{
// Headers management
char *buf = allocMem<char *>(res.header.content_type.length() + 1);
strcpy(buf, res.header.content_type.c_str());
res.header.content_type.clear();
MB_String contentTypeName;
appendHeaderName(contentTypeName, message_headers[esp_mail_message_header_field_content_type].text, false, false, false);
res.buf = subStr(buf, contentTypeName.c_str(), esp_mail_str_35 /* ";" */, 0, 0, false);
if (res.buf)
{
res.headerState = esp_mail_imap_state_content_type;
collectHeaderField(imap, res.buf, res.header, res.headerState);
// release memory
freeMem(&res.buf);
if (res.header.content_type.length() > 0)
{
int p1 = strposP(res.header.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0);
if (p1 != -1)
{
p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1;
res.header.multipart = true;
// inline or embedded images
if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_related;
// multiple text formats e.g. plain, html, enriched
else if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative;
// medias
else if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel;
// rfc822 encapsulated
else if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest;
else if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_report;
// others can be attachments
else if (strpos(res.header.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1)
res.header.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed;
}
p1 = strposP(res.header.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0);
if (p1 != -1)
{
p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1;
if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1)
{
res.header.rfc822_part = true;
res.header.message_sub_type = esp_mail_imap_message_sub_type_rfc822;
}
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1)
res.header.message_sub_type = esp_mail_imap_message_sub_type_partial;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1)
res.header.message_sub_type = esp_mail_imap_message_sub_type_external_body;
else if (strpos(res.part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1)
res.header.message_sub_type = esp_mail_imap_message_sub_type_delivery_status;
}
}
MB_String charset;
appendLowerCaseString(charset, message_headers[esp_mail_message_header_field_charset].text, false);
charset += esp_mail_str_7; /* "=" */
res.buf = subStr(buf, charset.c_str(), NULL, 0, -1, false);
if (res.buf)
{
res.headerState = esp_mail_imap_state_char_set;
collectHeaderField(imap, res.buf, res.header, res.headerState);
// release memory
freeMem(&res.buf);
}
if (res.header.multipart)
{
if (strcmpP(buf, 0, esp_mail_str_64 /* "boundary=\"" */))
{
res.buf = subStr(buf, esp_mail_str_64 /* "boundary=\"" */, esp_mail_str_11 /* "\"" */, 0, 0, false);
if (res.buf)
{
res.headerState = esp_mail_imap_state_boundary;
collectHeaderField(imap, res.buf, res.header, res.headerState);
// release memory
freeMem(&res.buf);
}
}
}
}
// release memory
freeMem(&buf);
// Decode the headers fields
for (int i = esp_mail_rfc822_header_field_from; i < esp_mail_rfc822_header_field_maxType; i++)
{
if (i != esp_mail_rfc822_header_field_msg_id && i != esp_mail_rfc822_header_field_flags)
decodeString(imap, res.header.header_fields.header_items[i]);
}
imap->_headers.push_back(res.header);
}
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime)
{
// Expect the octet length in the response for the existent part
if (res.part.octetLen > 0)
{
res.part.partNumStr = cHeader(imap)->partNumStr;
res.part.partNumFetchStr = cHeader(imap)->partNumStr;
if (cHeader(imap)->part_headers.size() > 0)
{
struct esp_mail_message_part_info_t *_part = &cHeader(imap)->part_headers[cHeader(imap)->part_headers.size() - 1];
bool rfc822_body_subtype = _part->message_sub_type == esp_mail_imap_message_sub_type_rfc822;
if (rfc822_body_subtype)
{
if (!_part->rfc822_part)
{
// additional rfc822 message header, store it to the rfc822 part header
_part->rfc822_part = true;
_part->rfc822_header = res.part.rfc822_header;
imap->_rfc822_part_count++;
_part->rfc822_msg_Idx = imap->_rfc822_part_count;
}
}
}
cHeader(imap)->part_headers.push_back(res.part);
cHeader(imap)->message_data_count = cHeader(imap)->part_headers.size();
if (res.part.msg_type != esp_mail_msg_type_none ||
res.part.attach_type != esp_mail_att_type_none)
{
if (res.part.attach_type == esp_mail_att_type_attachment ||
res.part.message_sub_type != esp_mail_imap_message_sub_type_rfc822)
{
if (res.part.attach_type != esp_mail_att_type_none &&
cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative)
{
cHeader(imap)->hasAttachment = true;
cHeader(imap)->attachment_count++;
}
}
}
}
else
{
// nonexistent part
// return false to exit the loop without closing the connection
if (closeSession)
imap->closeSession();
return false;
}
}
if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
{
if (cPart(imap) && cPart(imap)->file_open_write)
mbfs->close(mbfs_type imap->_imap_data->storage.type);
}
if (cPart(imap) && imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text)
cPart(imap)->text[cPart(imap)->textLen] = 0;
}
else
{
// Response NO
// Some server responses NO and should exit (false) from MIME feching loop without
// closing the session
if (imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_mime)
return handleIMAPError(imap, errCode, false);
if (closeSession)
imap->closeSession();
return false;
}
return true;
}
void ESP_Mail_Client::addHeader(MB_String &s, PGM_P name, const char *s_value, int num_value, bool trim, bool isJson)
{
if (isJson)
{
s += (s.length() > 0) ? esp_mail_str_77 /* ",\"" */ : esp_mail_str_78 /* "{\"" */;
s += name;
if (strlen(s_value) > 0)
{
s += esp_mail_str_79; /* "\":\"" */
;
if (trim)
{
MB_String t = s_value;
t.replaceAll("\"", "");
s += t;
}
else
s += s_value;
s += esp_mail_str_11; /* "\"" */
}
else
{
s += esp_mail_str_34 /* ":" */;
appendSpace(s);
s += num_value;
}
}
else
{
if (s.length() > 0)
appendNewline(s);
s += name;
s += esp_mail_str_34 /* ":" */;
appendSpace(s);
if (strlen(s_value) > 0)
s += s_value;
else
s += num_value;
}
}
void ESP_Mail_Client::saveHeader(IMAPSession *imap, bool json)
{
if (!imap->_storageReady)
return;
MB_String headerFilePath;
prepareFilePath(imap, headerFilePath, true);
headerFilePath += json ? esp_mail_str_65 /* "/header.json" */ : esp_mail_str_66 /* "/header.txt" */;
prepareFileList(imap, headerFilePath);
int sz = mbfs->open(headerFilePath, mbfs_type imap->_imap_data->storage.type, mb_fs_open_mode_write);
if (sz < 0)
{
imap->_responseStatus.errorCode = sz;
imap->_responseStatus.text.clear();
#if !defined(SILENT_MODE)
if (imap->_debug)
{
esp_mail_debug_print_tag(imap->errorReason().c_str(), esp_mail_debug_tag_type_error, true);
}
#endif
return;
}
MB_String s;
for (size_t i = 0; i < imap->_headers.size(); i++)
addHeaderItem(s, &imap->_headers[i], json);
if (json)
s += esp_mail_str_75; /* "]}" */
mbfs->print(mbfs_type imap->_imap_data->storage.type, s.c_str());
mbfs->close(mbfs_type imap->_imap_data->storage.type);
imap->_headerSaved = true;
}
void ESP_Mail_Client::addHeaderItem(MB_String &str, esp_mail_message_header_t *header, bool json)
{
MB_String s;
if (json)
{
if (str.length() > 0)
str += esp_mail_str_8; /* "," */
else
str = esp_mail_str_76; /* "{\"Messages\":[" */
}
addHeader(s, message_headers[esp_mail_message_header_field_number].text, "", header->message_no, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_uid].text, "", header->message_uid, false, json);
if (header->accept_language.length() > 0)
addHeader(s, message_headers[esp_mail_message_header_field_accept_language].text, header->accept_language.c_str(), 0, false, json);
if (header->content_language.length() > 0)
addHeader(s, message_headers[esp_mail_message_header_field_content_language].text, header->content_language.c_str(), 0, false, json);
addRFC822Headers(s, &header->header_fields, json);
for (size_t j = 0; j < header->part_headers.size(); j++)
{
if (header->part_headers[j].rfc822_part)
{
MB_String s1;
addRFC822Headers(s1, &header->part_headers[j].rfc822_header, json);
if (json)
{
s += esp_mail_str_69; /* ",\"RFC822\":" */
s += s1;
s += esp_mail_str_36; /* "}" */
}
else
{
s += esp_mail_str_70; /* "\r\n\r\nRFC822:\r\n" */
s += s1;
}
}
}
if (header->attachment_count > 0)
{
if (json)
{
s += esp_mail_str_71 /* ",\"Attachments\":{\"Count\":" */;
s += header->attachment_count;
s += esp_mail_str_72; /* ",\"Files\":[" */
}
else
{
s += esp_mail_str_73; /* "\r\n\r\nAttachments (" */
s += header->attachment_count;
s += esp_mail_str_74; /* ")\r\n" */
}
int index = 0;
for (size_t j = 0; j < header->part_headers.size(); j++)
{
if (header->part_headers[j].attach_type == esp_mail_att_type_none || header->part_headers[j].rfc822_part)
continue;
if (json)
{
if (index > 0)
s += esp_mail_str_8; /* "," */
s += esp_mail_str_67; /* /"{\"Filename\":\"" */
s += header->part_headers[j].filename;
s += esp_mail_str_11; /* "\"" */
}
else
{
if (index > 0)
appendNewline(s);
appendNewline(s);
s += esp_mail_str_68; /* /"Index: " */
s += index + 1;
addHeader(s, message_headers[esp_mail_message_header_field_filename].text, header->part_headers[j].filename.c_str(), 0, false, json);
}
addHeader(s, message_headers[esp_mail_message_header_field_name].text, header->part_headers[j].name.c_str(), 0, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_size].text, "", header->part_headers[j].attach_data_size, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_mime].text, header->part_headers[j].content_type.c_str(), 0, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_type].text, header->part_headers[j].attach_type == esp_mail_att_type_attachment ? esp_mail_content_disposition_type_t::attachment : esp_mail_content_disposition_type_t::inline_, 0, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_description].text, header->part_headers[j].content_description.c_str(), 0, false, json);
addHeader(s, message_headers[esp_mail_message_header_field_creation_date].text, header->part_headers[j].creation_date.c_str(), 0, false, json);
if (json)
s += esp_mail_str_36; /* "}" */
index++;
}
if (json)
s += esp_mail_str_75; /* "]}" */
}
if (json)
{
s += esp_mail_str_36; /* "}" */
}
str += s;
}
int ESP_Mail_Client::getRFC822HeaderPtr(int index, esp_mail_imap_rfc822_msg_header_item_t *header)
{
if (index > esp_mail_rfc822_header_field_from && index < esp_mail_rfc822_header_field_maxType)
return toAddr(header->header_items[index]);
return 0;
}
void ESP_Mail_Client::addRFC822Headers(MB_String &s, esp_mail_imap_rfc822_msg_header_item_t *header, bool json)
{
for (int i = esp_mail_rfc822_header_field_from; i < esp_mail_rfc822_header_field_maxType; i++)
addRFC822HeaderItem(s, header, i, json);
}
void ESP_Mail_Client::addRFC822HeaderItem(MB_String &s, esp_mail_imap_rfc822_msg_header_item_t *header, int index, bool json)
{
int ptr = getRFC822HeaderPtr(index, header);
if (ptr > 0)
addHeader(s, rfc822_headers[index].text, addrTo<MB_String *>(ptr)->c_str(), 0, rfc822_headers[index].trim, json);
}
esp_mail_imap_response_status ESP_Mail_Client::imapResponseStatus(IMAPSession *imap, char *response, PGM_P tag)
{
imap->_responseStatus.clear(false);
MB_String test;
esp_mail_imap_response_status status = esp_mail_imap_resp_unknown;
esp_mail_imap_response_types type = esp_mail_imap_response_maxType;
if (strpos(response, imap->prependTag(imap_responses[esp_mail_imap_response_ok].text, tag).c_str(), 0) > -1)
{
status = esp_mail_imap_resp_ok;
type = esp_mail_imap_response_ok;
}
else if (strpos(response, imap->prependTag(imap_responses[esp_mail_imap_response_no].text, tag).c_str(), 0) > -1)
{
status = esp_mail_imap_resp_no;
type = esp_mail_imap_response_no;
}
else if (strpos(response, imap->prependTag(imap_responses[esp_mail_imap_response_bad].text, tag).c_str(), 0) > -1)
{
status = esp_mail_imap_resp_bad;
type = esp_mail_imap_response_bad;
}
if (status != esp_mail_imap_resp_unknown)
{
test = imap->prependTag(imap_responses[type].text, tag);
imap->_responseStatus.text = &response[test.length()];
if (imap->_responseStatus.text[imap->_responseStatus.text.length() - 2] == '\r')
imap->_responseStatus.text[imap->_responseStatus.text.length() - 2] = 0;
test = imap_responses[type].text;
test.trim();
imap->_responseStatus.status = test;
imap->_responseStatus.completed = true;
}
return status;
}
bool ESP_Mail_Client::parseCapabilityResponse(IMAPSession *imap, const char *buf, int &chunkIdx)
{
if (chunkIdx == 0)
{
MB_String res;
// We add white space to make post token checking to work in all capabilities.
// This will allow us to check "IDLE " and "ID " correctly.
appendSpace(res, false, buf);
if (strposP(res.c_str(), imap_responses[esp_mail_imap_response_capability_untagged].text, 0) > -1 || strposP(res.c_str(), imap_responses[esp_mail_imap_response_capability].text, 0) > -1)
{
for (int i = esp_mail_auth_capability_plain; i < esp_mail_auth_capability_maxType; i++)
{
if (strposP(res.c_str(), imap_auth_cap_pre_tokens[i].c_str(), 0) > -1)
imap->_auth_capability[i] = true;
}
for (int i = esp_mail_imap_read_capability_imap4; i < esp_mail_imap_read_capability_maxType; i++)
{
if (strposP(res.c_str(), imap_read_cap_post_tokens[i].c_str(), 0) > -1)
{
imap->_feature_capability[i] = true;
if (i == esp_mail_imap_read_capability_logindisable)
imap->_auth_capability[esp_mail_auth_capability_login] = false;
}
}
return true;
}
}
return false;
}
char *ESP_Mail_Client::getList(char *buf, bool &isList)
{
if (buf[0] == '(' && buf[1] != ')')
{
if (buf[strlen(buf) - 1] == ')')
buf[strlen(buf) - 1] = 0;
else
isList = true;
return &buf[1];
}
else if (isList)
{
if (buf[strlen(buf) - 1] == ')')
{
buf[strlen(buf) - 1] = 0;
isList = false;
}
}
return buf;
}
void ESP_Mail_Client::parseFoldersResponse(IMAPSession *imap, char *buf, bool list)
{
struct esp_mail_folder_info_t fd;
int pos = list ? strposP(buf, imap_responses[esp_mail_imap_response_list].text, 0) : strposP(buf, imap_responses[esp_mail_imap_response_lsub].text, 0);
bool isList = false, delimOk = false;
if (pos != -1)
{
char *p = allocMem<char *>(strlen(buf));
strcpy(p, buf);
char *pp = p;
char *end = p;
int count = 0;
int tkPos = 3;
while (pp != NULL)
{
// See RFC2047.h
ESP_MAIL_STRSEP(&end, " ");
count++;
if (count >= tkPos && strlen(pp) > 0)
{
if (count == tkPos && pp[0] == '(' && pp[1] != ')')
fd.attributes = getList(pp, isList);
else if (isList)
{
fd.attributes += ' ';
fd.attributes += getList(pp, isList);
}
else
{
if (pp[strlen(pp) - 1] == '"')
pp[strlen(pp) - 1] = 0;
const char *ptr = pp[0] == '"' ? &pp[1] : &pp[0];
if (!delimOk)
{
delimOk = true;
fd.delimiter = ptr;
}
else
{
if (fd.name.length() > 0)
fd.name += ' ';
fd.name += ptr;
}
}
}
pp = end;
}
// release memory
freeMem(&p);
imap->_folders.add(fd);
}
}
bool ESP_Mail_Client::parseIdleResponse(IMAPSession *imap)
{
int chunkBufSize = 0;
if (!reconnect(imap))
return false;
if (imap->client.connected())
chunkBufSize = imap->client.available();
else
return false;
if (chunkBufSize > 0)
{
chunkBufSize = ESP_MAIL_CLIENT_RESPONSE_BUFFER_SIZE;
char *buf = allocMem<char *>(chunkBufSize + 1);
int octetCount = 0;
int readLen = 0;
MB_String ovfBuf;
readResponse<IMAPSession *>(imap, buf, chunkBufSize, readLen, false, octetCount, ovfBuf);
// If buffer overflown, copy from overflow buffer
if (ovfBuf.length() > 0)
{
// release memory
freeMem(&buf);
buf = allocMem<char *>(ovfBuf.length() + 1);
strcpy(buf, ovfBuf.c_str());
ovfBuf.clear();
}
if (readLen > 0)
{
if (imap->_debug && imap->_debugLevel > esp_mail_debug_level_basic)
esp_mail_debug_print((const char *)buf, true);
char *tmp = nullptr;
int p1 = -1;
bool exists = false;
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_exists].text, 0);
if (tmp)
{
int numMsg = imap->_mbif._msgCount;
imap->_mbif._msgCount = atoi(tmp);
// release memory
freeMem(&tmp);
exists = true;
imap->_mbif._folderChanged |= (int)imap->_mbif._msgCount != numMsg;
if ((int)imap->_mbif._msgCount > numMsg)
{
imap->_mbif._polling_status.type = imap_polling_status_type_new_message;
imap->_mbif._polling_status.messageNum = imap->_mbif._msgCount;
}
goto ex;
}
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_expunge].text, 0);
if (tmp)
{
imap->_mbif._polling_status.type = imap_polling_status_type_remove_message;
imap->_mbif._polling_status.messageNum = atoi(tmp);
if (imap->_mbif._polling_status.messageNum == imap->_mbif._msgCount && imap->_mbif._nextUID > 0)
imap->_mbif._nextUID--;
// release memory
freeMem(&tmp);
imap->_mbif._folderChanged = true;
goto ex;
}
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_recent].text, 0);
if (tmp)
{
imap->_mbif._recentCount = atoi(tmp);
// release memory
freeMem(&tmp);
goto ex;
}
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_fetch].text, 0);
if (tmp)
{
imap->_mbif._polling_status.messageNum = atoi(tmp);
// release memory
freeMem(&tmp);
imap->_mbif._polling_status.argument = buf;
imap->_mbif._polling_status.argument.erase(0, p1 + 8);
imap->_mbif._polling_status.argument.pop_back();
imap->_mbif._folderChanged = true;
imap->_mbif._polling_status.type = imap_polling_status_type_fetch_message;
goto ex;
}
ex:
imap->_mbif._floderChangedState = (imap->_mbif._folderChanged && exists) || imap->_mbif._polling_status.type == imap_polling_status_type_fetch_message;
}
// release memory
freeMem(&buf);
}
size_t imap_idle_tmo = imap->_imap_data->limit.imap_idle_timeout;
if (imap_idle_tmo < 60 * 1000 || imap_idle_tmo > 29 * 60 * 1000)
imap_idle_tmo = 10 * 60 * 1000;
if (millis() - imap->_mbif._idleTimeMs > imap_idle_tmo)
{
if (imap->mStopListen(true))
return imap->mListen(true);
return false;
}
return true;
}
void ESP_Mail_Client::appendFetchString(MB_String &buf, bool uid)
{
if (uid)
buf += imap_cmd_post_tokens[esp_mail_imap_command_uid];
else
joinStringSpace(buf, false, 2, imap_commands[esp_mail_imap_command_flags].text, esp_mail_str_38 /* "(" */);
}
void ESP_Mail_Client::parseCmdResponse(IMAPSession *imap, char *buf, PGM_P find)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_get_uid)
imap->_uid_tmp = 0;
char *tmp = nullptr;
int p1 = strposP(buf, find, 0);
if (p1 != -1)
{
if (imap->_imap_cmd == esp_mail_imap_cmd_get_quota_root ||
imap->_imap_cmd == esp_mail_imap_cmd_get_acl ||
imap->_imap_cmd == esp_mail_imap_cmd_my_rights)
{
int ofs = imap->_imap_cmd == esp_mail_imap_cmd_get_quota_root ? 0 : 1;
int p2 = strposP(buf, esp_mail_str_2 /* " " */, p1 + strlen_P(find) + ofs);
if (p2 != -1)
{
int len = strlen(buf) - p2 - 1;
tmp = allocMem<char *>(len);
strncpy(tmp, buf + p2 + 1, strlen(buf) - p2 - 1);
if (imap->_imap_cmd == esp_mail_imap_cmd_get_quota_root)
{
if (imap->_quota_root_tmp.length() > 0)
imap->_quota_root_tmp += esp_mail_str_8; /* ";" */
imap->_quota_root_tmp += tmp;
}
else
{
imap->_acl_tmp = tmp;
}
// release memory
freeMem(&tmp);
}
}
else
{
int len = imap->_imap_cmd == esp_mail_imap_cmd_get_uid ? 20 : strlen(buf) - p1 - strlen_P(find);
int ofs = imap->_imap_cmd == esp_mail_imap_cmd_get_uid ||
imap->_imap_cmd == esp_mail_imap_cmd_fetch_sequence_set
? 1
: imap->_imap_cmd == esp_mail_imap_cmd_namespace || imap->_imap_cmd == esp_mail_imap_cmd_id ? 0
: 2;
tmp = allocMem<char *>(len);
strncpy(tmp, buf + p1 + strlen_P(find), strlen(buf) - p1 - strlen_P(find) - ofs);
esp_mail_imap_msg_num_t msg_num;
switch ((int)imap->_imap_cmd)
{
case esp_mail_imap_cmd_get_uid:
imap->_uid_tmp = atoi(tmp);
break;
case esp_mail_imap_cmd_get_flags:
imap->_flags_tmp = tmp;
break;
case esp_mail_imap_cmd_get_quota:
imap->_quota_tmp = tmp;
break;
case esp_mail_imap_cmd_id:
imap->_server_id_tmp = tmp;
break;
case esp_mail_imap_cmd_namespace:
imap->_ns_tmp += tmp;
break;
case esp_mail_imap_cmd_fetch_sequence_set:
msg_num.type = esp_mail_imap_msg_num_type_uid;
msg_num.value = (uint32_t)atoi(tmp);
imap->_imap_msg_num.push_back(msg_num);
if (imap->_imap_msg_num.size() > imap->_imap_data->limit.fetch)
imap->_imap_msg_num.erase(imap->_imap_msg_num.begin());
break;
default:
break;
}
}
// release memory
freeMem(&tmp);
}
}
bool ESP_Mail_Client::getFlags(IMAPSession *imap, char *buf, esp_mail_imap_response_types type)
{
if (strposP(buf, imap_responses[type].text, 0) != -1)
{
char *p = allocMem<char *>(strlen(buf));
strcpy(p, buf);
char *pp = p;
char *end = p;
int count = 0;
bool isList = false;
int tkPos = (type == esp_mail_imap_response_permanent_flags) ? 4 : 3;
while (pp != NULL)
{
// See RFC2047.h
ESP_MAIL_STRSEP(&end, " ");
count++;
if (count >= tkPos && strlen(pp) > 0)
{
if (type == esp_mail_imap_response_permanent_flags && pp[strlen(pp) - 1] == ']')
pp[strlen(pp) - 1] = 0;
if (count == tkPos && pp[0] == '(' && pp[1] != ')')
imap->_mbif.addFlag(getList(pp, isList), type == esp_mail_imap_response_permanent_flags);
else if (isList)
imap->_mbif.addFlag(getList(pp, isList), type == esp_mail_imap_response_permanent_flags);
}
pp = end;
}
// release memory
freeMem(&p);
return true;
}
return false;
}
void ESP_Mail_Client::parseExamineResponse(IMAPSession *imap, char *buf)
{
char *tmp = NULL;
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_exists].text, 0);
if (tmp)
{
imap->_mbif._msgCount = atoi(tmp);
// release memory
freeMem(&tmp);
return;
}
tmp = subStr(buf, NULL, imap_responses[esp_mail_imap_response_recent].text, 0);
if (tmp)
{
imap->_mbif._recentCount = atoi(tmp);
// release memory
freeMem(&tmp);
return;
}
if (imap->_mbif._flags.size() == 0 && getFlags(imap, buf, esp_mail_imap_response_flags))
return;
if (imap->_mbif._permanent_flags.size() == 0 && getFlags(imap, buf, esp_mail_imap_response_permanent_flags))
return;
if (imap->_mbif._uidValidity == 0)
{
tmp = subStr(buf, imap_responses[esp_mail_imap_response_uidvalidity].text, esp_mail_str_41 /* "]" */, 0, 0);
if (tmp)
{
imap->_mbif._uidValidity = atoi(tmp);
// release memory
freeMem(&tmp);
return;
}
}
if (imap->_nextUID.length() == 0)
{
tmp = subStr(buf, imap_responses[esp_mail_imap_response_uidnext].text, esp_mail_str_41 /* "]" */, 0, 0);
if (tmp)
{
imap->_nextUID = tmp;
imap->_mbif._nextUID = atoi(tmp);
// release memory
freeMem(&tmp);
return;
}
}
if (imap->_unseenMsgIndex.length() == 0)
{
tmp = subStr(buf, imap_responses[esp_mail_imap_response_unseen].text, esp_mail_str_41 /* "]" */, 0, 0);
if (tmp)
{
imap->_unseenMsgIndex = tmp;
imap->_mbif._unseenMsgIndex = atoi(tmp);
// release memory
freeMem(&tmp);
return;
}
}
if (imap->isCondStoreSupported())
{
tmp = subStr(buf, imap_responses[esp_mail_imap_response_highest_modsec].text, esp_mail_str_41 /* "]" */, 0, 0);
if (tmp)
{
imap->_mbif._highestModSeq = tmp;
// release memory
freeMem(&tmp);
return;
}
tmp = subStr(buf, imap_responses[esp_mail_imap_response_nomodsec].text, nullptr, 0, 0);
if (tmp)
{
imap->_mbif._nomodsec = true;
// release memory
freeMem(&tmp);
return;
}
}
}
bool ESP_Mail_Client::handleIMAPError(IMAPSession *imap, int err, bool ret)
{
if (err < 0)
{
errorStatusCB<IMAPSession *, IMAPSession *>(imap, nullptr, err, true);
if (imap->_headers.size() > 0 && cHeader(imap))
{
if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) && (imap->_imap_data->download.attachment || imap->_imap_data->download.inlineImg))
{
if (cPart(imap) && cHeader(imap)->part_headers.size() > 0)
cPart(imap)->download_error = imap->errorReason().c_str();
}
else if (cHeader(imap))
cHeader(imap)->error_msg = imap->errorReason().c_str();
cHeader(imap)->error = true;
}
}
closeTCPSession<IMAPSession *>(imap);
imap->_cbData.empty();
return ret;
}
void ESP_Mail_Client::prepareFileList(IMAPSession *imap, MB_String &filePath)
{
#if defined(MBFS_SD_FS)
if (!mbfs->longNameSupported())
{
cHeader(imap)->sd_alias_file_count++;
MB_String alias = cHeader(imap)->message_uid;
alias += esp_mail_str_83; /* "_" */
alias += cHeader(imap)->sd_alias_file_count;
if (imap->_sdFileList.length() > 0)
{
if (imap->_sdFileList[imap->_sdFileList.length() - 1] == ']')
{
imap->_sdFileList[imap->_sdFileList.length() - 1] = 0;
imap->_sdFileList += esp_mail_str_8; /* "," */
}
}
imap->_sdFileList += esp_mail_str_80; /* "{\"Renamed\":\"" */
imap->_sdFileList += alias;
imap->_sdFileList += esp_mail_str_81; /* "\",\"Original\":\"" */
imap->_sdFileList += filePath;
imap->_sdFileList += esp_mail_str_82; /* "\"}]" */
// rename the original file
filePath = alias;
}
#endif
}
bool ESP_Mail_Client::parseAttachmentResponse(IMAPSession *imap, char *buf, esp_mail_imap_response_data &res)
{
int bufLen = res.readLen;
if (res.chunkIdx == 0)
{
char *tmp = subStr(buf, esp_mail_str_36 /* "{" */, esp_mail_str_37 /* "}" */, 0);
if (tmp)
{
res.chunkIdx++;
res.octetCount = 0; // CRLF counted from first line
res.octetLength = atoi(tmp);
// release memory
freeMem(&tmp);
cPart(imap)->octetLen = res.octetLength;
cPart(imap)->octetCount = 0;
cHeader(imap)->total_download_size += res.octetLength;
imap->_lastProgress = -1;
#if defined(ESP_MAIL_OTA_UPDATE_ENABLED)
if (cPart(imap)->is_firmware_file)
{
cPart(imap)->is_firmware_file = Update.begin(cPart(imap)->attach_data_size);
if (!cPart(imap)->is_firmware_file)
{
imap->_responseStatus.errorCode = IMAP_STATUS_FIRMWARE_UPDATE_INIT_FAILED;
imap->_responseStatus.text.clear();
}
#if !defined(SILENT_MODE)
sendCallback<IMAPSession *>(imap, esp_mail_cb_str_42 /* "Updating firmware..." */, true, false);
if (!cPart(imap)->is_firmware_file)
{
printDebug<IMAPSession *>(imap,
imap->errorReason().c_str(),
imap->errorReason().c_str(),
esp_mail_debug_tag_type_error,
true,
false);
}
#endif
}
#endif
if (!cPart(imap)->file_open_write)
{
if (imap->_storageReady && cPart(imap)->save_to_file)
{
res.downloadRequest = true;
res.filePath.clear();
res.filePath += imap->_imap_data->storage.saved_path;
res.filePath += esp_mail_str_10; /* "/" */
res.filePath += cHeader(imap)->message_uid;
res.filePath += esp_mail_str_10; /* "/" */
res.filePath += cPart(imap)->filename;
prepareFileList(imap, res.filePath);
int sz = mbfs->open(res.filePath, mbfs_type imap->_imap_data->storage.type, mb_fs_open_mode_write);
if (sz < 0)
{
imap->_responseStatus.errorCode = sz;
imap->_responseStatus.text.clear();
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
imap->errorReason().c_str(),
imap->errorReason().c_str(),
esp_mail_debug_tag_type_error,
true,
false);
#endif
}
cPart(imap)->file_open_write = true;
}
}
}
return true;
}
yield_impl();
if (res.octetLength == 0)
return true;
if (cPart(imap)->octetCount <= res.octetLength)
{
if (cPart(imap)->octetCount + bufLen > res.octetLength)
{
bufLen = res.octetLength - cPart(imap)->octetCount;
buf[bufLen] = 0;
cPart(imap)->octetCount += bufLen;
}
else
cPart(imap)->octetCount = res.octetCount;
if (imap->_imap_data->enable.download_status)
{
if (imap->_debug)
downloadReport(imap, 100 * cPart(imap)->octetCount / res.octetLength);
}
if (cPart(imap)->octetCount > res.octetLength)
return true;
bool write_error = false, fw_write_error = false;
if (cPart(imap)->xencoding == esp_mail_msg_xencoding_base64)
{
size_t olen = 0;
unsigned char *decoded = decodeBase64((const unsigned char *)buf, bufLen, &olen);
if (decoded)
{
if (!cPart(imap)->sizeProp)
{
cPart(imap)->attach_data_size += olen;
cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size;
}
sendStreamCB(imap, (void *)decoded, olen, res.chunkIdx, false);
size_t write = olen;
if (cPart(imap)->is_firmware_file)
{
#if defined(ESP_MAIL_OTA_UPDATE_ENABLED)
size_t fw_write = Update.write((uint8_t *)decoded, olen);
cPart(imap)->firmware_downloaded_byte += fw_write == olen ? olen : 0;
fw_write_error = fw_write != olen;
#endif
}
if (cPart(imap)->save_to_file)
{
if (mbfs->ready(mbfs_type imap->_imap_data->storage.type))
write = mbfs->write(mbfs_type imap->_imap_data->storage.type, (uint8_t *)decoded, olen);
}
yield_impl();
// release memory
freeMem(&decoded);
write_error = write != olen;
}
if (!reconnect(imap))
return false;
}
else
{
// binary content
if (!cPart(imap)->sizeProp)
{
cPart(imap)->attach_data_size += bufLen;
cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size;
}
sendStreamCB(imap, (void *)buf, bufLen, res.chunkIdx, false);
int write = bufLen;
if (cPart(imap)->is_firmware_file)
{
#if defined(ESP_MAIL_OTA_UPDATE_ENABLED)
int fw_write = Update.write((uint8_t *)buf, bufLen);
cPart(imap)->firmware_downloaded_byte += fw_write == bufLen ? (size_t)bufLen : 0;
fw_write_error = fw_write != bufLen;
#endif
}
if (cPart(imap)->save_to_file)
{
if (mbfs->ready(mbfs_type imap->_imap_data->storage.type))
write = mbfs->write(mbfs_type imap->_imap_data->storage.type, (uint8_t *)buf, bufLen);
}
yield_impl();
write_error = write != bufLen;
if (!reconnect(imap))
return false;
}
#if defined(ESP_MAIL_OTA_UPDATE_ENABLED)
if (cPart(imap)->is_firmware_file)
{
bool update_result_ok = !fw_write_error;
if (!imap->_isFirmwareUpdated && update_result_ok &&
(cPart(imap)->firmware_downloaded_byte == (size_t)cPart(imap)->attach_data_size || cPart(imap)->octetCount >= res.octetLength))
{
update_result_ok = Update.end(cPart(imap)->octetCount >= res.octetLength);
if (update_result_ok)
imap->_isFirmwareUpdated = true;
}
if (!update_result_ok)
{
cPart(imap)->is_firmware_file = false;
imap->_responseStatus.errorCode = fw_write_error ? IMAP_STATUS_FIRMWARE_UPDATE_WRITE_FAILED : IMAP_STATUS_FIRMWARE_UPDATE_END_FAILED;
imap->_responseStatus.text.clear();
#if !defined(SILENT_MODE)
printDebug<IMAPSession *>(imap,
imap->_responseStatus.text.c_str(),
imap->errorReason().c_str(),
esp_mail_debug_tag_type_error,
!imap->_debug,
false);
#endif
}
}
#endif
if (write_error || fw_write_error)
return false;
}
res.chunkIdx++;
return true;
}
void ESP_Mail_Client::downloadReport(IMAPSession *imap, int progress)
{
printProgress(progress, imap->_lastProgress);
}
void ESP_Mail_Client::fetchReport(IMAPSession *imap, int progress, bool download)
{
#if !defined(SILENT_MODE)
if (imap->_debug && imap->_lastProgress == -1 && strcmp_P(cPart(imap)->filename.c_str(), esp_mail_str_84 /* "message" */) != 0)
esp_mail_debug_print_tag(cPart(imap)->filename.c_str(), esp_mail_debug_tag_type_client, true);
printProgress(progress, imap->_lastProgress);
#endif
}
void ESP_Mail_Client::searchReport(IMAPSession *imap, int progress)
{
printProgress(progress, imap->_lastProgress);
}
struct esp_mail_imap_msg_num_t ESP_Mail_Client::cMSG(IMAPSession *imap)
{
return imap->_imap_msg_num[cIdx(imap)];
}
int ESP_Mail_Client::cIdx(IMAPSession *imap)
{
return imap->_cMsgIdx;
}
void ESP_Mail_Client::decodeText(IMAPSession *imap, esp_mail_imap_response_data &res)
{
int bufLen = res.readLen;
bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822;
if (res.chunkIdx == 0)
{
imap->_lastProgress = -1;
char *tmp = subStr(res.response, esp_mail_str_36 /* "{" */, esp_mail_str_37 /* "}" */, 0);
if (tmp)
{
res.chunkIdx++;
res.octetCount = 0;
res.octetLength = atoi(tmp);
// release memory
freeMem(&tmp);
cPart(imap)->octetLen = res.octetLength;
cPart(imap)->octetCount = 0;
bool dlMsg = (rfc822_body_subtype && imap->_imap_data->download.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->download.text)));
if (dlMsg)
prepareFilePath(imap, res.filePath, false);
if (res.filePath.length() == 0)
{
if (!rfc822_body_subtype)
res.filePath += esp_mail_str_84; /* "message" */
else
joinStringSpace(res.filePath, false, 2, esp_mail_str_85 /* "rfc822" */, esp_mail_str_84 /* "message" */);
}
cPart(imap)->filename = res.filePath;
if (imap->_storageReady && !cPart(imap)->file_open_write && dlMsg)
{
prepareFileList(imap, res.filePath);
int sz = mbfs->open(res.filePath, mbfs_type imap->_imap_data->storage.type, mb_fs_open_mode_write);
if (sz > -1)
{
res.downloadRequest = true;
cPart(imap)->file_open_write = true;
}
else
{
imap->_responseStatus.errorCode = sz;
imap->_responseStatus.text.clear();
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(imap->errorReason().c_str(), esp_mail_debug_tag_type_error, true);
#endif
}
}
return;
}
else
{
#if !defined(SILENT_MODE)
if (imap->_debug)
esp_mail_debug_print_tag(esp_mail_error_imap_str_12 /* "no centent" */, esp_mail_debug_tag_type_client, false);
#endif
}
}
yield_impl();
if (res.octetLength == 0)
return;
bool enableDownloads = (imap->_imap_data->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->download.text)));
if (imap->_imap_data->download.rfc822 || imap->_imap_data->download.html || imap->_imap_data->download.text || (rfc822_body_subtype && imap->_imap_data->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->enable.text))))
{
if (cPart(imap)->octetCount + bufLen >= res.octetLength)
{
bufLen = res.octetLength - cPart(imap)->octetCount;
cPart(imap)->octetCount += bufLen;
if (res.octetLength <= res.octetCount && cPart(imap)->xencoding != esp_mail_msg_xencoding_base64 && cPart(imap)->xencoding != esp_mail_msg_xencoding_qp)
bufLen = 0;
res.response[bufLen] = 0;
}
else
cPart(imap)->octetCount = res.octetCount;
if (imap->_debug)
fetchReport(imap, 100 * cPart(imap)->octetCount / res.octetLength, enableDownloads);
if (cPart(imap)->octetCount <= res.octetLength)
{
bool hrdBrk = cPart(imap)->xencoding == esp_mail_msg_xencoding_qp;
hrdBrk &= cPart(imap)->octetCount < res.octetLength;
hrdBrk &= bufLen == 2 && res.response[bufLen - 2] != '\r' && res.response[bufLen - 1] != '\n';
// remove soft break for QP
if (bufLen <= QP_ENC_MSG_LEN && res.response[bufLen - 1] == '=' && cPart(imap)->xencoding == esp_mail_msg_xencoding_qp)
{
hrdBrk = false;
res.response[bufLen - 1] = 0;
bufLen--;
}
size_t olen = 0;
char *decoded = nullptr;
MB_String str;
bool dontDeleteOrModify = false;
// decode the content based on the transfer decoding
if (cPart(imap)->xencoding == esp_mail_msg_xencoding_base64)
{
decoded = (char *)decodeBase64((const unsigned char *)res.response, bufLen, &olen);
}
else if (cPart(imap)->xencoding == esp_mail_msg_xencoding_qp)
{
decoded = allocMem<char *>(bufLen + 10);
decodeQP_UTF8(res.response, decoded);
olen = strlen(decoded);
}
else if (cPart(imap)->xencoding == esp_mail_msg_xencoding_7bit)
{
decoded = decode7Bit_UTF8(res.response);
olen = strlen(decoded);
}
else if (cPart(imap)->xencoding == esp_mail_msg_xencoding_8bit)
{
decoded = decode8Bit_UTF8(res.response);
olen = strlen(decoded);
}
else
{
// binary
dontDeleteOrModify = true;
decoded = res.response;
olen = bufLen;
}
if (decoded)
{
// charset? apply character decoding to the decoded or nonencoded content
if ((rfc822_body_subtype && imap->_imap_data->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_imap_data->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_imap_data->enable.text))))
{
if (imap->_charDecCallback)
{
IMAP_Decoding_Info decoding;
decoding.charset = cPart(imap)->charset.c_str();
decoding.data = decoded;
decoding.type = IMAP_Decoding_Info::message_part_type_text;
imap->_charDecCallback(&decoding);
if (decoding.decodedString.length() > 0)
{
char *buf2 = allocMem<char *>(decoding.decodedString.length() + 1);
strcpy(buf2, decoding.decodedString.c_str());
if (decoded && !dontDeleteOrModify)
// release memory
freeMem(&decoded);
decoded = buf2;
olen = strlen(buf2);
}
}
else
{
esp_mail_char_decoding_scheme scheme = getEncodingFromCharset(cPart(imap)->charset.c_str());
if (scheme == esp_mail_char_decoding_scheme_iso8859_1)
{
int ilen = olen;
int olen2 = (ilen + 1) * 2;
unsigned char *tmp = allocMem<unsigned char *>(olen2);
decodeLatin1_UTF8(tmp, &olen2, (unsigned char *)decoded, &ilen);
if (decoded && !dontDeleteOrModify)
// release memory
freeMem(&decoded);
olen = olen2;
decoded = (char *)tmp;
}
else if (scheme == esp_mail_char_decoding_scheme_tis_620 || scheme == esp_mail_char_decoding_scheme_iso8859_11 || scheme == esp_mail_char_decoding_scheme_windows_874)
{
char *out = allocMem<char *>((olen + 1) * 3);
decodeTIS620_UTF8(out, decoded, olen);
olen = strlen(out);
if (decoded && !dontDeleteOrModify)
// release memory
freeMem(&decoded);
decoded = out;
}
}
if (cPart(imap)->text.length() < imap->_imap_data->limit.msg_size)
{
if (cPart(imap)->text.length() + olen < imap->_imap_data->limit.msg_size)
{
cPart(imap)->textLen += olen;
cPart(imap)->text.append(decoded, olen);
if (hrdBrk)
{
appendNewline(cPart(imap)->text);
cPart(imap)->textLen += 2;
}
}
else
{
int d = imap->_imap_data->limit.msg_size - cPart(imap)->text.length();
cPart(imap)->textLen += d;
if (d > 0)
cPart(imap)->text.append(decoded, d);
if (hrdBrk)
{
appendNewline(cPart(imap)->text);
cPart(imap)->textLen += 2;
}
}
}
}
if (res.filePath.length() > 0 && res.downloadRequest)
{
if (mbfs->ready(mbfs_type imap->_imap_data->storage.type))
{
if (olen > 0)
mbfs->write(mbfs_type imap->_imap_data->storage.type, (uint8_t *)decoded, olen);
if (hrdBrk)
mbfs->write(mbfs_type imap->_imap_data->storage.type, (uint8_t *)"\r\n", 2);
}
}
sendStreamCB(imap, (void *)decoded, olen, res.chunkIdx, hrdBrk);
if (decoded && !dontDeleteOrModify)
// release memory
freeMem(&decoded);
}
}
}
res.chunkIdx++;
}
void ESP_Mail_Client::sendStreamCB(IMAPSession *imap, void *buf, size_t len, int chunkIndex, bool hrdBrk)
{
if (imap->_mimeDataStreamCallback && len > 0)
{
MIME_Data_Stream_Info streaminfo;
streaminfo.uid = cHeader(imap)->message_uid;
streaminfo.disposition = cPart(imap)->content_disposition.c_str();
streaminfo.type = cPart(imap)->content_type.c_str();
streaminfo.charSet = cPart(imap)->charset.c_str();
streaminfo.transfer_encoding = cPart(imap)->content_transfer_encoding.c_str();
streaminfo.cid = cPart(imap)->CID.c_str();
streaminfo.description = cPart(imap)->content_description.c_str();
streaminfo.date = cPart(imap)->creation_date.c_str();
streaminfo.filename = cPart(imap)->filename.c_str();
streaminfo.size = (cPart(imap)->sizeProp) ? cPart(imap)->attach_data_size : cPart(imap)->octetLen;
streaminfo.name = cPart(imap)->name.c_str();
streaminfo.octet_size = cPart(imap)->octetLen;
streaminfo.octet_count = cPart(imap)->octetCount;
streaminfo.isFirstData = chunkIndex == 1;
streaminfo.isLastData = !hrdBrk ? cPart(imap)->octetLen == cPart(imap)->octetCount : false;
streaminfo.data_size = len;
streaminfo.data = buf;
streaminfo.flowed = cPart(imap)->plain_flowed;
streaminfo.delsp = cPart(imap)->plain_delsp;
imap->_mimeDataStreamCallback(streaminfo);
if (hrdBrk)
{
streaminfo.isFirstData = false;
streaminfo.isLastData = cPart(imap)->octetLen == cPart(imap)->octetCount;
streaminfo.data_size = 2;
streaminfo.data = (void *)"\r\n";
imap->_mimeDataStreamCallback(streaminfo);
}
}
}
void ESP_Mail_Client::prepareFilePath(IMAPSession *imap, MB_String &filePath, bool header)
{
bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822;
MB_String fpath = imap->_imap_data->storage.saved_path;
fpath += esp_mail_str_10; /* "/" */
fpath += cHeader(imap)->message_uid;
if (!header)
{
if (!rfc822_body_subtype)
{
fpath += esp_mail_str_86; /* "/msg" */
if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
fpath += mimeinfo[esp_mail_file_extension_txt].endsWith;
else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
fpath += mimeinfo[esp_mail_file_extension_htm].endsWith;
}
else
{
fpath += esp_mail_str_87; /* "/rfc822_msg" */
if (cPart(imap)->rfc822_msg_Idx > 0)
fpath += cPart(imap)->rfc822_msg_Idx;
if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
fpath += mimeinfo[esp_mail_file_extension_txt].endsWith;
else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
fpath += mimeinfo[esp_mail_file_extension_htm].endsWith;
else
// possible rfc822 encapsulated message which cannot fetch its header
fpath += mimeinfo[esp_mail_file_extension_dat].endsWith;
}
}
filePath = fpath;
}
void ESP_Mail_Client::sendStorageNotReadyError(IMAPSession *imap, esp_mail_file_storage_type storageType)
{
#if defined(MBFS_FLASH_FS) || defined(MBFS_SD_FS)
#if !defined(SILENT_MODE)
if (imap->_debug && (storageType == esp_mail_file_storage_type_flash || storageType == esp_mail_file_storage_type_sd))
{
if (storageType == esp_mail_file_storage_type_flash)
{
esp_mail_debug_print_tag(esp_mail_error_mem_str_1 /* "flash Storage is not ready." */, esp_mail_debug_tag_type_error, true);
#if defined(MB_ARDUINO_PICO)
esp_mail_debug_print_tag(esp_mail_error_mem_str_10 /* "please make sure that the size of flash filesystem is not 0 in Pico." */, esp_mail_debug_tag_type_error, true);
#endif
}
else if (storageType == esp_mail_file_storage_type_sd)
esp_mail_debug_print_tag(esp_mail_error_mem_str_2 /* "SD Storage is not ready." */, esp_mail_debug_tag_type_error, true);
}
#endif
#endif
}
IMAPSession::IMAPSession(Client *client)
{
setClient(client);
}
IMAPSession::IMAPSession()
{
}
IMAPSession::~IMAPSession()
{
empty();
}
bool IMAPSession::closeSession()
{
_prev_imap_cmd = esp_mail_imap_cmd_sasl_login;
_prev_imap_custom_cmd = esp_mail_imap_cmd_custom;
if (!connected())
return false;
if (_mbif._idleTimeMs > 0)
mStopListen(false);
if (_loginStatus)
{
#if !defined(ESP8266)
/**
* The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure
* The client disposed without memory released after the server close
* the connection due to LOGOUT command, which caused the memory leaks.
*/
if (!MailClient.imapLogout(this))
return false;
#endif
}
return MailClient.handleIMAPError(this, 0, true);
}
bool IMAPSession::connected()
{
return client.connected();
}
bool IMAPSession::connect(Session_Config *session_config, IMAP_Data *imap_data, bool login)
{
_sessionSSL = false;
_sessionLogin = login;
if (session_config)
session_config->clearPorts();
this->_customCmdResCallback = NULL;
int ptr = toAddr(*session_config);
session_config->addPtr(&_configPtrList, ptr);
if (!handleConnection(session_config, imap_data, _sessionSSL))
return false;
if (!_sessionLogin)
return true;
_loginStatus = MailClient.imapAuth(this, _sessionSSL);
return _loginStatus;
}
bool IMAPSession::mLogin(MB_StringPtr email, MB_StringPtr password, bool isToken)
{
if (_loginStatus)
return true;
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
_session_cfg->login.email = email;
_session_cfg->login.accessToken.clear();
_session_cfg->login.password.clear();
if (isToken)
_session_cfg->login.accessToken = password;
else
_session_cfg->login.password = password;
_loginStatus = MailClient.imapAuth(this, _sessionSSL);
return _loginStatus;
}
void IMAPSession::appendIdList(MB_String &list, IMAP_Identification *identification)
{
if (identification->name.length() > 0 && identification->name.length() <= 1024)
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_name].text, identification->name.c_str());
if (identification->version.length() > 0 && identification->version.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_version].text, identification->version.c_str());
}
if (identification->os.length() > 0 && identification->os.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_os].text, identification->os.c_str());
}
if (identification->vendor.length() > 0 && identification->vendor.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_vendor].text, identification->vendor.c_str());
}
if (identification->support_url.length() > 0 && identification->support_url.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_support_url].text, identification->support_url.c_str());
}
if (identification->address.length() > 0 && identification->address.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_address].text, identification->address.c_str());
}
if (identification->date.length() > 0 && identification->date.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_date].text, identification->date.c_str());
}
if (identification->command.length() > 0 && identification->command.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_command].text, identification->command.c_str());
}
if (identification->arguments.length() > 0 && identification->arguments.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_arguments].text, identification->arguments.c_str());
}
if (identification->environment.length() > 0 && identification->environment.length() <= 1024)
{
if (list.length() > 0)
MailClient.appendSpace(list);
MailClient.appendImap4KeyValue(list, imap_identification_keys[esp_mail_imap_identification_key_environment].text, identification->environment.c_str());
}
}
bool IMAPSession::id(IMAP_Identification *identification)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_61 /* "Send client identification..." */,
esp_mail_dbg_str_82 /* "send IMAP command, ID" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!_feature_capability[esp_mail_imap_read_capability_id])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
if (!MailClient.reconnect(this))
return false;
MB_String cmd, idList;
appendIdList(idList, identification);
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_id].text);
if (idList.length() > 0)
MailClient.appendString(cmd, idList.c_str(), false, false, esp_mail_string_mark_type_round_bracket);
else
{
int bufLen = 50;
char *buf = MailClient.allocMem<char *>(bufLen);
snprintf(buf, bufLen, pgm2Str(esp_mail_str_21 /* "(\"name\" \"ESP Mail Client\" \"version\" \"%s\")" */), ESP_MAIL_VERSION);
cmd += buf;
// release memory
MailClient.freeMem(&buf);
}
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_id;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
String IMAPSession::serverID()
{
return _server_id_tmp.c_str();
}
bool IMAPSession::isAuthenticated()
{
return _authenticated;
}
bool IMAPSession::isLoggedIn()
{
return _loginStatus;
}
bool IMAPSession::isFirmwareUpdateSuccess()
{
return _isFirmwareUpdated;
}
bool IMAPSession::mCustomConnect(Session_Config *session_config, imapResponseCallback callback, MB_StringPtr tag)
{
this->_customCmdResCallback = callback;
this->_responseStatus.tag = tag;
bool ssl = false;
if (!handleConnection(session_config, NULL, ssl))
return false;
return true;
}
bool IMAPSession::handleConnection(Session_Config *session_config, IMAP_Data *imap_data, bool &ssl)
{
_session_cfg = session_config;
if (!client.isInitialized())
return MailClient.handleIMAPError(this, TCP_CLIENT_ERROR_NOT_INITIALIZED, false);
// Resources are also released if network disconnected.
if (!MailClient.reconnect(this))
return false;
// Need to close previous connection first to free resources.
MailClient.closeTCPSession<IMAPSession *>(this);
_session_cfg = session_config;
_imap_data = imap_data;
MailClient.setCert(_session_cfg, _session_cfg->certificate.cert_data);
ssl = false;
if (!connect(ssl))
{
MailClient.closeTCPSession<IMAPSession *>(this);
return false;
}
return true;
}
bool IMAPSession::connect(bool &ssl)
{
ssl = false;
MB_String buf;
if (_imap_data)
{
if (_imap_data->fetch.sequence_set.string.length() > 0 || _imap_data->fetch.uid.length() > 0 || _imap_data->fetch.number.length() > 0)
_headerOnly = false;
else
_headerOnly = true;
}
_totalRead = 0;
_secure = true;
bool secureMode = true;
client.txBufDivider = 16; // minimum, tx buffer size for ssl data and request command data
client.rxBufDivider = 1;
if (_imap_data)
{
if (!_headerOnly && !_imap_data->firmware_update.attach_filename.length() == 0 && !_imap_data->enable.html && !_imap_data->enable.text && !_imap_data->download.attachment && !_imap_data->download.inlineImg && !_imap_data->download.html && !_imap_data->download.text)
client.rxBufDivider = 16; // minimum rx buffer size for only message header
}
MailClient.preparePortFunction(_session_cfg, false, _secure, secureMode, ssl);
#if !defined(SILENT_MODE)
MailClient.printLibInfo<IMAPSession *>(this);
#endif
MailClient.prepareTime<IMAPSession *>(_session_cfg, this);
MailClient.setSecure(client, _session_cfg);
if (!MailClient.beginConnection<IMAPSession *>(_session_cfg, this, secureMode))
return false;
// server connected
client.setTimeout(tcpTimeout);
// wait for greeting
unsigned long dataMs = millis();
while (client.connected() && client.available() == 0 && millis() - dataMs < 2000)
{
yield_impl();
}
int chunkBufSize = client.available();
if (chunkBufSize > 0)
{
char *buf = MailClient.allocMem<char *>(chunkBufSize + 1);
client.readBytes(buf, chunkBufSize);
if (_debug && _debugLevel > esp_mail_debug_level_basic && !_customCmdResCallback)
esp_mail_debug_print((const char *)buf, true);
if (_customCmdResCallback)
{
MailClient.imapResponseStatus(this, buf, esp_mail_str_3 /* "*" */);
_responseStatus.text = buf;
if (_responseStatus.text[_responseStatus.text.length() - 2] == '\r' && _responseStatus.text[_responseStatus.text.length() - 1] == '\n')
_responseStatus.text[_responseStatus.text.length() - 2] = 0;
if (_responseStatus.tag.length() == 0)
this->_responseStatus.tag = esp_mail_imap_tag_str;
_customCmdResCallback(_responseStatus);
}
// release memory
MailClient.freeMem(&buf);
}
if (!_customCmdResCallback)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_48 /* "IMAP server connected" */,
esp_mail_dbg_str_33 /* "IMAP server connected" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
}
return true;
}
void IMAPSession::debug(int level)
{
if (level > esp_mail_debug_level_none)
{
if (level > esp_mail_debug_level_basic && level < esp_mail_debug_level_maintainer)
level = esp_mail_debug_level_basic;
_debugLevel = level;
_debug = true;
client.setDebugLevel(level);
}
else
{
_debugLevel = esp_mail_debug_level_none;
_debug = false;
client.setDebugLevel(0);
}
}
String IMAPSession::errorReason()
{
return MailClient.errorReason(false, _responseStatus.errorCode, _responseStatus.text.c_str());
}
int IMAPSession::errorCode()
{
return _responseStatus.errorCode;
}
bool IMAPSession::mSelectFolder(MB_StringPtr folderName, bool readOnly)
{
if (connected())
{
if (!openFolder(folderName, readOnly))
return false;
}
else
{
_currentFolder = folderName;
}
if (!connected())
{
_responseStatus.errorCode = IMAP_STATUS_OPEN_MAILBOX_FAILED;
_responseStatus.clear();
}
return connected();
}
void IMAPSession::setTCPTimeout(unsigned long timeoutSec)
{
tcpTimeout = timeoutSec;
}
void IMAPSession::setClient(Client *client)
{
this->client.setClient(client);
}
void IMAPSession::setGSMClient(Client *client, void *modem, const char *pin, const char *apn, const char *user, const char *password)
{
this->client.setGSMClient(client, modem, pin, apn, user, password);
}
void IMAPSession::networkConnectionRequestCallback(NetworkConnectionRequestCallback networkConnectionCB)
{
this->client.networkConnectionRequestCallback(networkConnectionCB);
}
void IMAPSession::networkStatusRequestCallback(NetworkStatusRequestCallback networkStatusCB)
{
this->client.networkStatusRequestCallback(networkStatusCB);
}
void IMAPSession::setNetworkStatus(bool status)
{
this->client.setNetworkStatus(status);
MailClient.networkStatus = status;
}
void IMAPSession::setSSLBufferSize(int rx, int tx)
{
this->client.setIOBufferSize(rx, tx);
}
bool IMAPSession::mOpenFolder(MB_StringPtr folderName, bool readOnly)
{
if (!connected())
{
_responseStatus.errorCode = IMAP_STATUS_OPEN_MAILBOX_FAILED;
_responseStatus.clear();
return false;
}
if (readOnly)
return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_examine, true, false);
else
return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_select, true, false);
}
bool IMAPSession::getFolders(FoldersCollection &folders)
{
if (!connected())
return false;
return getMailboxes(folders);
}
bool IMAPSession::mCloseFolder(bool expunge)
{
// no folder opened
if (_currentFolder.length() == 0)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_error_imap_str_11 /* "no mailbox opened" */,
esp_mail_error_imap_str_11 /* "no mailbox opened" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
return false;
}
return closeMailbox(expunge);
}
bool IMAPSession::mListen(bool recon)
{
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if (_currentFolder.length() == 0)
{
_mbif._floderChangedState = false;
_mbif._folderChanged = false;
return false;
}
if (!MailClient.reconnect(this))
return false;
if (!connected())
{
if (!_imap_data || (millis() - _last_server_connect_ms < 2000 && _last_server_connect_ms > 0))
return false;
_last_server_connect_ms = millis();
bool ssl = false;
if (!connect(ssl))
{
MailClient.closeTCPSession<IMAPSession *>(this);
return false;
}
// re-authenticate after session closed
if (!MailClient.imapAuth(this, ssl))
{
MailClient.closeTCPSession<IMAPSession *>(this);
return false;
}
// re-open folder
if (!selectFolder(_currentFolder.c_str()))
return false;
}
// no IDLE was not supported (should be checked after imapAuth)
if (!_feature_capability[esp_mail_imap_read_capability_idle])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
}
if (_mbif._idleTimeMs == 0)
{
_mbif._polling_status.messageNum = 0;
_mbif._polling_status.type = imap_polling_status_type_undefined;
_mbif._polling_status.argument.clear();
_mbif._recentCount = 0;
_mbif._folderChanged = false;
#if !defined(SILENT_MODE)
MB_String dbMsg;
if (!recon)
{
dbMsg = esp_mail_dbg_str_51; /* "listening to " */
dbMsg += _currentFolder;
dbMsg += esp_mail_dbg_str_52; /* " folder changes" */
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_29 /* "Listening to mailbox changes..." */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
}
#endif
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_idle].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_idle;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
#if !defined(SILENT_MODE)
if (!recon)
{
dbMsg = esp_mail_str_88 /* "polling established on " */;
dbMsg += MailClient.Time.getDateTimeString(MailClient.Time.getCurrentTimestamp(), "%B %d, %Y %H:%M:%S");
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_49 /* "Polling established" */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
}
#endif
_mbif._idleTimeMs = millis();
}
else
{
if (_mbif._floderChangedState)
{
_mbif._floderChangedState = false;
_mbif._folderChanged = false;
_mbif._polling_status.messageNum = 0;
_mbif._polling_status.type = imap_polling_status_type_undefined;
_mbif._polling_status.argument.clear();
_mbif._recentCount = 0;
}
size_t imap_idle_tmo = _imap_data->limit.imap_idle_timeout;
if (imap_idle_tmo < 60 * 1000 || imap_idle_tmo > 29 * 60 * 1000)
imap_idle_tmo = 10 * 60 * 1000;
size_t host_check_interval = _imap_data->limit.imap_idle_host_check_interval;
if (host_check_interval < 30 * 1000 || host_check_interval > imap_idle_tmo)
host_check_interval = 60 * 1000;
if (millis() - _last_host_check_ms > host_check_interval && connected())
{
_last_host_check_ms = millis();
IPAddress ip;
if (client.hostByName(_session_cfg->server.host_name.c_str(), ip) != 1)
{
closeSession();
_mbif._idleTimeMs = millis();
return false;
}
}
return MailClient.parseIdleResponse(this);
}
return true;
}
bool IMAPSession::mStopListen(bool recon)
{
_mbif._idleTimeMs = 0;
_mbif._floderChangedState = false;
_mbif._folderChanged = false;
_mbif._polling_status.messageNum = 0;
_mbif._polling_status.type = imap_polling_status_type_undefined;
_mbif._polling_status.argument.clear();
_mbif._recentCount = 0;
if (!connected() || _currentFolder.length() == 0 || !_feature_capability[esp_mail_imap_read_capability_idle])
return false;
if (MailClient.imapSend(this, imap_commands[esp_mail_imap_command_done].text, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_done;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
#if !defined(SILENT_MODE)
if (!recon)
{
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_50 /* "Mailbox listening stopped" */,
esp_mail_dbg_str_54 /* "Mailbox listening stopped" */,
esp_mail_debug_tag_type_client,
true,
false);
}
#endif
return true;
}
bool IMAPSession::folderChanged()
{
return _mbif._floderChangedState;
}
bool IMAPSession::noop()
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_62 /* "Sending noop..." */,
esp_mail_dbg_str_83 /* "send IMAP command, NOOP" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_noop].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_noop;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
void IMAPSession::checkUID()
{
if (MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_uid].text) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_flags].text) ||
MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_body].text) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_peek].text) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_text].text) ||
MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_header].text) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_fields].text) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, esp_mail_str_40 /* "[" */) ||
MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, esp_mail_str_41 /* "]" */) || MailClient.strcmpP(_imap_data->fetch.uid.c_str(), 0, imap_commands[esp_mail_imap_command_mime].text))
_imap_data->fetch.uid = esp_mail_str_3; /* "*" */
}
void IMAPSession::checkPath()
{
MB_String path = _imap_data->storage.saved_path;
if (path[0] != '/')
{
path = '/';
path += _imap_data->storage.saved_path;
path = path.c_str();
}
}
bool IMAPSession::headerOnly()
{
return _headerOnly;
}
struct esp_mail_imap_msg_list_t IMAPSession::data()
{
struct esp_mail_imap_msg_list_t ret;
for (size_t i = 0; i < _headers.size(); i++)
{
#if defined(MB_ARDUINO_ESP) || defined(MB_ARDUINO_PICO)
if (MailClient.getFreeHeap() < ESP_MAIL_MIN_MEM)
continue;
#endif
struct esp_mail_imap_msg_item_t itm;
itm.setRFC822Headers(&_headers[i].header_fields);
itm.UID = _headers[i].message_uid;
itm.msgNo = _headers[i].message_no;
itm.flags = _headers[i].flags.c_str();
itm.acceptLang = _headers[i].accept_language.c_str();
itm.contentLang = _headers[i].content_language.c_str();
itm.hasAttachment = _headers[i].hasAttachment;
itm.fetchError = _headers[i].error_msg.c_str();
getMessages(i, itm);
getRFC822Messages(i, itm);
ret.msgItems.push_back(itm);
}
return ret;
}
SelectedFolderInfo IMAPSession::selectedFolder()
{
return _mbif;
}
void IMAPSession::callback(imapStatusCallback imapCallback)
{
_statusCallback = imapCallback;
}
void IMAPSession::characterDecodingCallback(imapCharacterDecodingCallback callback)
{
_charDecCallback = callback;
}
void IMAPSession::mimeDataStreamCallback(MIMEDataStreamCallback mimeDataStreamCallback)
{
_mimeDataStreamCallback = mimeDataStreamCallback;
}
void IMAPSession::setSystemTime(time_t ts, float gmtOffset)
{
MailClient.Time.setTimestamp(ts, gmtOffset);
}
void IMAPSession::keepAlive(int tcpKeepIdleSeconds, int tcpKeepIntervalSeconds, int tcpKeepCount)
{
this->client.keepAlive(tcpKeepIdleSeconds, tcpKeepIntervalSeconds, tcpKeepCount);
}
bool IMAPSession::isKeepAlive()
{
return this->client.isKeepAlive();
}
void IMAPSession::getMessages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg)
{
msg.text.content = "";
msg.text.charSet = "";
msg.text.content_type = "";
msg.text.transfer_encoding = "";
msg.html.content = "";
msg.html.charSet = "";
msg.html.content_type = "";
msg.html.transfer_encoding = "";
if (messageIndex < _headers.size())
{
int sz = _headers[messageIndex].part_headers.size();
if (sz > 0)
{
for (int i = 0; i < sz; i++)
{
if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_attachment || (!_headers[messageIndex].part_headers[i].rfc822_part && _headers[messageIndex].part_headers[i].message_sub_type != esp_mail_imap_message_sub_type_rfc822))
{
if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none)
{
if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched)
{
msg.text.content = _headers[messageIndex].part_headers[i].text.c_str();
msg.text.charSet = _headers[messageIndex].part_headers[i].charset.c_str();
msg.text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str();
msg.text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str();
}
if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html)
{
msg.html.content = _headers[messageIndex].part_headers[i].text.c_str();
msg.html.charSet = _headers[messageIndex].part_headers[i].charset.c_str();
msg.html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str();
msg.html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str();
}
}
else
{
struct esp_mail_attachment_info_t att;
att.filename = _headers[messageIndex].part_headers[i].filename.c_str();
att.mime = _headers[messageIndex].part_headers[i].content_type.c_str();
att.name = _headers[messageIndex].part_headers[i].name.c_str();
att.size = _headers[messageIndex].part_headers[i].attach_data_size;
att.description = _headers[messageIndex].part_headers[i].content_description.c_str();
att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str();
att.type = _headers[messageIndex].part_headers[i].attach_type;
msg.attachments.push_back(att);
}
}
}
}
}
}
void IMAPSession::getRFC822Messages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg)
{
if (messageIndex < _headers.size())
{
int size = _headers[messageIndex].part_headers.size();
int partIdx = 0;
int cIdx = 0;
IMAP_MSG_Item *_rfc822 = nullptr;
if (size > 0)
{
for (int i = 0; i < size; i++)
{
if (_headers[messageIndex].part_headers[i].message_sub_type == esp_mail_imap_message_sub_type_rfc822 && _headers[messageIndex].part_headers[i].attach_type != esp_mail_att_type_attachment)
{
if (_headers[messageIndex].part_headers[i].rfc822_part)
{
if (partIdx > 0)
msg.rfc822.push_back(*_rfc822);
cIdx = i;
partIdx++;
_rfc822 = new IMAP_MSG_Item();
_rfc822->setRFC822Headers(&_headers[messageIndex].part_headers[i].rfc822_header);
}
else
{
if (MailClient.multipartMember(_headers[messageIndex].part_headers[cIdx].partNumStr, _headers[messageIndex].part_headers[i].partNumStr))
{
if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none)
{
if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched)
{
_rfc822->text.charSet = _headers[messageIndex].part_headers[i].charset.c_str();
_rfc822->text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str();
_rfc822->text.content = _headers[messageIndex].part_headers[i].text.c_str();
_rfc822->text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str();
}
if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html)
{
_rfc822->html.charSet = _headers[messageIndex].part_headers[i].charset.c_str();
_rfc822->html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str();
_rfc822->html.content = _headers[messageIndex].part_headers[i].text.c_str();
_rfc822->html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str();
}
}
else
{
struct esp_mail_attachment_info_t att;
att.filename = _headers[messageIndex].part_headers[i].filename.c_str();
att.mime = _headers[messageIndex].part_headers[i].content_type.c_str();
att.name = _headers[messageIndex].part_headers[i].name.c_str();
att.size = _headers[messageIndex].part_headers[i].attach_data_size;
att.description = _headers[messageIndex].part_headers[i].content_description.c_str();
att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str();
att.type = _headers[messageIndex].part_headers[i].attach_type;
_rfc822->attachments.push_back(att);
}
}
}
}
}
if ((int)msg.rfc822.size() < partIdx && _rfc822 != nullptr)
msg.rfc822.push_back(*_rfc822);
}
}
}
bool IMAPSession::closeMailbox(bool expunge)
{
#if !defined(SILENT_MODE)
MB_String dbMsg = esp_mail_dbg_str_32; /* "closing the " */
dbMsg += _currentFolder;
dbMsg += esp_mail_str_89; /* " folder..." */
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_27 /* "Closing the mailbox folder..." */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.reconnect(this))
return false;
// If folder was opened in readonly mode, use CLOSE command will not expunge the deleted messages
// Or user intent to expunge the deleted message after close the folder.
if (expunge || _readOnlyMode)
{
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_close].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_close;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CLOSE_MAILBOX_FAILED, false))
return false;
}
else
{
// Close folder without expunge
if (_feature_capability[esp_mail_imap_read_capability_unselect])
{
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_unselect].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_unselect;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CLOSE_MAILBOX_FAILED, false))
return false;
}
else
{
// Open non-existing folder
MB_String folder = esp_mail_str_84 /* "message" */;
folder += MailClient.Time.getCurrentTimestamp();
MB_StringPtr folderPtr;
folderPtr = toStringPtr(folder);
openMailbox(folderPtr, esp_mail_imap_auth_mode::esp_mail_imap_mode_examine, true, true);
}
}
_currentFolder.clear();
_mailboxOpened = false;
return true;
}
bool IMAPSession::openMailbox(MB_StringPtr folder, esp_mail_imap_auth_mode mode, bool waitResponse, bool unselect)
{
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
MB_String _folder = folder;
if (!MailClient.reconnect(this))
return false;
if (_folder.length() == 0)
return false;
bool _dbg = _debug;
imapStatusCallback _cb = _statusCallback;
// The SELECT/EXAMINE command automatically deselects any currently selected mailbox
// before attempting the new selection (RFC3501 p.33)
// folder should not close for re-selection otherwise the server returned * BAD Command Argument Error. 12
if (!unselect)
{
bool sameFolder = strcmp(_currentFolder.c_str(), _folder.c_str()) == 0;
// guards 3 seconds to prevent accidently frequently select the same folder with the same mode
if (!_mailboxOpened && sameFolder && millis() - _lastSameFolderOpenMillis < 3000)
{
if ((_readOnlyMode && mode == esp_mail_imap_mode_examine) || (!_readOnlyMode && mode == esp_mail_imap_mode_select))
return true;
}
if (!sameFolder)
_currentFolder = folder;
#if !defined(SILENT_MODE)
MB_String dbMsg = esp_mail_dbg_str_68; /* "selecting the " */
dbMsg += _currentFolder;
dbMsg += esp_mail_str_89; /* " folder..." */
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_51 /* "Open the mailbox folder..." */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
#endif
}
else
{
// Hide the callback and debug info
_debug = false;
_statusCallback = NULL;
_currentFolder = folder;
}
MB_String cmd;
MailClient.appendSpace(cmd, true, mode == esp_mail_imap_mode_examine ? imap_commands[esp_mail_imap_command_examine].text : imap_commands[esp_mail_imap_command_select].text);
MailClient.appendString(cmd, _currentFolder.c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (isCondStoreSupported())
{
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, imap_commands[esp_mail_imap_command_condstore].text, false, false, esp_mail_string_mark_type_round_bracket);
}
_imap_cmd = mode == esp_mail_imap_mode_examine ? esp_mail_imap_cmd_examine : esp_mail_imap_cmd_select;
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
if (!unselect)
_lastSameFolderOpenMillis = millis();
if (waitResponse && !MailClient.handleIMAPResponse(this, IMAP_STATUS_OPEN_MAILBOX_FAILED, false))
{
if (!unselect)
return false;
}
if (unselect)
{
_statusCallback = _cb;
_debug = _dbg;
}
if (mode == esp_mail_imap_mode_examine)
_readOnlyMode = true;
else if (mode == esp_mail_imap_mode_select)
_readOnlyMode = false;
_mailboxOpened = !unselect;
return true;
}
bool IMAPSession::getMailboxes(FoldersCollection &folders)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_16 /* "Reading the list of mailboxes..." */,
esp_mail_dbg_str_35 /* "send IMAP command, LIST" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
_folders.clear();
MB_String cmd;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_list].text);
MailClient.appendString(cmd, NULL, false, false, esp_mail_string_mark_type_double_quote);
MailClient.prependSpace(cmd, esp_mail_str_3 /* "*" */);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_list;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
return false;
folders = _folders;
return true;
}
bool IMAPSession::mGetSubscribesMailboxes(MB_StringPtr reference, MB_StringPtr mailbox, FoldersCollection &folders)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_30 /* "Listing the subscribed mailboxes..." */,
esp_mail_dbg_str_56 /* "send IMAP command, LSUB" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
folders.clear();
MB_String cmd;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_lsub].text);
MailClient.appendString(cmd, MB_String(reference).c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, MB_String(mailbox).c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
FoldersCollection tmp;
for (size_t i = 0; i < this->_folders.size(); i++)
tmp.add(this->_folders[i]);
this->_folders.clear();
_imap_cmd = esp_mail_imap_cmd_lsub;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
return false;
for (size_t i = 0; i < this->_folders.size(); i++)
folders.add(this->_folders[i]);
this->_folders.clear();
for (size_t i = 0; i < tmp.size(); i++)
this->_folders.add(tmp[i]);
tmp.clear();
return true;
}
bool IMAPSession::mSubscribe(MB_StringPtr folder)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_31 /* "Subscribe mailbox..." */,
esp_mail_dbg_str_57 /* "send IMAP command, SUBSCRIBE" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
MB_String cmd;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_subscribe].text);
MailClient.appendString(cmd, MB_String(folder).c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_subscribe;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
return false;
return true;
}
bool IMAPSession::mUnSubscribe(MB_StringPtr folder)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_32 /* "Unsubscribe mailbox..." */,
esp_mail_dbg_str_58 /* "send IMAP command, UNSUBSCRIBE" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
MB_String cmd;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_unsubscribe].text);
MailClient.appendString(cmd, MB_String(folder).c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_unsubscribe;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
return false;
return true;
}
bool IMAPSession::mFetchSequenceSet()
{
MB_String cmd;
if (_imap_data->fetch.sequence_set.UID)
MailClient.appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_fetch].text);
else
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_fetch].text);
MailClient.appendSpace(cmd, false, _imap_data->fetch.sequence_set.string.c_str());
MailClient.appendString(cmd, imap_commands[esp_mail_imap_command_uid].text, false, false, esp_mail_string_mark_type_round_bracket);
addModifier(cmd, esp_mail_imap_command_changedsince, _imap_data->fetch.modsequence);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_msg_num.clear();
_imap_cmd = esp_mail_imap_cmd_fetch_sequence_set;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
return false;
return true;
}
MB_String IMAPSession::prependTag(PGM_P cmd, PGM_P tag)
{
MB_String s = (tag == NULL) ? esp_mail_imap_tag_str : tag;
MailClient.appendSpace(s);
s += cmd;
return s;
}
bool IMAPSession::checkCapabilities()
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_52 /* "Checking the capability..." */,
esp_mail_dbg_str_76 /* "check the capability" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_capability].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_capability;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CHECK_CAPABILITIES_FAILED, false))
return false;
return true;
}
bool IMAPSession::mCreateFolder(MB_StringPtr folderName)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_59 /* "Creating folder..." */,
esp_mail_dbg_str_49 /* "creating folder" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
MB_String cmd;
MailClient.joinStringSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_create].text, folderName);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_create;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
bool IMAPSession::mRenameFolder(MB_StringPtr currentFolderName, MB_StringPtr newFolderName)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_53 /* "Renaming folder..." */,
esp_mail_dbg_str_55 /* "renaming folder" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
MB_String o = currentFolderName;
MB_String n = newFolderName;
o.trim();
n.trim();
if (o == n)
return true;
if (o.length() == 0 || n.length() == 0)
return false;
MB_String cmd;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_rename].text);
MailClient.appendString(cmd, o.c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, n.c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_rename;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
if (_currentFolder == o)
selectFolder(n.c_str(), false);
return true;
}
int IMAPSession::getUID(int msgNum)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_25 /* "Get UID..." */,
esp_mail_dbg_str_79 /* "get UID" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
int uid = mGetUID(msgNum);
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
#if !defined(SILENT_MODE)
MB_String dbMsg = esp_mail_cb_str_54; /* "UID is " */
dbMsg += uid;
MailClient.printDebug<IMAPSession *>(this,
dbMsg.c_str(),
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
#endif
return uid;
}
int IMAPSession::mGetUID(int msgNum)
{
if (_currentFolder.length() == 0)
return 0;
MB_String cmd;
MailClient.joinStringSpace(cmd, true, 3, imap_commands[esp_mail_imap_command_fetch].text, MB_String(msgNum).c_str(), imap_commands[esp_mail_imap_command_uid].text);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return 0;
_imap_cmd = esp_mail_imap_cmd_get_uid;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return 0;
return _uid_tmp;
}
const char *IMAPSession::getFlags(int msgNum)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_55 /* "Get Flags..." */,
esp_mail_dbg_str_80 /* "get flags" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
_flags_tmp.clear();
if (_currentFolder.length() == 0)
return _flags_tmp.c_str();
MB_String cmd, cmd2;
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_fetch].text);
MailClient.appendString(cmd2, imap_commands[esp_mail_imap_command_flags].text, false, false, esp_mail_string_mark_type_round_bracket);
MailClient.joinStringSpace(cmd, false, 2, MB_String(msgNum).c_str(), cmd2.c_str());
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return _flags_tmp.c_str();
_imap_cmd = esp_mail_imap_cmd_get_flags;
MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false);
return _flags_tmp.c_str();
}
bool IMAPSession::mSendCustomCommand(MB_StringPtr cmd, imapResponseCallback callback, MB_StringPtr tag)
{
_customCmdResCallback = callback;
_cmd = cmd;
_cmd.trim();
MB_String _tag = cmd;
_tag.trim();
MB_String _tag2 = tag;
_tag2.trim();
if (_tag2.length() == 0)
{
int p = MailClient.strpos(_tag.c_str(), " ", 0);
if (p > -1)
{
_tag.erase(p, _tag.length() - p);
_tag.trim();
_responseStatus.tag = _tag;
}
}
else
{
_responseStatus.tag = tag;
_responseStatus.tag.trim();
if (MailClient.strpos(_cmd.c_str(), _responseStatus.tag.c_str(), 0, false) == -1)
_cmd = prependTag(_cmd.c_str(), _responseStatus.tag.c_str());
}
// filter for specific command
if (_cmd.find(imap_cmd_pre_tokens[esp_mail_imap_command_idle].c_str()) != MB_String::npos)
_imap_custom_cmd = esp_mail_imap_cmd_idle;
else if (_cmd.find(imap_cmd_pre_tokens[esp_mail_imap_command_append].c_str()) != MB_String::npos)
_imap_custom_cmd = esp_mail_imap_cmd_append;
else if (_cmd.find(imap_cmd_pre_tokens[esp_mail_imap_command_login].c_str()) != MB_String::npos)
_imap_custom_cmd = esp_mail_imap_cmd_sasl_login;
else if (_cmd.find(imap_cmd_pre_tokens[esp_mail_imap_command_logout].c_str()) != MB_String::npos)
_imap_custom_cmd = esp_mail_imap_cmd_logout;
else
_imap_custom_cmd = esp_mail_imap_cmd_custom;
if (_prev_imap_custom_cmd != _imap_custom_cmd || _imap_custom_cmd != esp_mail_imap_cmd_idle)
{
if (MailClient.imapSend(this, _cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
{
_prev_imap_custom_cmd = esp_mail_imap_cmd_custom;
return false;
}
}
_imap_cmd = esp_mail_imap_cmd_custom;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
{
_prev_imap_custom_cmd = esp_mail_imap_cmd_custom;
return false;
}
if (_imap_custom_cmd == esp_mail_imap_cmd_sasl_login)
{
_authenticated = true;
_loginStatus = true;
}
else if (_imap_custom_cmd == esp_mail_imap_cmd_logout)
{
_authenticated = false;
_loginStatus = false;
}
_prev_imap_custom_cmd = _imap_custom_cmd;
if (MailClient.strposP(_cmd.c_str(), imap_auth_capabilities[esp_mail_auth_capability_starttls].text, 0, false) == 0)
{
bool verify = false;
if (_session_cfg)
verify = _session_cfg->certificate.verify;
if (!client.connectSSL(verify))
return false;
// set the secure mode
if (_session_cfg)
{
// We reset the prefer connection mode in case user set it.
_session_cfg->secure.startTLS = false;
_session_cfg->secure.mode = esp_mail_secure_mode_undefined;
}
_secure = true;
}
return true;
}
bool IMAPSession::mSendData(MB_StringPtr data, bool lastData, esp_mail_imap_command cmd)
{
MB_String _data = data;
if (MailClient.imapSend(this, _data.c_str(), lastData) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
if (lastData)
{
_imap_cmd = cmd;
_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, esp_mail_imap_command cmd)
{
if (MailClient.imapSend(this, data, size) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
if (lastData)
{
if (MailClient.imapSend(this, pgm2Str(esp_mail_str_42 /* "\r\n" */), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = cmd;
_cmd.clear();
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
}
return true;
}
bool IMAPSession::mDeleteFolder(MB_StringPtr folderName)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_56 /* "Deleting folder..." */,
esp_mail_dbg_str_81 /* "delete folder" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
MB_String cmd;
MailClient.joinStringSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_delete].text, MB_String(folderName).c_str());
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_delete;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
bool IMAPSession::isCondStoreSupported()
{
return _feature_capability[esp_mail_imap_read_capability_condstore];
}
bool IMAPSession::isModseqSupported()
{
return isCondStoreSupported() && !_mbif._nomodsec;
}
void IMAPSession::addModifier(MB_String &cmd, esp_mail_imap_command_types type, int32_t modsequence)
{
if (modsequence > -1 && isModseqSupported())
{
MB_String modifier;
MailClient.joinStringSpace(modifier, false, 2, imap_commands[type].text, MB_String(modsequence).c_str());
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, modifier.c_str(), false, false, esp_mail_string_mark_type_round_bracket);
}
}
bool IMAPSession::deleteMsg(MessageList *toDelete, const char *sequenceSet, bool UID, bool expunge, int32_t modsequence)
{
if ((toDelete && toDelete->_list.size() == 0) || (!toDelete && strlen(sequenceSet) == 0))
return false;
if (!selectFolder(_currentFolder.c_str(), false))
return false;
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_57 /* "Deleting message(s)..." */,
esp_mail_dbg_str_75 /* "delete message(s)" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
MB_String cmd;
if (UID || toDelete)
{
MailClient.appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_store].text);
if (toDelete && toDelete->_list.size() > 0)
MailClient.appendList<int>(cmd, toDelete->_list);
}
else if (!toDelete && strlen(sequenceSet) > 0)
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_store].text);
if (!toDelete && strlen(sequenceSet) > 0)
cmd += sequenceSet;
addModifier(cmd, esp_mail_imap_command_unchangedsince, modsequence);
cmd += imap_cmd_pre_tokens[esp_mail_imap_command_plus_flags];
MailClient.prependDot(cmd, imap_commands[esp_mail_imap_command_silent].text);
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, esp_mail_str_91 /* "\\Deleted" */, false, false, esp_mail_string_mark_type_round_bracket);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_store;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
if (expunge)
{
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_expunge].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_expunge;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
}
return true;
}
bool IMAPSession::mDeleteMessages(MessageList *toDelete, bool expunge, int32_t modsequence)
{
if (toDelete->_list.size() > 0)
return deleteMsg(toDelete, "", false, expunge);
return true;
}
bool IMAPSession::mDeleteMessagesSet(MB_StringPtr sequenceSet, bool UID, bool expunge, int32_t modsequence)
{
return deleteMsg(nullptr, MB_String(sequenceSet).c_str(), UID, expunge);
}
bool IMAPSession::copyMsg(MessageList *toCopy, const char *sequenceSet, bool UID, MB_StringPtr dest)
{
#if !defined(SILENT_MODE)
MB_String dbMsg = esp_mail_dbg_str_48; /* "copying message(s) to " */
dbMsg += dest;
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_58 /* "Copying message(s)..." */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if ((toCopy && toCopy->_list.size() == 0) || (!toCopy && strlen(sequenceSet) == 0))
return false;
if (!selectFolder(_currentFolder.c_str(), false))
return false;
MB_String cmd;
if (UID || toCopy)
{
MailClient.appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_copy].text);
if (toCopy && toCopy->_list.size() > 0)
MailClient.appendList<int>(cmd, toCopy->_list);
}
else if (!toCopy && strlen(sequenceSet) > 0)
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_copy].text);
if (!toCopy && strlen(sequenceSet) > 0)
cmd += sequenceSet;
MailClient.prependSpace(cmd, MB_String(dest).c_str());
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_copy;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
bool IMAPSession::mCopyMessages(MessageList *toCopy, MB_StringPtr dest)
{
return copyMsg(toCopy, "", false, dest);
}
bool IMAPSession::mCopyMessagesSet(MB_StringPtr sequenceSet, bool UID, MB_StringPtr dest)
{
return copyMsg(nullptr, MB_String(sequenceSet).c_str(), UID, dest);
}
bool IMAPSession::moveMsg(MessageList *toMove, const char *sequenceSet, bool UID, MB_StringPtr dest)
{
#if !defined(SILENT_MODE)
MB_String dbMsg = esp_mail_dbg_str_59; /* "moving message(s) to " */
dbMsg += dest;
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_60 /* "Moving message(s)..." */,
dbMsg.c_str(),
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if ((toMove && toMove->_list.size() == 0) || (!toMove && strlen(sequenceSet) == 0))
return false;
if (!_feature_capability[esp_mail_imap_read_capability_move])
{
bool ret = mCopyMessages(toMove, dest);
if (ret)
ret = mDeleteMessages(toMove, true);
return ret;
}
if (!selectFolder(_currentFolder.c_str(), false))
return false;
MB_String cmd;
if (UID || toMove)
{
MailClient.appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_uid].text, imap_commands[esp_mail_imap_command_move].text);
if (toMove && toMove->_list.size() > 0)
MailClient.appendList<int>(cmd, toMove->_list);
}
else if (!toMove && strlen(sequenceSet) > 0)
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_move].text);
if (!toMove && strlen(sequenceSet) > 0)
cmd += sequenceSet;
MailClient.prependSpace(cmd, MB_String(dest).c_str());
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_move;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
bool IMAPSession::mMoveMessages(MessageList *toMove, MB_StringPtr dest)
{
return moveMsg(toMove, "", false, dest);
}
bool IMAPSession::mMoveMessagesSet(MB_StringPtr sequenceSet, bool UID, MB_StringPtr dest)
{
return moveMsg(nullptr, MB_String(sequenceSet).c_str(), UID, dest);
}
bool IMAPSession::mGetSetQuota(MB_StringPtr quotaRoot, IMAP_Quota_Root_Info *data, bool getMode)
{
#if !defined(SILENT_MODE)
PGM_P p1 = getMode ? esp_mail_cb_str_33 /* "Get quota root resource usage and limit..." */ : esp_mail_cb_str_34 /* "Set quota root resource usage and limit..." */;
PGM_P p2 = getMode ? esp_mail_dbg_str_60 /* "send IMAP command, GETQUOTA" */ : esp_mail_dbg_str_61 /* "send IMAP command, SETQUOTA" */;
MailClient.printDebug<IMAPSession *>(this, p1, p2, esp_mail_debug_tag_type_client, true, false);
#endif
if (!_feature_capability[esp_mail_imap_read_capability_quota])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
MB_String _quotaRoot = quotaRoot;
MB_String cmd;
if (getMode)
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_getquota].text);
MailClient.appendString(cmd, _quotaRoot.c_str(), false, false, esp_mail_string_mark_type_double_quote);
}
else
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_setquota].text);
MailClient.appendString(cmd, _quotaRoot.c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MB_String cmd2;
MailClient.joinStringSpace(cmd2, false, 2, data->name.c_str(), MB_String((int)data->limit).c_str());
MailClient.appendString(cmd, cmd2.c_str(), false, false, esp_mail_string_mark_type_round_bracket);
}
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_quota_tmp.clear();
_imap_cmd = (getMode) ? esp_mail_imap_cmd_get_quota : esp_mail_imap_cmd_set_quota;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
if (getMode)
{
mParseQuota(_quota_tmp.c_str(), data);
}
_quota_tmp.clear();
return true;
}
void IMAPSession::mParseQuota(const char *quota, IMAP_Quota_Root_Info *data)
{
_vectorImpl<MB_String> tokens;
MailClient.splitToken(quota, tokens, " ");
data->quota_root = tokens[0];
tokens[1].erase(0, 1);
data->name = tokens[1];
data->usage = atoi(tokens[2].c_str());
data->limit = atoi(tokens[3].c_str());
}
bool IMAPSession::mGetQuotaRoots(MB_StringPtr mailbox, IMAP_Quota_Roots_List *quotaRootsList)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_35 /* "Get the list of quota roots..." */,
esp_mail_dbg_str_62 /* "send IMAP command, GETQUOTAROOT" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if (!_feature_capability[esp_mail_imap_read_capability_quota])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
MB_String _mailbox = mailbox;
MB_String cmd;
MailClient.appendSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_getquota].text, imap_commands[esp_mail_imap_command_root].text);
MailClient.appendString(cmd, _mailbox.c_str(), false, false, esp_mail_string_mark_type_double_quote);
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_quota_root_tmp.clear();
_quota_tmp.clear();
_imap_cmd = esp_mail_imap_cmd_get_quota_root;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
_vectorImpl<MB_String> tokens;
MailClient.splitToken(_quota_root_tmp.c_str(), tokens, ",");
for (size_t i = 0; i < tokens.size(); i++)
{
_vectorImpl<MB_String> tk;
MailClient.splitToken(tokens[i].c_str(), tk, ":");
IMAP_Quota_Root_Info data;
if (tk.size() > 1)
mParseQuota(tk[1].c_str(), &data);
else
data.quota_root = tk[0];
quotaRootsList->add(data);
}
_quota_root_tmp.clear();
_quota_tmp.clear();
return true;
}
bool IMAPSession::mManageACL(MB_StringPtr mailbox, IMAP_Rights_List *acl_list, IMAP_Rights_Info *acl, MB_StringPtr identifier, esp_mail_imap_command type)
{
#if !defined(SILENT_MODE)
PGM_P p1 = NULL;
PGM_P p2 = NULL;
if (type == esp_mail_imap_cmd_get_acl)
{
p1 = esp_mail_cb_str_36; /* "Get the ACL..." */
p2 = esp_mail_dbg_str_77; /* "get the ACL" */
}
else if (type == esp_mail_imap_cmd_set_acl)
{
p1 = esp_mail_cb_str_37; /* "Setting the ACL..." */
p2 = esp_mail_dbg_str_78; /* "set the ACL" */
}
else if (type == esp_mail_imap_cmd_delete_acl)
{
p1 = esp_mail_cb_str_38; /* "Deleting the ACL..." */
p2 = esp_mail_dbg_str_72; /* "delete the ACL" */
}
else if (type == esp_mail_imap_cmd_my_rights)
{
p1 = esp_mail_cb_str_39; /* "Get my ACL..." */
p2 = esp_mail_dbg_str_23; /* "get my ACL" */
}
MailClient.printDebug<IMAPSession *>(this, p1, p2, esp_mail_debug_tag_type_client, true, false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if (!_feature_capability[esp_mail_imap_read_capability_acl])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
MB_String _mailbox = mailbox;
MB_String _identifier = identifier;
MB_String cmd;
if (type == esp_mail_imap_cmd_get_acl)
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_get_acl].text);
MailClient.appendString(cmd, _mailbox.c_str(), false, false, esp_mail_string_mark_type_double_quote);
}
else if (type == esp_mail_imap_cmd_set_acl)
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_set_acl].text);
MailClient.appendString(cmd, MB_String(mailbox).c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, acl->identifier.c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MB_String rights;
getRights(rights, acl);
MailClient.appendString(cmd, rights.c_str(), false, false, esp_mail_string_mark_type_double_quote);
}
else if (type == esp_mail_imap_cmd_delete_acl)
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_delete_acl].text);
MailClient.appendString(cmd, _mailbox.c_str(), false, false, esp_mail_string_mark_type_double_quote);
MailClient.appendSpace(cmd);
MailClient.appendString(cmd, _identifier.c_str(), false, false, esp_mail_string_mark_type_double_quote);
}
else if (type == esp_mail_imap_cmd_my_rights)
{
MailClient.appendSpace(cmd, true, imap_commands[esp_mail_imap_command_myrights].text);
MailClient.appendString(cmd, _mailbox.c_str(), false, false, esp_mail_string_mark_type_double_quote);
}
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_acl_tmp.clear();
_imap_cmd = type;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
if (type == esp_mail_imap_cmd_get_acl)
parseACL(_acl_tmp, acl_list);
else if (type == esp_mail_imap_cmd_my_rights)
parseRights(_acl_tmp, acl);
_acl_tmp.clear();
return true;
}
void IMAPSession::parseACL(MB_String &acl_str, IMAP_Rights_List *right_list)
{
_vectorImpl<MB_String> tokens;
MailClient.splitToken(acl_str.c_str(), tokens, " ");
for (size_t i = 0; i < tokens.size(); i += 2)
{
IMAP_Rights_Info info;
info.identifier = tokens[i];
parseRights(tokens[i + 1], &info);
right_list->add(info);
}
}
void IMAPSession::parseRights(MB_String &righs_str, IMAP_Rights_Info *info)
{
for (size_t i = 0; i < righs_str.length(); i++)
{
uint8_t c = righs_str[i] - 97;
if (c >= esp_mail_imap_rights_administer && c < esp_mail_imap_rights_maxType)
info->rights[c] = true;
}
}
void IMAPSession::getRights(MB_String &righs_str, IMAP_Rights_Info *info)
{
for (size_t i = esp_mail_imap_rights_administer; i < esp_mail_imap_rights_maxType; i++)
{
if (info->rights[i])
righs_str += (char)(i + 97);
}
}
bool IMAPSession::mNamespace(IMAP_Namespaces_List *ns)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_40 /* "Get namespace..." */,
esp_mail_dbg_str_67 /* "send IMAP command, NAMESPACE" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!MailClient.sessionExisted<IMAPSession *>(this))
return false;
if (!_feature_capability[esp_mail_imap_read_capability_namespace])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
if (MailClient.imapSend(this, prependTag(imap_commands[esp_mail_imap_command_namespace].text).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_ns_tmp.clear();
_imap_cmd = esp_mail_imap_cmd_namespace;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
int cnt = 0;
MB_String personal, other, shared;
for (size_t i = 0; i < _ns_tmp.length(); i++)
{
if (i > 0 && i < _ns_tmp.length() - 1 && _ns_tmp[i] == ' ' && _ns_tmp[i - 1] != '"' && _ns_tmp[i + 1] != '"')
{
if (cnt == 0)
personal = _ns_tmp.substr(0, i);
else if (cnt == 1)
{
other = _ns_tmp.substr(personal.length() + 1, i - personal.length() - 1);
shared = _ns_tmp.substr(i + 1, _ns_tmp.length() - i - 1);
}
cnt++;
}
}
if (personal.length() > 4)
parseNamespaces(personal, &(ns->personal_namespaces));
if (other.length() > 4)
parseNamespaces(other, &(ns->other_users_namespaces));
if (shared.length() > 4)
parseNamespaces(shared, &(ns->shared_namespaces));
return true;
}
bool IMAPSession::mEnable(MB_StringPtr capability)
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_cb_str_41 /* "Enable capability..." */,
esp_mail_dbg_str_68 /* "send IMAP command, ENABLE" */,
esp_mail_debug_tag_type_client,
true,
false);
#endif
if (!_feature_capability[esp_mail_imap_read_capability_enable])
{
#if !defined(SILENT_MODE)
printDebugNotSupported();
#endif
return false;
}
MB_String _cap = capability;
MB_String cmd;
MailClient.joinStringSpace(cmd, true, 2, imap_commands[esp_mail_imap_command_enable].text, _cap.c_str());
if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
return false;
_imap_cmd = esp_mail_imap_cmd_enable;
if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
return false;
return true;
}
void IMAPSession::parseNamespaces(MB_String &ns_str, IMAP_Namespaces *ns)
{
MB_String tmp = ns_str.substr(2, ns_str.length() - 4);
tmp.replaceAll(")(", " ");
_vectorImpl<MB_String> tokens;
MailClient.splitToken(tmp.c_str(), tokens, " ");
for (size_t i = 0; i < tokens.size(); i += 2)
{
IMAP_Namespace_Info info;
info.prefix = tokens[i];
info.delimiter = tokens[i + 1];
ns->add(info);
}
}
void IMAPSession::empty()
{
_nextUID.clear();
_unseenMsgIndex.clear();
_flags_tmp.clear();
_quota_tmp.clear();
_quota_root_tmp.clear();
_acl_tmp.clear();
_ns_tmp.clear();
_server_id_tmp.clear();
_sdFileList.clear();
clearMessageData();
}
IMAP_Status IMAPSession::status()
{
return _cbData;
}
String IMAPSession::fileList()
{
return _sdFileList.c_str();
}
void IMAPSession::clearMessageData()
{
for (size_t i = 0; i < _headers.size(); i++)
{
_headers[i].part_headers.clear();
}
_headers.clear();
_imap_msg_num.clear();
_mbif._searchCount = 0;
_flags_tmp.clear();
#if defined(MB_USE_STD_VECTOR)
_folders.clear();
_mbif._flags.clear();
#endif
}
void IMAPSession::printDebugNotSupported()
{
#if !defined(SILENT_MODE)
MailClient.printDebug<IMAPSession *>(this,
esp_mail_error_imap_str_13 /* "not supported by IMAP server" */,
esp_mail_error_imap_str_13 /* "not supported by IMAP server" */,
esp_mail_debug_tag_type_error,
true,
false);
#endif
}
IMAP_Status::IMAP_Status()
{
}
IMAP_Status::~IMAP_Status()
{
empty();
}
const char *IMAP_Status::info()
{
return _info.c_str();
}
bool IMAP_Status::success()
{
return _success;
}
void IMAP_Status::empty()
{
_info.clear();
}
#endif
#endif /* ESP_MAIL_IMAP_H */