
#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 */