/**
 * Mail Client Arduino Library for Espressif's ESP32 and ESP8266
 *
 *   Version:   1.2.0
 *   Released:  May 17, 2021
 *
 *   Updates:
 * - Add support ESP8266 Core SDK v3.x.x.
 *
 *
 * This library allows Espressif's ESP32 and ESP8266 devices to send and read Email
 * through the SMTP and IMAP servers.
 *
 * The MIT License (MIT)
 * Copyright (c) 2021 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.
*/

#ifndef ESP_Mail_Client_CPP
#define ESP_Mail_Client_CPP

#include "ESP_Mail_Client.h"

#if defined(ESP32)
extern "C"
{
#include <esp_err.h>
#include <esp_wifi.h>
}
#endif

bool ESP_Mail_Client::sendIMAPCommand(IMAPSession *imap, int msgIndex, int cmdCase)
{

  std::string cmd;
  if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0)
    appendP(cmd, esp_mail_str_142, true);
  else
    appendP(cmd, esp_mail_str_143, true);

  char *tmp = intStr(imap->_msgNum[msgIndex]);
  cmd += tmp;
  delS(tmp);
  appendP(cmd, esp_mail_str_147, false);
  if (!imap->_config->fetch.set_seen)
  {
    appendP(cmd, esp_mail_str_152, false);
    appendP(cmd, esp_mail_str_214, false);
  }
  appendP(cmd, esp_mail_str_218, false);

  switch (cmdCase)
  {
  case 1:

    appendP(cmd, esp_mail_str_269, false);
    break;

  case 2:

    if (cPart(imap)->partNumFetchStr.length() > 0)
      cmd += cPart(imap)->partNumFetchStr;
    else
      appendP(cmd, esp_mail_str_215, false);
    appendP(cmd, esp_mail_str_156, false);
    break;

  case 3:

    cmd += cPart(imap)->partNumFetchStr;
    appendP(cmd, esp_mail_str_156, false);
    break;

  default:
    break;
  }

  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)
{

  imap->checkUID();
  imap->checkPath();

  if (!imap->_tcpConnected)
    imap->_mailboxOpened = false;

  std::string buf;
  std::string command;
  std::string _uid;
  appendP(command, esp_mail_str_27, true);
  char *tmp = nullptr;
  size_t readCount = 0;
  imap->_multipart_levels.clear();

  if (!reconnect(imap))
    return false;

  int cmem = ESP.getFreeHeap();

  if (cmem < ESP_MAIL_MIN_MEM)
  {
    if (imap->_debug)
    {
      esp_mail_debug("");
      errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY);
    }
    goto out;
  }

  //new session
  if (!imap->_tcpConnected)
  {
    //authenticate new
    if (!imapAuth(imap))
    {
      closeTCP(imap);
      return false;
    }
  }
  else
  {
    //reuse session
    for (size_t i = 0; i < imap->_headers.size(); i++)
      imap->_headers[i].part_headers.clear();
    imap->_headers.clear();

    if (strlen(imap->_config->fetch.uid) > 0)
      imap->_headerOnly = false;
    else
      imap->_headerOnly = true;
  }
  imap->_rfc822_part_count = 0;
  imap->_mbif._availableItems = 0;
  imap->_msgNum.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->_config->fetch.set_seen && !imap->_headerOnly && imap->_readOnlyMode))
  {
    if (!imap->openFolder(imap->_currentFolder.c_str(), imap->_readOnlyMode && !imap->_config->fetch.set_seen))
      return handleIMAPError(imap, IMAP_STATUS_OPEN_MAILBOX_FAILED, false);
  }

  if (imap->_headerOnly)
  {
    if (strlen(imap->_config->search.criteria) > 0)
    {

      if (imap->_readCallback)
      {
        imapCB(imap, "", false);
        imapCBP(imap, esp_mail_str_66, false);
      }

      if (imap->_debug)
        debugInfoP(esp_mail_str_232);

      if (strposP(imap->_config->search.criteria, esp_mail_str_137, 0) != -1)
      {
        imap->_uidSearch = true;
        appendP(command, esp_mail_str_138, false);
      }

      appendP(command, esp_mail_str_139, false);

      for (size_t i = 0; i < strlen(imap->_config->search.criteria); i++)
      {
        if (imap->_config->search.criteria[i] != ' ' && imap->_config->search.criteria[i] != '\r' && imap->_config->search.criteria[i] != '\n' && imap->_config->search.criteria[i] != '$')
          buf.append(1, imap->_config->search.criteria[i]);

        if (imap->_config->search.criteria[i] == ' ')
        {
          tmp = strP(esp_mail_str_140);
          char *tmp2 = strP(esp_mail_str_224);

          if ((imap->_uidSearch && strcmp(buf.c_str(), tmp) == 0) || (imap->_unseen && buf.find(tmp2) != std::string::npos))
            buf.clear();
          delS(tmp);
          delS(tmp2);

          tmp = strP(esp_mail_str_141);
          if (strcmp(buf.c_str(), tmp) != 0 && buf.length() > 0)
          {
            appendP(command, esp_mail_str_131, false);
            command += buf;
          }
          delS(tmp);
          buf.clear();
        }
      }

      tmp = strP(esp_mail_str_223);
      if (imap->_unseen && strpos(imap->_config->search.criteria, tmp, 0) == -1)
        appendP(command, esp_mail_str_223, false);
      delS(tmp);

      if (buf.length() > 0)
      {
        appendP(command, esp_mail_str_131, false);
        command += buf;
      }

      if (imapSend(imap, command.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      std::string().swap(command);

      imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_search;

      if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession))
        return false;

      if (imap->_readCallback)
      {
        std::string s;
        appendP(s, esp_mail_str_34, true);
        appendP(s, esp_mail_str_68, false);
        char *tmp = intStr(imap->_config->limit.search);
        s += tmp;
        delS(tmp);
        imapCB(imap, s.c_str(), false);

        if (imap->_msgNum.size() > 0)
        {

          appendP(s, esp_mail_str_69, true);
          tmp = intStr(imap->_mbif._searchCount);
          s += tmp;
          delS(tmp);
          appendP(s, esp_mail_str_70, false);
          imapCB(imap, s.c_str(), false);

          appendP(s, esp_mail_str_71, true);
          tmp = intStr(imap->_msgNum.size());
          s += tmp;
          delS(tmp);
          appendP(s, esp_mail_str_70, false);
          imapCB(imap, s.c_str(), false);
        }
        else
          imapCBP(imap, esp_mail_str_72, false);
      }
    }
    else
    {
      imap->_mbif._availableItems++;
      imap->_msgNum.push_back(imap->_mbif._nextUID - 1);
      imap->_headerOnly = false;
      char *tmp = intStr(imap->_mbif._nextUID - 1);
      _uid = tmp;
      delS(tmp);
      imap->_config->fetch.uid = _uid.c_str();

      if (imap->_readCallback)
        imapCBP(imap, esp_mail_str_73, false);
    }
  }
  else
  {
    imap->_mbif._availableItems++;
    imap->_msgNum.push_back(atoi(imap->_config->fetch.uid));
  }

  for (size_t i = 0; i < imap->_msgNum.size(); i++)
  {

    imap->_cMsgIdx = i;
    imap->_totalRead++;

    if (ESP.getFreeHeap() - (imap->_config->limit.msg_size * (i + 1)) < ESP_MAIL_MIN_MEM)
    {
      if (imap->_debug)
        errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY);
      goto out;
    }

    if (imap->_readCallback)
    {
      readCount++;

      std::string s;
      appendP(s, esp_mail_str_74, true);
      char *tmp = intStr(imap->_totalRead);
      s += tmp;
      delS(tmp);

      if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0)
        appendP(s, esp_mail_str_75, false);
      else
        appendP(s, esp_mail_str_76, false);

      tmp = intStr(imap->_msgNum[i]);
      s += tmp;
      delS(tmp);
      imapCB(imap, "", false);
      imapCB(imap, s.c_str(), false);
    }

    if (imap->_debug)
      debugInfoP(esp_mail_str_233);

    std::string cmd;
    if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0)
      appendP(cmd, esp_mail_str_142, true);
    else
      appendP(cmd, esp_mail_str_143, true);

    if (imap->_debug)
      debugInfoP(esp_mail_str_77);

    char *tmp = intStr(imap->_msgNum[i]);
    cmd += tmp;
    delS(tmp);

    appendP(cmd, esp_mail_str_147, false);
    if (!imap->_config->fetch.set_seen)
    {
      appendP(cmd, esp_mail_str_152, false);
      appendP(cmd, esp_mail_str_214, false);
    }
    appendP(cmd, esp_mail_str_218, false);

    appendP(cmd, esp_mail_str_144, false);
    appendP(cmd, esp_mail_str_156, false);
    if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_header;

    int err = IMAP_STATUS_BAD_COMMAND;
    if (imap->_headerOnly)
      err = IMAP_STATUS_IMAP_RESPONSE_FAILED;

    if (!handleIMAPResponse(imap, err, closeSession))
      return false;

    if (!imap->_headerOnly)
    {
      imap->_cPartIdx = 0;

      //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
      {
        //singlepart
        if (imap->_debug)
        {
          std::string s;
          appendP(s, esp_mail_str_81, true);
          s += '1';
          esp_mail_debug(s.c_str());
        }

        cHeader(imap)->partNumStr.clear();
        if (!sendIMAPCommand(imap, i, 1))
          return false;

        imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_mime;
        if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession))
          return false;
      }

      if (imap->_config->download.text || imap->_config->download.html || imap->_config->download.attachment || imap->_config->download.inlineImg)
      {
        if (!_sdOk && imap->_storageType == esp_mail_file_storage_type_sd)
        {
          _sdOk = sdTest();
          if (_sdOk)
            if (!ESP_MAIL_SD_FS.exists(imap->_config->storage.saved_path))
              createDirs(imap->_config->storage.saved_path);
        }
        else if (!_flashOk && imap->_storageType == esp_mail_file_storage_type_flash)
#if defined(ESP32)
          _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
          _flashOk = ESP_MAIL_FLASH_FS.begin();
#endif
      }

      if (cHeader(imap)->part_headers.size() > 0)
      {
        if (cHeader(imap)->attachment_count > 0 && imap->_readCallback)
        {
          std::string s;
          appendP(s, esp_mail_str_34, true);
          appendP(s, esp_mail_str_78, false);
          char *tmp = intStr(cHeader(imap)->attachment_count);
          s += tmp;
          delS(tmp);
          appendP(s, esp_mail_str_79, false);
          imapCB(imap, s.c_str(), false);

          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)
              imapCB(imap, cPart(imap)->filename.c_str(), false);
          }
        }

        std::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 *tmp = getUID();
                  cPart(imap)->filename = tmp;
                  appendP(cPart(imap)->filename, esp_mail_str_40, false);
                  delS(tmp);
                }
              }
            }
          }
        }

        int acnt = 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;

          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->_config->enable.rfc822 || imap->_config->download.rfc822) && rfc822_body_subtype) || (!rfc822_body_subtype && ((imap->_config->enable.text && (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)) || (imap->_config->enable.html && cPart(imap)->msg_type == esp_mail_msg_type_html) || (cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)));
            if (!ret)
              continue;

            if ((imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))))
            {

              if (ccnt == 0)
              {
                imapCB(imap, "", false);
                imapCBP(imap, esp_mail_str_57, false);
              }

              if (imap->_debug)
              {
                if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
                  debugInfoP(esp_mail_str_59);
                else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
                  debugInfoP(esp_mail_str_60);
              }
            }
            else
            {
              if (ccnt == 0)
              {
                imapCB(imap, "", false);
                imapCBP(imap, esp_mail_str_307, false);
              }

              if (imap->_debug)
              {
                if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
                  debugInfoP(esp_mail_str_308);
                else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
                  debugInfoP(esp_mail_str_309);
              }
            }

            ccnt++;

            if (!sendIMAPCommand(imap, i, 2))
              return false;

            imap->_imap_cmd = esp_mail_imap_command::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 && (_sdOk || _flashOk))
          {

            if (imap->_config->download.attachment || imap->_config->download.inlineImg)
            {
              if (acnt == 0)
              {
                imapCB(imap, "", false);
                imapCBP(imap, esp_mail_str_80, false);
              }

              if (imap->_debug)
                debugInfoP(esp_mail_str_55);

              acnt++;
              if (cPart(imap)->octetLen <= (int)imap->_config->limit.attachment_size)
              {

                if (_sdOk || _flashOk)
                {

                  if ((int)j < (int)cHeader(imap)->part_headers.size() - 1)
                    if (cHeader(imap)->part_headers[j + 1].octetLen > (int)imap->_config->limit.attachment_size)
                      cHeader(imap)->downloaded_bytes += cHeader(imap)->part_headers[j + 1].octetLen;

                  if (!sendIMAPCommand(imap, i, 3))
                    return false;

                  imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_attachment;
                  if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession))
                    return false;
                  delay(0);
                }
              }
              else
              {
                if ((int)j == (int)cHeader(imap)->part_headers.size() - 1)
                  cHeader(imap)->downloaded_bytes += cPart(imap)->octetLen;
              }
            }
          }
        }
      }

      if (imap->_config->download.header && !imap->_headerSaved)
      {
        if (imap->_readCallback)
        {
          imapCB(imap, "", false);
          imapCBP(imap, esp_mail_str_124, false);
        }
        saveHeader(imap);
      }

      if (closeSession)
      {
        if (imap->_storageType == esp_mail_file_storage_type_sd)
        {
          if (_sdOk)
            ESP_MAIL_SD_FS.end();
          _sdOk = false;
        }
        else if (imap->_storageType == esp_mail_file_storage_type_flash)
        {
          if (_flashOk)
            ESP_MAIL_FLASH_FS.end();

          _flashOk = false;
        }
      }

      imap->_cMsgIdx++;
    }

    if (imap->_debug)
    {
      std::string s;
      appendP(s, esp_mail_str_261, true);
      appendP(s, esp_mail_str_84, false);
      char *tmp = intStr(ESP.getFreeHeap());
      s += tmp;
      delS(tmp);
      esp_mail_debug(s.c_str());
    }
  }

out:

  if (readCount < imap->_msgNum.size())
  {
    imap->_mbif._availableItems = readCount;
    imap->_msgNum.erase(imap->_msgNum.begin() + readCount, imap->_msgNum.end());
  }

  if (closeSession)
  {
    if (!imap->closeSession())
      return false;
  }
  else
  {
    if (imap->_readCallback)
    {
      imapCB(imap, "", false);
      imapCBP(imap, esp_mail_str_87, false);
    }

    if (imap->_debug)
      debugInfoP(esp_mail_str_88);
  }

  if (imap->_readCallback)
    imapCB(imap, "", true);

  return true;
}
bool ESP_Mail_Client::getMultipartFechCmd(IMAPSession *imap, int msgIdx, std::string &partText)
{
  if (imap->_multipart_levels.size() == 0)
    return false;

  int cLevel = imap->_multipart_levels.size() - 1;

  cHeader(imap)->partNumStr.clear();

  if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0)
    appendP(partText, esp_mail_str_142, true);
  else
    appendP(partText, esp_mail_str_143, true);

  char *tmp = intStr(imap->_msgNum[msgIdx]);
  partText += tmp;
  delS(tmp);

  appendP(partText, esp_mail_str_147, false);
  if (!imap->_config->fetch.set_seen)
  {
    appendP(partText, esp_mail_str_152, false);
    appendP(partText, esp_mail_str_214, false);
  }
  appendP(partText, esp_mail_str_218, false);

  for (size_t i = 0; i < imap->_multipart_levels.size(); i++)
  {
    if (i > 0)
    {
      appendP(partText, esp_mail_str_152, false);
      appendP(cHeader(imap)->partNumStr, esp_mail_str_152, false);
    }

    tmp = intStr(imap->_multipart_levels[i].level);
    partText += tmp;
    cHeader(imap)->partNumStr += tmp;
    delS(tmp);
  }

  if (imap->_multipart_levels[cLevel].fetch_rfc822_header)
  {
    appendP(partText, esp_mail_str_51, false);
    imap->_multipart_levels[cLevel].append_body_text = true;
  }
  else
    appendP(partText, esp_mail_str_148, false);

  imap->_multipart_levels[cLevel].fetch_rfc822_header = false;

  return true;
}

bool ESP_Mail_Client::multipartMember(const std::string &part, const std::string &check)
{
  if (part.length() > check.length())
    return false;

  for (size_t i = 0; i < part.length(); i++)
    if (part[i] != check[i])
      return false;

  return true;
}

bool ESP_Mail_Client::fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx)
{
  bool ret = true;

  if (!connected(imap))
  {
    closeTCP(imap);
    return false;
  }
  int cLevel = 0;

  do
  {

    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;

    std::string cmd;

    if (!getMultipartFechCmd(imap, msgIdx, cmd))
      return true;

    if (imap->_debug)
    {
      std::string s;
      if (imap->_multipart_levels.size() > 1)
        appendP(s, esp_mail_str_86, true);
      else
        appendP(s, esp_mail_str_81, true);
      s += cHeader(imap)->partNumStr;
      esp_mail_debug(s.c_str());
    }

    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;
    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)
        {
          //to 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
            appendP(_cpart->partNumFetchStr, esp_mail_str_152, false);
            appendP(_cpart->partNumFetchStr, esp_mail_str_215, false);
            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;
}

bool ESP_Mail_Client::connected(IMAPSession *imap)
{
  if (!imap->_secure)
  {
    if (!imap->httpClient._stream())
      return false;
    return imap->httpClient._stream()->_ns_connected();
  }
  else
  {
    if (!imap->httpClient.stream())
      return false;
    return imap->httpClient.stream()->connected();
  }
}

bool ESP_Mail_Client::imapAuth(IMAPSession *imap)
{

  bool ssl = false;
  std::string buf;
#if defined(ESP32)
  imap->httpClient.setDebugCallback(NULL);
#elif defined(ESP8266)

#endif

  if (imap->_config != nullptr)
  {
    if (strlen(imap->_config->fetch.uid) > 0)
      imap->_headerOnly = false;
    else
      imap->_headerOnly = true;
  }

  imap->_totalRead = 0;
  imap->_secure = true;
  bool secureMode = true;

#if defined(ESP32)
  if (imap->_debug)
    imap->httpClient.setDebugCallback(esp_mail_debug);
#elif defined(ESP8266)
  imap->httpClient.txBufDivider = 16; //minimum, tx buffer size for ssl data and request command data
  imap->httpClient.rxBufDivider = 1;
  if (imap->_config != nullptr)
  {
    if (!imap->_headerOnly && !imap->_config->enable.html && !imap->_config->enable.text && !imap->_config->download.attachment && !imap->_config->download.inlineImg && !imap->_config->download.html && !imap->_config->download.text)
      imap->httpClient.rxBufDivider = 16; // minimum rx buffer size for only message header
  }
#endif

  if (imap->_sesson_cfg->server.port == esp_mail_imap_port_143)
  {
    imap->_secure = false;
    secureMode = false;
  }
  else
    secureMode = !imap->_sesson_cfg->secure.startTLS;

  setSecure(imap->httpClient, imap->_sesson_cfg, imap->_caCert);

  if (imap->_readCallback)
    imapCBP(imap, esp_mail_str_50, false);

  if (imap->_debug)
  {
    std::string s;
    appendP(s, esp_mail_str_314, true);
    s += ESP_MAIL_VERSION;
    esp_mail_debug(s.c_str());

    debugInfoP(esp_mail_str_225);
    appendP(s, esp_mail_str_261, true);
    appendP(s, esp_mail_str_211, false);
    s += imap->_sesson_cfg->server.host_name;
    esp_mail_debug(s.c_str());
    char *tmp = intStr(imap->_sesson_cfg->server.port);
    appendP(s, esp_mail_str_261, true);
    appendP(s, esp_mail_str_201, false);
    s += tmp;
    delS(tmp);
    esp_mail_debug(s.c_str());
  }

  imap->httpClient.begin(imap->_sesson_cfg->server.host_name, imap->_sesson_cfg->server.port);

  if (!imap->httpClient.connect(secureMode))
    return handleIMAPError(imap, IMAP_STATUS_SERVER_CONNECT_FAILED, false);

  imap->_tcpConnected = true;
  WiFiClient *stream = imap->httpClient.stream();
#if defined(ESP32)
  stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC);
#elif defined(ESP8266)
  stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000);
#endif
  imap->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000;

  if (imap->_readCallback)
    imapCBP(imap, esp_mail_str_54, false);

  if (imap->_debug)
    debugInfoP(esp_mail_str_228);

init:

  if (!imap->checkCapability())
    return false;

  //start TLS when needed or the server issue
  if ((imap->_auth_capability.start_tls || imap->_sesson_cfg->secure.startTLS) && !ssl)
  {
    std::string s;
    if (imap->_readCallback)
    {
      appendP(s, esp_mail_str_34, true);
      appendP(s, esp_mail_str_209, false);
      esp_mail_debug(s.c_str());
    }

    if (imap->_debug)
    {
      appendP(s, esp_mail_str_196, true);
      esp_mail_debug(s.c_str());
    }

    imapSendP(imap, esp_mail_str_311, false);
    imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_starttls;
    if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false))
      return false;

    if (imap->_debug)
    {
      debugInfoP(esp_mail_str_310);
    }

    //connect in secure mode
    //do ssl handshaking
    if (!imap->httpClient._stream()->_ns_connect_ssl())
      return handleIMAPError(imap, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP, false);

    //set the secure mode
    imap->_sesson_cfg->secure.startTLS = false;
    ssl = true;
    imap->_secure = true;

    //check the capabilitiy again
    goto init;
  }

  imap->clearMessageData();
  imap->_mailboxOpened = false;

  bool creds = strlen(imap->_sesson_cfg->login.email) > 0 && strlen(imap->_sesson_cfg->login.password) > 0;
  bool xoauth_auth = strlen(imap->_sesson_cfg->login.accessToken) > 0 && imap->_auth_capability.xoauth2;
  bool login_auth = creds;
  bool plain_auth = imap->_auth_capability.plain && creds;

  bool supported_auth = xoauth_auth || login_auth || plain_auth;

  if (!supported_auth)
    return handleIMAPError(imap, IMAP_STATUS_NO_SUPPORTED_AUTH, false);

  //rfc4959
  if (supported_auth)
  {
    if (imap->_readCallback)
    {
      imapCB(imap, "", false);
      imapCBP(imap, esp_mail_str_56, false);
    }
  }

  if (xoauth_auth)
  {
    if (!imap->_auth_capability.xoauth2)
      return handleIMAPError(imap, IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false);

    if (imap->_debug)
      debugInfoP(esp_mail_str_291);

    std::string cmd;
    appendP(cmd, esp_mail_str_292, true);
    cmd += getEncodedToken(imap);
    if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_auth;
    if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, false))
      return false;
  }
  else if (login_auth)
  {

    if (imap->_debug)
      debugInfoP(esp_mail_str_229);

    std::string cmd;

    appendP(cmd, esp_mail_str_130, true);
    cmd += imap->_sesson_cfg->login.email;
    appendP(cmd, esp_mail_str_131, false);
    cmd += imap->_sesson_cfg->login.password;

    if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login;
    if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true))
      return false;
  }
  else if (plain_auth)
  {
    if (imap->_debug)
      debugInfoP(esp_mail_str_290);

    const char *usr = imap->_sesson_cfg->login.email;
    const char *psw = imap->_sesson_cfg->login.password;
    int len = strlen(usr) + strlen(psw) + 2;
    uint8_t *tmp = new uint8_t[len];
    memset(tmp, 0, len);
    int p = 1;
    memcpy(tmp + p, usr, strlen(usr));
    p += strlen(usr) + 1;
    memcpy(tmp + p, psw, strlen(psw));
    p += strlen(psw);

    std::string s;
    appendP(s, esp_mail_str_41, true);
    s += encodeBase64Str(tmp, p);
    delete[] tmp;

    if (imapSend(imap, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login;
    if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true))
      return false;
  }

  return true;
}

std::string ESP_Mail_Client::getEncodedToken(IMAPSession *imap)
{
  std::string raw;
  appendP(raw, esp_mail_str_285, true);
  raw += imap->_sesson_cfg->login.email;
  appendP(raw, esp_mail_str_286, false);
  raw += imap->_sesson_cfg->login.accessToken;
  appendP(raw, esp_mail_str_287, false);
  std::string s = encodeBase64Str((const unsigned char *)raw.c_str(), raw.length());
  return s;
}

bool ESP_Mail_Client::imapLogout(IMAPSession *imap)
{
  if (imap->_readCallback)
  {
    imapCB(imap, "", false);
    imapCBP(imap, esp_mail_str_85, false);
  }

  if (imap->_debug)
    debugInfoP(esp_mail_str_234);

  if (imapSendP(imap, esp_mail_str_146, 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;

  if (imap->_readCallback)
  {
    imapCB(imap, "", false);
    imapCBP(imap, esp_mail_str_187, false);
  }

  if (imap->_debug)
    debugInfoP(esp_mail_str_235);

  return true;
}

void ESP_Mail_Client::errorStatusCB(IMAPSession *imap, int error)
{
  imap->_imapStatus.statusCode = error;
  std::string s;
  if (imap->_readCallback)
  {
    appendP(s, esp_mail_str_53, true);
    s += imap->errorReason().c_str();
    imapCB(imap, s.c_str(), false);
  }

  if (imap->_debug)
  {
    appendP(s, esp_mail_str_185, true);
    s += imap->errorReason().c_str();
    esp_mail_debug(s.c_str());
  }
}

size_t ESP_Mail_Client::imapSendP(IMAPSession *imap, PGM_P v, bool newline)
{
  if (!reconnect(imap))
  {
    closeTCP(imap);
    return 0;
  }

  if (!connected(imap))
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!imap->_tcpConnected)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  char *tmp = strP(v);
  size_t len = 0;

  if (newline)
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(tmp);
    if (!imap->_secure)
      len = imap->httpClient._ns_println(tmp);
    else
      len = imap->httpClient.stream()->println(tmp);
  }
  else
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(tmp, false);
    if (!imap->_secure)
      len = imap->httpClient._ns_print(tmp);
    else
      len = imap->httpClient.stream()->print(tmp);
  }

  if (len != strlen(tmp) && len != strlen(tmp) + 2)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  delS(tmp);

  return len;
}

size_t ESP_Mail_Client::imapSend(IMAPSession *imap, const char *data, bool newline)
{
  if (!reconnect(imap))
  {
    closeTCP(imap);
    return 0;
  }

  if (!connected(imap))
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!imap->_tcpConnected)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  size_t len = 0;

  if (newline)
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(data);
    if (!imap->_secure)
      len = imap->httpClient._ns_println(data);
    else
      len = imap->httpClient.stream()->println(data);
  }
  else
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(data, false);
    if (!imap->_secure)
      len = imap->httpClient._ns_print(data);
    else
      len = imap->httpClient.stream()->print(data);
  }

  if (len != strlen(data) && len != strlen(data) + 2)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }
  return len;
}

size_t ESP_Mail_Client::imapSend(IMAPSession *imap, int data, bool newline)
{
  if (!reconnect(imap))
  {
    closeTCP(imap);
    return 0;
  }

  if (!connected(imap))
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!imap->_tcpConnected)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  char *tmp = intStr(data);
  size_t len = 0;

  if (newline)
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(tmp);
    if (!imap->_secure)
      len = imap->httpClient._ns_println(tmp);
    else
      len = imap->httpClient.stream()->println(tmp);
  }
  else
  {
    if (imap->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(tmp, false);
    if (!imap->_secure)
      len = imap->httpClient._ns_print(tmp);
    else
      len = imap->httpClient.stream()->print(tmp);
  }

  if (len != strlen(tmp) && len != strlen(tmp) + 2)
  {
    errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  delS(tmp);

  return len;
}

bool ESP_Mail_Client::setFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession)
{
  return _setFlag(imap, msgUID, flag, 0, closeSession);
}

bool ESP_Mail_Client::addFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession)
{
  return _setFlag(imap, msgUID, flag, 1, closeSession);
}

bool ESP_Mail_Client::removeFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession)
{
  return _setFlag(imap, msgUID, flag, 2, closeSession);
}

bool ESP_Mail_Client::_setFlag(IMAPSession *imap, int msgUID, const char *flag, uint8_t action, bool closeSession)
{
  if (!reconnect(imap))
    return false;

  if (!imap->_tcpConnected)
  {
    imap->_mailboxOpened = false;
    return false;
  }

  if (imap->_currentFolder.length() == 0)
  {
    if (imap->_readCallback)
      debugInfoP(esp_mail_str_153);

    if (imap->_debug)
    {
      std::string e;
      appendP(e, esp_mail_str_185, true);
      appendP(e, esp_mail_str_151, false);
      esp_mail_debug(e.c_str());
    }
  }
  else
  {
    if (imap->_readOnlyMode || !imap->_mailboxOpened)
    {
      if (!imap->selectFolder(imap->_currentFolder.c_str(), false))
        return false;
    }
  }

  if (imap->_readCallback)
  {
    imapCB(imap, "", false);
    if (action == 0)
      debugInfoP(esp_mail_str_157);
    else if (action == 1)
      debugInfoP(esp_mail_str_155);
    else
      debugInfoP(esp_mail_str_154);
  }

  if (imap->_debug)
  {
    if (action == 0)
      debugInfoP(esp_mail_str_253);
    else if (action == 1)
      debugInfoP(esp_mail_str_254);
    else
      debugInfoP(esp_mail_str_255);
  }

  std::string cmd;
  appendP(cmd, esp_mail_str_249, true);
  char *tmp = intStr(msgUID);
  cmd += tmp;
  delS(tmp);
  if (action == 0)
    appendP(cmd, esp_mail_str_250, false);
  else if (action == 1)
    appendP(cmd, esp_mail_str_251, false);
  else
    appendP(cmd, esp_mail_str_252, false);
  cmd += flag;
  appendP(cmd, esp_mail_str_192, false);

  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_PARSE_FLAG_FAILED, false))
    return false;

  if (closeSession)
    imap->closeSession();

  return true;
}

void ESP_Mail_Client::createDirs(std::string dirs)
{
  std::string dir = "";
  int count = 0;
  for (size_t i = 0; i < dirs.length(); i++)
  {
    dir.append(1, dirs[i]);
    count++;
    if (dirs[i] == '/')
    {
      if (dir.length() > 0)
        ESP_MAIL_SD_FS.mkdir(dir.substr(0, dir.length() - 1).c_str());
      count = 0;
    }
  }
  if (count > 0)
    ESP_MAIL_SD_FS.mkdir(dir.c_str());
  std::string().swap(dir);
}

bool ESP_Mail_Client::sdTest()
{
#if defined(CARD_TYPE_SD)
  if (_sdConfigSet)
    sdBegin(_sck, _miso, _mosi, _ss);
  else
    sdBegin();
#endif

#if defined(ESP32)
#if defined(CARD_TYPE_SD_MMC)
  if (_sdConfigSet)
    sdMMCBegin(sd_mmc_mountpoint, sd_mmc_mode1bit, sd_mmc_format_if_mount_failed);
#endif
#endif

  file = ESP_MAIL_SD_FS.open(esp_mail_str_204, FILE_WRITE);
  if (!file)
    return false;

  if (!file.write(32))
    return false;
  file.close();

  file = ESP_MAIL_SD_FS.open(esp_mail_str_204);
  if (!file)
    return false;

  while (file.available())
  {
    if (file.read() != 32)
      return false;
  }
  file.close();

  ESP_MAIL_SD_FS.remove(esp_mail_str_204);

  return true;
}

std::string ESP_Mail_Client::getEncodedToken(SMTPSession *smtp)
{
  std::string raw;
  appendP(raw, esp_mail_str_285, true);
  raw += smtp->_sesson_cfg->login.email;
  appendP(raw, esp_mail_str_286, false);
  raw += smtp->_sesson_cfg->login.accessToken;
  appendP(raw, esp_mail_str_287, false);
  return encodeBase64Str((const unsigned char *)raw.c_str(), raw.length());
}

bool ESP_Mail_Client::smtpAuth(SMTPSession *smtp)
{

  if (!reconnect(smtp))
    return false;

  bool ssl = false;
  smtp->_secure = true;
  bool secureMode = true;

  std::string s;

#if defined(ESP32)
  smtp->httpClient.setDebugCallback(NULL);
#elif defined(ESP8266)
  smtp->httpClient.rxBufDivider = 16; // minimum rx buffer for smtp status response
  smtp->httpClient.txBufDivider = 8;  // medium tx buffer for faster attachment/inline data transfer
#endif

  if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_25)
  {
    smtp->_secure = false;
    secureMode = false;
  }
  else
  {
    if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_587)
      smtp->_sesson_cfg->secure.startTLS = true;

    secureMode = !smtp->_sesson_cfg->secure.startTLS;

    //to prevent to send the connection upgrade command when some server promotes
    //the starttls capability even the current connection was already secured.
    if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_465)
      ssl = true;
  }

  setSecure(smtp->httpClient, smtp->_sesson_cfg, smtp->_caCert);

  //Server connection attempt: no status code
  if (smtp->_sendCallback)
    smtpCBP(smtp, esp_mail_str_120);

  if (smtp->_debug)
  {
    appendP(s, esp_mail_str_314, true);
    s += ESP_MAIL_VERSION;
    esp_mail_debug(s.c_str());

    debugInfoP(esp_mail_str_236);
    appendP(s, esp_mail_str_261, true);
    appendP(s, esp_mail_str_211, false);
    s += smtp->_sesson_cfg->server.host_name;
    esp_mail_debug(s.c_str());
    char *tmp = intStr(smtp->_sesson_cfg->server.port);
    appendP(s, esp_mail_str_261, true);
    appendP(s, esp_mail_str_201, false);
    s += tmp;
    delS(tmp);
    esp_mail_debug(s.c_str());
  }
#if defined(ESP32)
  if (smtp->_debug)
    smtp->httpClient.setDebugCallback(esp_mail_debug);
#endif

  smtp->httpClient.begin(smtp->_sesson_cfg->server.host_name, smtp->_sesson_cfg->server.port);

  if (!smtp->httpClient.connect(secureMode))
    return handleSMTPError(smtp, SMTP_STATUS_SERVER_CONNECT_FAILED);

  //server connected
  smtp->_tcpConnected = true;

  if (smtp->_debug)
    debugInfoP(esp_mail_str_238);

  if (smtp->_sendCallback)
  {
    smtpCB(smtp, "");
    smtpCBP(smtp, esp_mail_str_121);
  }

#if defined(ESP32)
  smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC);
#elif defined(ESP8266)
  smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000);
#endif
  smtp->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000;

  smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state;

  //expected status code 220 for ready to service
  if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_220, SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED))
    return false;

init:

  //Sending greeting hello response
  if (smtp->_sendCallback)
  {
    smtpCB(smtp, "");
    smtpCBP(smtp, esp_mail_str_122);
  }

  if (smtp->_debug)
    debugInfoP(esp_mail_str_239);

  appendP(s, esp_mail_str_6, true);
  if (strlen(smtp->_sesson_cfg->login.user_domain) > 0)
    s += smtp->_sesson_cfg->login.user_domain;
  else
    appendP(s, esp_mail_str_44, false);

  if (smtpSendP(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_greeting;

  if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, 0))
  {
    appendP(s, esp_mail_str_5, true);
    if (strlen(smtp->_sesson_cfg->login.user_domain) > 0)
      s += smtp->_sesson_cfg->login.user_domain;
    else
      appendP(s, esp_mail_str_44, false);

    if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED))
      return false;
    smtp->_send_capability.esmtp = false;
    smtp->_auth_capability.login = true;
  }
  else
    smtp->_send_capability.esmtp = true;

  //start TLS when needed
  if ((smtp->_auth_capability.start_tls || smtp->_sesson_cfg->secure.startTLS) && !ssl)
  {
    //send starttls command
    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "");
      smtpCBP(smtp, esp_mail_str_209);
    }

    if (smtp->_debug)
    {
      appendP(s, esp_mail_str_196, true);
      esp_mail_debug(s.c_str());
    }

    //expected status code 250 for complete the request
    //some server returns 220 to restart to initial state
    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls;
    smtpSendP(smtp, esp_mail_str_311, false);
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED))
      return false;

    if (smtp->_debug)
    {
      debugInfoP(esp_mail_str_310);
    }

    //connect using secure mode
    //do ssl handshaking
    if (!smtp->httpClient._stream()->_ns_connect_ssl())
      return handleSMTPError(smtp, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP);

    //set the secure mode
    smtp->_sesson_cfg->secure.startTLS = false;
    ssl = true;
    smtp->_secure = true;

    //return to initial state if the response status is 220.
    if (smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_220)
      goto init;
  }

  bool creds = strlen(smtp->_sesson_cfg->login.email) > 0 && strlen(smtp->_sesson_cfg->login.password) > 0;
  bool xoauth_auth = strlen(smtp->_sesson_cfg->login.accessToken) > 0 && smtp->_auth_capability.xoauth2;
  bool login_auth = smtp->_auth_capability.login && creds;
  bool plain_auth = smtp->_auth_capability.plain && creds;

  if (xoauth_auth || login_auth || plain_auth)
  {
    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "", false);
      smtpCBP(smtp, esp_mail_str_56, false);
    }

    //log in
    if (xoauth_auth)
    {
      if (smtp->_debug)
        debugInfoP(esp_mail_str_288);

      if (!smtp->_auth_capability.xoauth2)
        return handleSMTPError(smtp, SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false);

      if (smtpSendP(smtp, esp_mail_str_289, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      if (smtpSend(smtp, getEncodedToken(smtp).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_AUTHEN_FAILED))
        return false;

      return true;
    }
    else if (plain_auth)
    {

      if (smtp->_debug)
        debugInfoP(esp_mail_str_241);

      if (smtp->_debug)
      {
        appendP(s, esp_mail_str_261, true);
        s += smtp->_sesson_cfg->login.email;
        esp_mail_debug(s.c_str());

        appendP(s, esp_mail_str_131, false);
        for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++)
          appendP(s, esp_mail_str_183, false);
        esp_mail_debug(s.c_str());
      }

      //rfc4616
      const char *usr = smtp->_sesson_cfg->login.email;
      const char *psw = smtp->_sesson_cfg->login.password;
      int len = strlen(usr) + strlen(psw) + 2;
      uint8_t *tmp = new uint8_t[len];
      memset(tmp, 0, len);
      int p = 1;
      memcpy(tmp + p, usr, strlen(usr));
      p += strlen(usr) + 1;
      memcpy(tmp + p, psw, strlen(psw));
      p += strlen(psw);

      std::string s;
      appendP(s, esp_mail_str_45, true);
      appendP(s, esp_mail_str_131, false);
      s += encodeBase64Str(tmp, p);
      delete[] tmp;

      if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth_plain;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_USER_LOGIN_FAILED))
        return false;

      return true;
    }
    else if (login_auth)
    {
      if (smtp->_debug)
        debugInfoP(esp_mail_str_240);

      if (smtpSendP(smtp, esp_mail_str_4, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_AUTHEN_FAILED))
        return false;

      if (smtp->_debug)
      {
        appendP(s, esp_mail_str_261, true);
        s += smtp->_sesson_cfg->login.email;
        esp_mail_debug(s.c_str());
      }

      if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.email, strlen(smtp->_sesson_cfg->login.email)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_user;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_USER_LOGIN_FAILED))
        return false;

      if (smtp->_debug)
      {
        appendP(s, esp_mail_str_261, true);
        for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++)
          appendP(s, esp_mail_str_183, false);
        esp_mail_debug(s.c_str());
      }

      if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.password, strlen(smtp->_sesson_cfg->login.password)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_psw;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_PASSWORD_LOGIN_FAILED))
        return false;

      return true;
    }
  }

  return true;
}

void ESP_Mail_Client::mimeFromFile(const char *name, std::string &mime)
{
  std::string ext = name;
  size_t p = ext.find_last_of(".");
  if (p != std::string::npos)
  {
    ext = ext.substr(p, ext.length() - p);
    if (ext.length() > 0)
      getMIME(ext.c_str(), mime);
  }
}

bool ESP_Mail_Client::connected(SMTPSession *smtp)
{
  if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
  {
    if (!smtp->httpClient._stream())
      return false;
    return smtp->httpClient._stream()->_ns_connected();
  }
  else
  {
    if (!smtp->httpClient.stream())
      return false;
    return smtp->httpClient.stream()->connected();
  }
}

bool ESP_Mail_Client::setSendingResult(SMTPSession *smtp, SMTP_Message *msg, bool result)
{
  if (result)
    smtp->_sentSuccessCount++;
  else
    smtp->_sentFailedCount++;

  if (smtp->_sendCallback)
  {
    struct esp_mail_smtp_send_status_t status;
    status.completed = result;
    status.timesstamp = time(nullptr);
    status.subject = msg->subject;
    status.recipients = msg->_rcp[0].email;

    smtp->sendingResult.add(status);

    smtp->_cbData._sentSuccess = smtp->_sentSuccessCount;
    smtp->_cbData._sentFailed = smtp->_sentFailedCount;
  }

  return result;
}

bool ESP_Mail_Client::sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession)
{

  if (strlen(msg->html.content) > 0 || msg->html.blob.size > 0 || strlen(msg->html.file.name) > 0)
    msg->type |= esp_mail_msg_type_html;

  if (strlen(msg->text.content) > 0 || msg->text.blob.size > 0 || strlen(msg->text.file.name) > 0)
    msg->type |= esp_mail_msg_type_plain;

  for (size_t i = 0; i < msg->_rfc822.size(); i++)
  {
    if (strlen(msg->_rfc822[i].html.content) > 0)
      msg->_rfc822[i].type |= esp_mail_msg_type_html;

    if (strlen(msg->_rfc822[i].text.content) > 0)
      msg->_rfc822[i].type |= esp_mail_msg_type_plain;
  }

  return _sendMail(smtp, msg, closeSession);
}

void ESP_Mail_Client::getMIME(const char *ext, std::string &mime)
{
  mime = "";
  for (int i = 0; i < esp_mail_file_extension_maxType; i++)
  {
    if (strcmp_P(ext, mimeinfo[i].endsWith) == 0)
    {
      char *tmp = strP(mimeinfo[i].mimeType);
      mime = tmp;
      delS(tmp);
      break;
    }
  }
}

size_t ESP_Mail_Client::numAtt(SMTPSession *smtp, esp_mail_attach_type type, SMTP_Message *msg)
{
  size_t count = 0;
  for (size_t i = 0; i < msg->_att.size(); i++)
  {
    if (msg->_att[i]._int.att_type == type)
      count++;
  }
  return count;
}

bool ESP_Mail_Client::validEmail(const char *s)
{
  std::string str(s);
  auto at = std::find(str.begin(), str.end(), '@');
  auto dot = std::find(at, str.end(), '.');
  return (at != str.end()) && (dot != str.end());
}
bool ESP_Mail_Client::checkEmail(SMTPSession *smtp, SMTP_Message *msg)
{
  bool validRecipient = false;

  if (!validEmail(msg->sender.email))
  {
    errorStatusCB(smtp, SMTP_STATUS_NO_VALID_SENDER_EXISTED);
    return setSendingResult(smtp, msg, false);
  }

  for (uint8_t i = 0; i < msg->_rcp.size(); i++)
  {
    if (validEmail(msg->_rcp[i].email))
      validRecipient = true;
  }

  if (!validRecipient)
  {
    errorStatusCB(smtp, SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED);
    return setSendingResult(smtp, msg, false);
  }

  return true;
}

bool ESP_Mail_Client::_sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession)
{

  smtp->_smtpStatus.statusCode = 0;
  smtp->_smtpStatus.respCode = 0;
  smtp->_smtpStatus.text.clear();
  bool rfc822MSG = false;

  if (!checkEmail(smtp, msg))
    return false;

  smtp->_chunkedEnable = false;
  smtp->_chunkCount = 0;

  //new session
  if (!smtp->_tcpConnected)
  {
    if (!smtpAuth(smtp))
    {
      closeTCP(smtp);
      return setSendingResult(smtp, msg, false);
    }
    smtp->_sentSuccessCount = 0;
    smtp->_sentFailedCount = 0;
    smtp->sendingResult.clear();
  }
  else
  {
    //reuse session
    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "");
      if (smtp->_sentSuccessCount || smtp->_sentFailedCount)
        smtpCBP(smtp, esp_mail_str_267);
      else
        smtpCBP(smtp, esp_mail_str_208);
    }

    if (smtp->_debug)
    {
      if (smtp->_sentSuccessCount || smtp->_sentFailedCount)
        debugInfoP(esp_mail_str_268);
      else
        debugInfoP(esp_mail_str_207);
    }
  }

  if (smtp->_sendCallback)
  {
    smtpCB(smtp, "");
    smtpCBP(smtp, esp_mail_str_125);
  }

  if (smtp->_debug)
    debugInfoP(esp_mail_str_242);

  std::string buf;
  std::string buf2;
  checkBinaryData(smtp, msg);

  if (msg->priority >= esp_mail_smtp_priority_high && msg->priority <= esp_mail_smtp_priority_low)
  {
    char *tmp = intStr(msg->priority);
    appendP(buf2, esp_mail_str_17, true);
    buf2 += tmp;
    delS(tmp);
    appendP(buf2, esp_mail_str_34, false);

    if (msg->priority == esp_mail_smtp_priority_high)
    {
      appendP(buf2, esp_mail_str_18, false);
      appendP(buf2, esp_mail_str_21, false);
    }
    else if (msg->priority == esp_mail_smtp_priority_normal)
    {
      appendP(buf2, esp_mail_str_19, false);
      appendP(buf2, esp_mail_str_22, false);
    }
    else if (msg->priority == esp_mail_smtp_priority_low)
    {
      appendP(buf2, esp_mail_str_20, false);
      appendP(buf2, esp_mail_str_23, false);
    }
  }

  appendP(buf2, esp_mail_str_10, false);

  if (strlen(msg->sender.name) > 0)
    buf2 += msg->sender.name;

  appendP(buf2, esp_mail_str_14, false);
  buf2 += msg->sender.email;
  appendP(buf2, esp_mail_str_15, false);
  appendP(buf2, esp_mail_str_34, false);

  appendP(buf, esp_mail_str_8, true);
  appendP(buf, esp_mail_str_14, false);
  buf += msg->sender.email;
  appendP(buf, esp_mail_str_15, false);

  if (smtp->_send_capability.binaryMIME && smtp->_send_capability.chunking && msg->enable.chunking && (msg->text._int.binary || msg->html._int.binary))
    appendP(buf, esp_mail_str_104, false);

  if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_sender;
  if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_SENDER_FAILED))
    return setSendingResult(smtp, msg, false);

  for (uint8_t i = 0; i < msg->_rcp.size(); i++)
  {
    if (i == 0)
    {
      appendP(buf2, esp_mail_str_11, false);
      if (strlen(msg->_rcp[i].name) > 0)
        buf2 += msg->_rcp[i].name;

      appendP(buf2, esp_mail_str_14, false);
      buf2 += msg->_rcp[i].email;
      appendP(buf2, esp_mail_str_15, false);
    }
    else
    {
      if (strlen(msg->_rcp[i].name) > 0)
      {
        appendP(buf2, esp_mail_str_263, false);
        buf2 += msg->_rcp[i].name;
        appendP(buf2, esp_mail_str_14, false);
      }
      else
        appendP(buf2, esp_mail_str_13, false);
      buf2 += msg->_rcp[i].email;
      appendP(buf2, esp_mail_str_15, false);
    }

    if (i == msg->_rcp.size() - 1)
      appendP(buf2, esp_mail_str_34, false);

    buf.clear();
    //only address
    appendP(buf, esp_mail_str_9, false);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->_rcp[i].email;
    appendP(buf, esp_mail_str_15, false);

    //rfc3461, rfc3464
    if (smtp->_send_capability.dsn)
    {
      if (msg->response.notify != esp_mail_smtp_notify::esp_mail_smtp_notify_never)
      {
        appendP(buf, esp_mail_str_262, false);
        int opcnt = 0;
        if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_success)
        {
          if (opcnt > 0)
            appendP(buf, esp_mail_str_263, false);
          appendP(buf, esp_mail_str_264, false);
          opcnt++;
        }
        if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_failure)
        {
          if (opcnt > 0)
            appendP(buf, esp_mail_str_263, false);
          appendP(buf, esp_mail_str_265, false);
          opcnt++;
        }
        if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_delay)
        {
          if (opcnt > 0)
            appendP(buf, esp_mail_str_263, false);
          appendP(buf, esp_mail_str_266, false);
          opcnt++;
        }
      }
    }

    if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);

    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED))
      return setSendingResult(smtp, msg, false);
  }

  for (uint8_t i = 0; i < msg->_cc.size(); i++)
  {
    if (i == 0)
    {
      appendP(buf2, esp_mail_str_12, false);
      appendP(buf2, esp_mail_str_14, false);
      buf2 += msg->_cc[i].email;
      appendP(buf2, esp_mail_str_15, false);
    }
    else
    {
      appendP(buf2, esp_mail_str_13, false);
      buf2 += msg->_cc[i].email;
      appendP(buf2, esp_mail_str_15, false);
    }

    if (i == msg->_cc.size() - 1)
      appendP(buf2, esp_mail_str_34, false);

    buf.clear();

    appendP(buf, esp_mail_str_9, false);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->_cc[i].email;
    appendP(buf, esp_mail_str_15, false);

    if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);

    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED))
      return setSendingResult(smtp, msg, false);
  }

  for (uint8_t i = 0; i < msg->_bcc.size(); i++)
  {
    appendP(buf, esp_mail_str_9, true);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->_bcc[i].email;
    appendP(buf, esp_mail_str_15, false);
    if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);
    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED))
      return setSendingResult(smtp, msg, false);
  }

  if (smtp->_sendCallback)
  {
    smtpCB(smtp, "");
    smtpCBP(smtp, esp_mail_str_126);
  }

  if (smtp->_debug)
    debugInfoP(esp_mail_str_243);

  if (smtp->_send_capability.chunking && msg->enable.chunking)
  {
    smtp->_chunkedEnable = true;
    if (!bdat(smtp, msg, buf2.length(), false))
      return false;
  }
  else
  {
    if (smtpSendP(smtp, esp_mail_str_16, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);

    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_354, SMTP_STATUS_SEND_BODY_FAILED))
      return setSendingResult(smtp, msg, false);
  }

  if (smtpSend(smtp, buf2.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  std::string s;
  appendP(s, esp_mail_str_24, true);
  s += msg->subject;
  appendP(s, esp_mail_str_34, false);

  if (msg->_hdr.size() > 0)
  {
    for (uint8_t k = 0; k < msg->_hdr.size(); k++)
    {
      s += msg->_hdr[k];
      appendP(s, esp_mail_str_34, false);
    }
  }

  if (strlen(msg->response.reply_to) > 0)
  {
    appendP(s, esp_mail_str_184, false);
    appendP(s, esp_mail_str_14, false);
    s += msg->response.reply_to;
    appendP(s, esp_mail_str_15, false);
    appendP(s, esp_mail_str_34, false);
  }

  if (strlen(msg->response.return_path) > 0)
  {
    appendP(s, esp_mail_str_46, false);
    appendP(s, esp_mail_str_14, false);
    s += msg->response.return_path;
    appendP(s, esp_mail_str_15, false);
    appendP(s, esp_mail_str_34, false);
  }

  appendP(s, esp_mail_str_3, false);

  if (!bdat(smtp, msg, s.length(), false))
    return false;

  if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  return sendMSGData(smtp, msg, closeSession, rfc822MSG);
}

bool ESP_Mail_Client::sendMSGData(SMTPSession *smtp, SMTP_Message *msg, bool closeSession, bool rfc822MSG)
{
  std::string s;
  std::string mixed = getBoundary(15);
  std::string alt = getBoundary(15);

  if (numAtt(smtp, esp_mail_att_type_attachment, msg) == 0 && msg->_parallel.size() == 0 && msg->_rfc822.size() == 0)
  {
    if (msg->type == (esp_mail_msg_type_plain | esp_mail_msg_type_html | esp_mail_msg_type_enriched) || numAtt(smtp, esp_mail_att_type_inline, msg) > 0)
    {
      if (!sendMSG(smtp, msg, alt))
        return setSendingResult(smtp, msg, false);
    }
    else if (msg->type != esp_mail_msg_type_none)
    {
      if (!sendPartText(smtp, msg, msg->type, ""))
        return setSendingResult(smtp, msg, false);
    }
  }
  else
  {
    appendP(s, esp_mail_str_1, true);
    s += mixed;
    appendP(s, esp_mail_str_35, false);

    appendP(s, esp_mail_str_33, false);
    s += mixed;
    appendP(s, esp_mail_str_34, false);

    if (!bdat(smtp, msg, s.length(), false))
      return false;

    if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);

    if (!sendMSG(smtp, msg, alt))
      return setSendingResult(smtp, msg, false);

    if (!bdat(smtp, msg, 2, false))
      return false;

    if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);

    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "");
      smtpCBP(smtp, esp_mail_str_127);
    }

    if (smtp->_debug)
      debugInfoP(esp_mail_str_244);

    if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_attachment, msg) > 0)
      esp_mail_debug("");

    if (!sendAttachments(smtp, msg, mixed))
      return setSendingResult(smtp, msg, false);

    if (!sendParallelAttachments(smtp, msg, mixed))
      return setSendingResult(smtp, msg, false);

    if (!sendRFC822Msg(smtp, msg, mixed, closeSession, msg->_rfc822.size() > 0))
      return setSendingResult(smtp, msg, false);

    appendP(s, esp_mail_str_33, true);
    s += mixed;
    appendP(s, esp_mail_str_33, false);

    if (!bdat(smtp, msg, s.length(), false))
      return false;

    if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return setSendingResult(smtp, msg, false);
  }

  if (!rfc822MSG)
  {
    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "");
      smtpCBP(smtp, esp_mail_str_303);
    }

    if (smtp->_debug)
      debugInfoP(esp_mail_str_304);

    if (smtp->_chunkedEnable)
    {

      if (!bdat(smtp, msg, 0, true))
        return false;

      smtp->_smtp_cmd = esp_mail_smtp_cmd_chunk_termination;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED))
        return false;
    }
    else
    {
      if (smtpSendP(smtp, esp_mail_str_37, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return setSendingResult(smtp, msg, false);

      smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body;
      if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED))
        return setSendingResult(smtp, msg, false);
    }

    setSendingResult(smtp, msg, true);

    if (closeSession)
      if (!smtp->closeSession())
        return false;
  }

  return true;
}

bool ESP_Mail_Client::sendRFC822Msg(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool closeSession, bool rfc822MSG)
{
  if (msg->_rfc822.size() == 0)
    return true;
  std::string buf;
  for (uint8_t i = 0; i < msg->_rfc822.size(); i++)
  {
    buf.clear();
    getRFC822PartHeader(smtp, buf, boundary);

    getRFC822MsgEnvelope(smtp, &msg->_rfc822[i], buf);

    if (!bdat(smtp, msg, buf.length(), false))
      return false;

    if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    if (!sendMSGData(smtp, &msg->_rfc822[i], closeSession, rfc822MSG))
      return false;
  }

  return true;
}

void ESP_Mail_Client::getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg, std::string &buf)
{
  if (strlen(msg->date) > 0)
  {
    appendP(buf, esp_mail_str_99, false);
    buf += msg->date;
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->from.email) > 0)
  {
    appendP(buf, esp_mail_str_10, false);

    if (strlen(msg->from.name) > 0)
      buf += msg->from.name;

    appendP(buf, esp_mail_str_14, false);
    buf += msg->from.email;
    appendP(buf, esp_mail_str_15, false);
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->sender.email) > 0)
  {
    appendP(buf, esp_mail_str_150, false);

    if (strlen(msg->sender.name) > 0)
      buf += msg->sender.name;

    appendP(buf, esp_mail_str_14, false);
    buf += msg->sender.email;
    appendP(buf, esp_mail_str_15, false);
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->response.reply_to) > 0)
  {
    appendP(buf, esp_mail_str_184, false);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->response.reply_to;
    appendP(buf, esp_mail_str_15, false);
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->response.return_path) > 0)
  {
    appendP(buf, esp_mail_str_46, false);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->response.return_path;
    appendP(buf, esp_mail_str_15, false);
    appendP(buf, esp_mail_str_34, false);
  }

  for (uint8_t i = 0; i < msg->_rcp.size(); i++)
  {
    if (i == 0)
    {
      appendP(buf, esp_mail_str_11, false);
      if (strlen(msg->_rcp[i].name) > 0)
        buf += msg->_rcp[i].name;

      appendP(buf, esp_mail_str_14, false);
      buf += msg->_rcp[i].email;
      appendP(buf, esp_mail_str_15, false);
    }
    else
    {
      if (strlen(msg->_rcp[i].name) > 0)
      {
        appendP(buf, esp_mail_str_263, false);
        buf += msg->_rcp[i].name;
        appendP(buf, esp_mail_str_14, false);
      }
      else
        appendP(buf, esp_mail_str_13, false);
      buf += msg->_rcp[i].email;
      appendP(buf, esp_mail_str_15, false);
    }

    if (i == msg->_rcp.size() - 1)
      appendP(buf, esp_mail_str_34, false);
  }

  for (uint8_t i = 0; i < msg->_cc.size(); i++)
  {
    if (i == 0)
    {
      appendP(buf, esp_mail_str_12, false);
      appendP(buf, esp_mail_str_14, false);
      buf += msg->_cc[i].email;
      appendP(buf, esp_mail_str_15, false);
    }
    else
    {
      appendP(buf, esp_mail_str_13, false);
      buf += msg->_cc[i].email;
      appendP(buf, esp_mail_str_15, false);
    }

    if (i == msg->_cc.size() - 1)
      appendP(buf, esp_mail_str_34, false);
  }

  for (uint8_t i = 0; i < msg->_bcc.size(); i++)
  {
    if (i == 0)
    {
      appendP(buf, esp_mail_str_149, false);
      appendP(buf, esp_mail_str_14, false);
      buf += msg->_bcc[i].email;
      appendP(buf, esp_mail_str_15, false);
    }
    else
    {
      appendP(buf, esp_mail_str_13, false);
      buf += msg->_bcc[i].email;
      appendP(buf, esp_mail_str_15, false);
    }

    if (i == msg->_bcc.size() - 1)
      appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->subject) > 0)
  {
    appendP(buf, esp_mail_str_279, false);
    buf += msg->subject;
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->keyword) > 0)
  {
    appendP(buf, esp_mail_str_145, false);
    buf += msg->keyword;
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->comment) > 0)
  {
    appendP(buf, esp_mail_str_134, false);
    buf += msg->comment;
    appendP(buf, esp_mail_str_34, false);
  }

  if (strlen(msg->messageID) > 0)
  {
    appendP(buf, esp_mail_str_274, false);
    appendP(buf, esp_mail_str_14, false);
    buf += msg->messageID;
    appendP(buf, esp_mail_str_15, false);
    appendP(buf, esp_mail_str_34, false);
  }
}

bool ESP_Mail_Client::bdat(SMTPSession *smtp, SMTP_Message *msg, int len, bool last)
{
  if (!smtp->_chunkedEnable || !msg->enable.chunking)
    return true;

  smtp->_chunkCount++;

  std::string bdat;
  appendP(bdat, esp_mail_str_106, true);
  char *tmp = intStr(len);
  bdat += tmp;
  if (last)
    appendP(bdat, esp_mail_str_173, false);
  delS(tmp);
  if (smtpSend(smtp, bdat.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  if (!smtp->_send_capability.pipelining)
  {
    smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body;
    if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED))
      return setSendingResult(smtp, msg, false);
    smtp->_chunkCount = 0;
  }
  return true;
}

void ESP_Mail_Client::checkBinaryData(SMTPSession *smtp, SMTP_Message *msg)
{
  if (msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type & esp_mail_msg_type_html)
  {
    if ((msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched) > 0)
    {
      if (strlen(msg->text.transfer_encoding) > 0)
      {
        if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0)
        {
          msg->text._int.binary = true;
        }
      }
    }

    if ((msg->type & esp_mail_msg_type_html) > 0)
    {
      if (strlen(msg->html.transfer_encoding) > 0)
      {
        if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0)
        {
          msg->html._int.binary = true;
        }
      }
    }
  }

  for (size_t i = 0; i < msg->_att.size(); i++)
  {
    if (strcmpP(msg->_att[i].descr.transfer_encoding, 0, esp_mail_str_166))
    {
      msg->_att[i]._int.binary = true;
    }
  }
}

bool ESP_Mail_Client::sendBlob(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att)
{
  if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0)
  {
    if (!sendBase64(smtp, msg, (const unsigned char *)att->blob.data, att->blob.size, att->_int.flash_blob, att->descr.filename, smtp->_sendCallback != NULL))
      return false;
    return true;
  }
  else
  {
    if (att->blob.size > 0)
    {
      size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE;
      size_t writeLen = 0;
      uint8_t *buf = new uint8_t[chunkSize];
      int pg = 0, _pg = 0;
      while (writeLen < att->blob.size)
      {
        if (writeLen > att->blob.size - chunkSize)
          chunkSize = att->blob.size - writeLen;

        if (!bdat(smtp, msg, chunkSize, false))
          break;
        memcpy_P(buf, att->blob.data, chunkSize);
        if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
          break;

        if (smtp->_sendCallback)
        {
          pg = (float)(100.0f * writeLen / att->blob.size);
          if (pg != _pg)
            uploadReport(att->descr.filename, pg);
          _pg = pg;
        }
        writeLen += chunkSize;
      }
      delete[] buf;
      if (smtp->_sendCallback && _pg < 100)
        uploadReport(att->descr.filename, 100);

      return writeLen >= att->blob.size;
    }
  }
  return false;
}

bool ESP_Mail_Client::sendFile(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file)
{
  if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0)
  {
    if (!sendBase64Stream(smtp, msg, file, att->descr.filename, smtp->_sendCallback != NULL))
      return false;
    return true;
  }
  else
  {
    if (file.size() > 0)
    {
      size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE;
      size_t writeLen = 0;
      if (file.size() < chunkSize)
        chunkSize = file.size();
      uint8_t *buf = new uint8_t[chunkSize];
      int pg = 0, _pg = 0;
      while (writeLen < file.size() && file.available())
      {
        if (writeLen > file.size() - chunkSize)
          chunkSize = file.size() - writeLen;
        size_t readLen = file.read(buf, chunkSize);
        if (readLen != chunkSize)
        {
          errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
          break;
        }

        if (!bdat(smtp, msg, chunkSize, false))
          break;

        if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
          break;

        if (smtp->_sendCallback)
        {
          pg = (float)(100.0f * writeLen / file.size());
          if (pg != _pg)
            uploadReport(att->descr.filename, pg);
          _pg = pg;
        }
        writeLen += chunkSize;
      }
      delete[] buf;
      if (smtp->_sendCallback && _pg < 100)
        uploadReport(att->descr.filename, 100);
      return writeLen == file.size();
    }
    return false;
  }
  return false;
}

bool ESP_Mail_Client::sendParallelAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary)
{
  if (msg->_parallel.size() == 0)
    return true;

  std::string buf;
  std::string parallel = getBoundary(15);
  appendP(buf, esp_mail_str_33, true);
  buf += boundary;
  appendP(buf, esp_mail_str_34, false);

  appendP(buf, esp_mail_str_28, false);
  buf += parallel;
  appendP(buf, esp_mail_str_35, false);

  if (!bdat(smtp, msg, buf.length(), false))
    return false;

  if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  if (!sendAttachments(smtp, msg, parallel, true))
    return setSendingResult(smtp, msg, false);

  appendP(buf, esp_mail_str_33, true);
  buf += parallel;
  appendP(buf, esp_mail_str_33, false);

  if (!bdat(smtp, msg, buf.length(), false))
    return false;

  if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return setSendingResult(smtp, msg, false);

  return true;
}

bool ESP_Mail_Client::sendAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool parallel)
{
  std::string s;
  std::string buf;
  int cnt = 0;

  SMTP_Attachment *att = nullptr;

  size_t sz = msg->_att.size();
  if (parallel)
    sz = msg->_parallel.size();

  for (size_t i = 0; i < sz; i++)
  {
    if (parallel)
      att = &msg->_parallel[i];
    else
      att = &msg->_att[i];

    if (att->_int.att_type == esp_mail_att_type_attachment)
    {
      appendP(s, esp_mail_str_261, true);
      s += att->descr.filename;

      if (smtp->_sendCallback)
      {
        if (cnt > 0)
          smtpCB(smtp, "");
        smtpCB(smtp, att->descr.filename);
      }

      if (smtp->_debug)
        esp_mail_debug(s.c_str());

      cnt++;

      if (att->file.storage_type == esp_mail_file_storage_type_none)
      {
        if (!att->blob.data)
          continue;

        if (smtp->_sendCallback)
          smtpCB(smtp, att->descr.filename);

        if (smtp->_debug)
          esp_mail_debug(s.c_str());

        buf.clear();
        getAttachHeader(buf, boundary, att, att->blob.size);

        if (!bdat(smtp, msg, buf.length(), false))
          return false;

        if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
          return false;

        if (!sendBlob(smtp, msg, att))
          return false;

        if (!bdat(smtp, msg, 2, false))
          return false;

        if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
          return false;
      }
      else
      {

        if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd)
          _sdOk = sdTest();

        if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)
#if defined(ESP32)
          _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
          _flashOk = ESP_MAIL_FLASH_FS.begin();
#endif

        if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash))
        {

          if (smtp->_sendCallback)
            debugInfoP(esp_mail_str_158);

          if (smtp->_debug)
          {
            std::string e;
            appendP(e, esp_mail_str_185, true);
            appendP(e, esp_mail_str_158, false);
            esp_mail_debug(e.c_str());
          }

          continue;
        }

        if (openFileRead(smtp, msg, att, file, s, buf, boundary, false))
        {
          if (file)
          {

            if (!sendFile(smtp, msg, att, file))
              return false;

            if (!bdat(smtp, msg, 2, false))
              return false;

            if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
              return false;
          }
        }
      }
    }
  }
  return true;
}

bool ESP_Mail_Client::openFileRead(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file, std::string &s, std::string &buf, const std::string &boundary, bool inlined)
{
  bool file_existed = false;
  std::string filepath;

  if (strlen(att->file.path) > 0)
  {
    if (att->file.path[0] != '/')
      appendP(filepath, esp_mail_str_202, true);
    filepath += att->file.path;
  }

  if (att->file.storage_type == esp_mail_file_storage_type_sd) {
    file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str());
  } else if (att->file.storage_type == esp_mail_file_storage_type_flash) {
    file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str());
  } else if (att->file.storage_type == esp_mail_file_storage_type_univ) {
    file_existed = ufsp->exists(filepath.c_str());
  }

  if (!file_existed)
  {

    if (strlen(att->descr.filename) > 0)
    {
      filepath.clear();
      if (att->descr.filename[0] != '/')
        appendP(filepath, esp_mail_str_202, true);
      filepath += att->descr.filename;
    }

    if (att->file.storage_type == esp_mail_file_storage_type_sd) {
      file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str());
    } else if (att->file.storage_type == esp_mail_file_storage_type_flash) {
      file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str());
    } else if (att->file.storage_type == esp_mail_file_storage_type_univ) {
      file_existed = ufsp->exists(filepath.c_str());
    }
  }

  if (!file_existed)
  {
    if (smtp->_sendCallback)
      debugInfoP(esp_mail_str_158);

    if (smtp->_debug)
    {
      std::string e;
      appendP(e, esp_mail_str_185, true);
      appendP(e, esp_mail_str_158, false);
      esp_mail_debug(e.c_str());
    }
  }

  if (file_existed)
  {

    buf.clear();

    if (att->file.storage_type == esp_mail_file_storage_type_sd) {
      file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ);
    } else if (att->file.storage_type == esp_mail_file_storage_type_flash) {
#if defined(ESP32)
      file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ);
#elif defined(ESP8266)
      file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r");
#endif
    } else if (att->file.storage_type == esp_mail_file_storage_type_univ) {
      file = ufsp->open(filepath.c_str(), "r");
    }

    if (!file)
      return false;

    if (inlined)
      getInlineHeader(buf, boundary, att, file.size());
    else
      getAttachHeader(buf, boundary, att, file.size());

    if (!bdat(smtp, msg, buf.length(), false))
      return false;

    if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    return true;
  }

  return false;
}

bool ESP_Mail_Client::openFileRead2(SMTPSession *smtp, SMTP_Message *msg, File &file, const char *path, esp_mail_file_storage_type storageType)
{
  bool file_existed = false;
  std::string filepath;

  if (strlen(path) > 0)
  {
    if (path[0] != '/')
      appendP(filepath, esp_mail_str_202, true);
    filepath += path;
  }

  if (storageType == esp_mail_file_storage_type_sd) {
    file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str());
  } else if (storageType == esp_mail_file_storage_type_flash) {
    file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str());
  } else if (storageType == esp_mail_file_storage_type_univ) {
    file_existed = ufsp->exists(filepath.c_str());
  }

  if (!file_existed)
  {
    if (smtp->_sendCallback)
      debugInfoP(esp_mail_str_158);

    if (smtp->_debug)
    {
      std::string e;
      appendP(e, esp_mail_str_185, true);
      appendP(e, esp_mail_str_158, false);
      esp_mail_debug(e.c_str());
    }
  }

  if (file_existed)
  {
    if (storageType == esp_mail_file_storage_type_sd)
      file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ);
    else if (storageType == esp_mail_file_storage_type_flash)
#if defined(ESP32)
      file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ);
#elif defined(ESP8266)
      file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r");
#endif
    return true;
  }

  return false;
}

bool ESP_Mail_Client::sendInline(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, byte type)
{
  size_t num = numAtt(smtp, esp_mail_att_type_inline, msg) > 0;

  if (num > 0)
  {
    if (smtp->_sendCallback)
    {
      smtpCB(smtp, "");
      smtpCBP(smtp, esp_mail_str_167);
    }

    if (smtp->_debug)
      debugInfoP(esp_mail_str_271);
  }

  std::string s;
  std::string buf;
  std::string related = getBoundary(15);
  int cnt = 0;
  SMTP_Attachment *att = nullptr;

  appendP(s, esp_mail_str_33, true);
  s += boundary;
  appendP(s, esp_mail_str_34, false);

  appendP(s, esp_mail_str_298, false);
  s += related;
  appendP(s, esp_mail_str_35, false);

  if (!bdat(smtp, msg, s.length(), false))
    return false;

  if (smtpSend(smtp, s.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  if (!sendPartText(smtp, msg, type, related.c_str()))
    return false;

  if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_inline, msg) > 0)
    esp_mail_debug("");

  if (num > 0)
  {
    for (uint8_t i = 0; i < msg->_att.size(); i++)
    {
      att = &msg->_att[i];
      if (att->_int.att_type == esp_mail_att_type_inline)
      {
        appendP(s, esp_mail_str_261, true);
        s += att->descr.filename;

        if (smtp->_sendCallback)
        {
          if (cnt > 0)
            smtpCB(smtp, "");
          smtpCB(smtp, att->descr.filename);
        }

        if (smtp->_debug)
          esp_mail_debug(s.c_str());

        cnt++;

        if (att->file.storage_type == esp_mail_file_storage_type_none)
        {
          if (!att->blob.data)
            continue;

          if (smtp->_sendCallback)
            smtpCB(smtp, att->descr.filename);

          if (smtp->_debug)
            esp_mail_debug(s.c_str());

          buf.clear();
          getInlineHeader(buf, related, att, att->blob.size);

          if (!bdat(smtp, msg, buf.length(), false))
            return false;

          if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
            return false;

          if (!sendBlob(smtp, msg, att))
            return false;

          if (!bdat(smtp, msg, 2, false))
            return false;

          if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
            return false;
        }
        else
        {
          if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd)
            _sdOk = sdTest();

          if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)
#if defined(ESP32)
            _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
            _flashOk = ESP_MAIL_FLASH_FS.begin();
#endif

          if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash))
          {

            if (smtp->_sendCallback)
              debugInfoP(esp_mail_str_158);

            if (smtp->_debug)
            {
              std::string e;
              appendP(e, esp_mail_str_185, true);
              appendP(e, esp_mail_str_158, false);
              esp_mail_debug(e.c_str());
            }

            continue;
          }

          if (openFileRead(smtp, msg, att, file, s, buf, related, true))
          {
            if (!file)
            {
              errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
              return false;
            }

            if (!sendFile(smtp, msg, att, file))
              return false;

            if (!bdat(smtp, msg, 2, false))
              return false;

            if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
              return false;
          }
        }
      }
    }
  }

  appendP(s, esp_mail_str_34, true);
  appendP(s, esp_mail_str_33, false);
  s += related;
  appendP(s, esp_mail_str_33, false);
  appendP(s, esp_mail_str_34, false);

  if (!bdat(smtp, msg, s.length(), false))
    return false;

  if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  return true;
}

void ESP_Mail_Client::errorStatusCB(SMTPSession *smtp, int error)
{
  smtp->_smtpStatus.statusCode = error;
  std::string s;

  if (smtp->_sendCallback)
  {
    appendP(s, esp_mail_str_53, true);
    s += smtp->errorReason().c_str();
    smtpCB(smtp, s.c_str(), false);
  }

  if (smtp->_debug)
  {
    appendP(s, esp_mail_str_185, true);
    s += smtp->errorReason().c_str();
    esp_mail_debug(s.c_str());
  }
}

size_t ESP_Mail_Client::smtpSendP(SMTPSession *smtp, PGM_P v, bool newline)
{
  if (!reconnect(smtp))
  {
    closeTCP(smtp);
    return 0;
  }

  if (!connected(smtp))
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!smtp->_tcpConnected)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  char *tmp = strP(v);
  size_t len = 0;

  if (newline)
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(tmp);
    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_println(tmp);
    else
      len = smtp->httpClient.stream()->println(tmp);
  }
  else
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(tmp, false);

    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_print(tmp);
    else
      len = smtp->httpClient.stream()->print(tmp);
  }

  if (len != strlen(tmp) && len != strlen(tmp) + 2)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  delS(tmp);

  return len;
}

size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, const char *data, bool newline)
{
  if (!reconnect(smtp))
  {
    closeTCP(smtp);
    return 0;
  }

  if (!connected(smtp))
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!smtp->_tcpConnected)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  size_t len = 0;

  if (newline)
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(data);

    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_println(data);
    else
      len = smtp->httpClient.stream()->println(data);
  }
  else
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(data, false);

    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_print(data);
    else
      len = smtp->httpClient.stream()->print(data);
  }

  if (len != strlen(data) && len != strlen(data) + 2)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  return len;
}

size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, int data, bool newline)
{
  if (!reconnect(smtp))
  {
    closeTCP(smtp);
    return 0;
  }

  if (!connected(smtp))
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!smtp->_tcpConnected)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  char *tmp = intStr(data);
  size_t len = 0;

  if (newline)
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug(tmp);

    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_println(tmp);
    else
      len = smtp->httpClient.stream()->println(tmp);
  }
  else
  {
    if (smtp->_debugLevel > esp_mail_debug_level_2)
      esp_mail_debug_line(tmp, false);

    if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
      len = smtp->httpClient._ns_print(tmp);
    else
      len = smtp->httpClient.stream()->print(tmp);
  }

  if (len != strlen(tmp) && len != strlen(tmp) + 2)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  delS(tmp);

  return len;
}

size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, uint8_t *data, size_t size)
{
  if (!reconnect(smtp))
  {
    closeTCP(smtp);
    return 0;
  }

  if (!connected(smtp))
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
    return 0;
  }

  if (!smtp->_tcpConnected)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    return 0;
  }

  size_t len = 0;

  if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
    len = smtp->httpClient._stream()->write(data, size);
  else
    len = smtp->httpClient.stream()->write(data, size);

  if (len != size)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED);
    len = 0;
  }

  return len;
}

bool ESP_Mail_Client::handleSMTPError(SMTPSession *smtp, int err, bool ret)
{
  if (err < 0)
    errorStatusCB(smtp, err);

  if (smtp->_tcpConnected)
    closeTCP(smtp);

  return ret;
}

void ESP_Mail_Client::debugInfoP(PGM_P info)
{
  char *tmp = strP(info);
  esp_mail_debug(tmp);
  delS(tmp);
}

bool ESP_Mail_Client::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss)
{
  _sck = sck;
  _miso = miso;
  _mosi = mosi;
  _ss = ss;
  _sdConfigSet = true;
#if defined(ESP32)
#if defined(CARD_TYPE_SD)
  SPI.begin(_sck, _miso, _mosi, _ss);
  return ESP_MAIL_SD_FS.begin(_ss, SPI);
#endif
#elif defined(ESP8266)
  return ESP_MAIL_SD_FS.begin(_ss);
#endif
  return false;
}

bool ESP_Mail_Client::sdBegin(void)
{
  _sdConfigSet = false;
#if defined(ESP32)
  return ESP_MAIL_SD_FS.begin();
#elif defined(ESP8266)
  return ESP_MAIL_SD_FS.begin(SD_CS_PIN);
#endif
  return false;
}

bool ESP_Mail_Client::sdMMCBegin(const char *mountpoint, bool mode1bit, bool format_if_mount_failed)
{
#if defined(ESP32)
#if defined(CARD_TYPE_SD_MMC)
  _sdConfigSet = true;
  sd_mmc_mountpoint = mountpoint;
  sd_mmc_mode1bit = mode1bit;
  sd_mmc_format_if_mount_failed = format_if_mount_failed;
  return ESP_MAIL_SD_FS.begin(mountpoint, mode1bit, format_if_mount_failed);
#endif
#endif
  return false;
}

bool ESP_Mail_Client::sendPartText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, const char *boundary)
{
  std::string header;

  if (strlen(boundary) > 0)
  {
    appendP(header, esp_mail_str_33, false);
    header += boundary;
    appendP(header, esp_mail_str_34, false);
  }

  if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)
  {
    if (strlen(msg->text.content_type) > 0)
    {
      appendP(header, esp_mail_str_25, false);
      header += msg->text.content_type;

      if (strlen(msg->text.charSet) > 0)
      {
        appendP(header, esp_mail_str_97, false);
        appendP(header, esp_mail_str_131, false);
        appendP(header, esp_mail_str_168, false);
        header += msg->text.charSet;
        appendP(header, esp_mail_str_136, false);
      }

      if (msg->text.flowed)
      {
        appendP(header, esp_mail_str_97, false);
        appendP(header, esp_mail_str_131, false);
        appendP(header, esp_mail_str_270, false);

        appendP(header, esp_mail_str_97, false);
        appendP(header, esp_mail_str_131, false);
        appendP(header, esp_mail_str_110, false);
      }

      if (msg->text.embed.enable)
      {
        appendP(header, esp_mail_str_26, false);
        appendP(header, esp_mail_str_164, false);
        appendP(header, esp_mail_str_136, false);
        char *tmp = getUID();
        msg->text._int.cid = tmp;
        delS(tmp);
      }

      appendP(header, esp_mail_str_34, false);
    }

    if (strlen(msg->text.transfer_encoding) > 0)
    {
      appendP(header, esp_mail_str_272, false);
      header += msg->text.transfer_encoding;
      appendP(header, esp_mail_str_34, false);
    }
  }
  else if (type == esp_mail_msg_type_html)
  {
    if (strlen(msg->text.content_type) > 0)
    {
      appendP(header, esp_mail_str_25, false);
      header += msg->html.content_type;

      if (strlen(msg->html.charSet) > 0)
      {
        appendP(header, esp_mail_str_97, false);
        appendP(header, esp_mail_str_131, false);
        appendP(header, esp_mail_str_168, false);
        header += msg->html.charSet;
        appendP(header, esp_mail_str_136, false);
      }
      if (msg->html.embed.enable)
      {
        appendP(header, esp_mail_str_26, false);
        appendP(header, esp_mail_str_159, false);
        appendP(header, esp_mail_str_136, false);
        char *tmp = getUID();
        msg->html._int.cid = tmp;
        delS(tmp);
      }
      appendP(header, esp_mail_str_34, false);
    }

    if (strlen(msg->html.transfer_encoding) > 0)
    {
      appendP(header, esp_mail_str_272, false);
      header += msg->html.transfer_encoding;
      appendP(header, esp_mail_str_34, false);
    }
  }

  if ((type == esp_mail_msg_type_html && msg->html.embed.enable) || ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable))
  {

    if ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable)
    {
      if (msg->text.embed.type == esp_mail_smtp_embed_message_type_attachment)
        appendP(header, esp_mail_str_30, false);
      else if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline)
        appendP(header, esp_mail_str_299, false);

      if (strlen(msg->text.embed.filename) > 0)
        header += msg->text.embed.filename;
      else
        appendP(header, esp_mail_str_164, false);
      appendP(header, esp_mail_str_36, false);

      if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline)
      {
        appendP(header, esp_mail_str_300, false);
        if (strlen(msg->text.embed.filename) > 0)
          header += msg->text.embed.filename;
        else
          appendP(header, esp_mail_str_159, false);
        appendP(header, esp_mail_str_34, false);

        appendP(header, esp_mail_str_301, false);
        header += msg->text._int.cid;
        appendP(header, esp_mail_str_15, false);
        appendP(header, esp_mail_str_34, false);
      }
    }
    else if (type == esp_mail_msg_type_html && msg->html.embed.enable)
    {
      if (msg->html.embed.type == esp_mail_smtp_embed_message_type_attachment)
        appendP(header, esp_mail_str_30, false);
      else if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline)
        appendP(header, esp_mail_str_299, false);

      if (strlen(msg->html.embed.filename) > 0)
        header += msg->html.embed.filename;
      else
        appendP(header, esp_mail_str_159, false);
      appendP(header, esp_mail_str_36, false);

      if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline)
      {
        appendP(header, esp_mail_str_300, false);
        if (strlen(msg->html.embed.filename) > 0)
          header += msg->html.embed.filename;
        else
          appendP(header, esp_mail_str_159, false);
        appendP(header, esp_mail_str_34, false);

        appendP(header, esp_mail_str_301, false);
        header += msg->html._int.cid;
        appendP(header, esp_mail_str_15, false);
        appendP(header, esp_mail_str_34, false);
      }
    }
  }

  appendP(header, esp_mail_str_34, false);

  if ((msg->text.blob.size > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (msg->html.blob.size > 0 && type == esp_mail_msg_type_html))
  {
    if (!bdat(smtp, msg, header.length(), false))
      return false;

    if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    header.clear();

    if (!sendBlobBody(smtp, msg, type))
      return false;
  }
  else if ((strlen(msg->text.file.name) > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (strlen(msg->html.file.name) > 0 && type == esp_mail_msg_type_html))
  {
    if (!bdat(smtp, msg, header.length(), false))
      return false;

    if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    header.clear();

    if (!sendFileBody(smtp, msg, type))
      return false;
  }
  else
    encodingText(smtp, msg, type, header);

  appendP(header, esp_mail_str_34, false);

  if (strlen(boundary) > 0)
    appendP(header, esp_mail_str_34, false);

  if (!bdat(smtp, msg, header.length(), false))
    return false;

  if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  return true;
}

char *ESP_Mail_Client::getUID()
{
  char *tmp = new char[36];
  memset(tmp, 0, 36);
  itoa(random(10000000, 20000000), tmp, 10);
  return tmp;
}

bool ESP_Mail_Client::sendBlobBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type)
{

  if (msg->text.blob.size == 0 && msg->html.blob.size == 0)
    return true;

  bool ret = true;
  int bufLen = 512;
  size_t pos = 0;
  int pg = 0, _pg = 0;

  if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)
  {
    char *tmp = strP(esp_mail_str_325);

    if (strlen(msg->text.transfer_encoding) > 0)
    {
      if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
      {
        ret = sendBase64(smtp, msg, (const unsigned char *)msg->text.blob.data, msg->text.blob.size, true, tmp, smtp->_sendCallback != NULL);
        delS(tmp);
        return ret;
      }
    }

    int len = msg->text.blob.size;
    int available = len;
    uint8_t *buf = new uint8_t[bufLen + 1];
    while (available)
    {
      if (available > bufLen)
        available = bufLen;

      memcpy_P(buf, msg->text.blob.data + pos, available);

      if (!bdat(smtp, msg, available, false))
        break;
      if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        break;
      pos += available;
      len -= available;
      available = len;
      if (smtp->_sendCallback)
      {
        pg = (float)(100.0f * pos / msg->text.blob.size);
        if (pg != _pg)
          uploadReport(tmp, pg);
        _pg = pg;
      }
    }
    delete[] buf;
    delS(tmp);
  }
  else if (type == esp_mail_message_type::esp_mail_msg_type_html)
  {
    char *tmp = strP(esp_mail_str_325);

    if (strlen(msg->html.transfer_encoding) > 0)
    {
      if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
      {
        ret = sendBase64(smtp, msg, (const unsigned char *)msg->html.blob.data, msg->html.blob.size, true, tmp, smtp->_sendCallback != NULL);
        delS(tmp);
        return ret;
      }
    }
    int len = msg->html.blob.size;
    int available = len;
    uint8_t *buf = new uint8_t[bufLen + 1];
    while (available)
    {

      if (available > bufLen)
        available = bufLen;

      memcpy_P(buf, msg->html.blob.data + pos, available);

      if (!bdat(smtp, msg, available, false))
      {
        ret = false;
        break;
      }

      if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      {
        ret = false;
        break;
      }
      pos += available;
      len -= available;
      available = len;
      if (smtp->_sendCallback)
      {
        pg = (float)(100.0f * pos / msg->html.blob.size);
        if (pg != _pg)
          uploadReport(tmp, pg);
        _pg = pg;
      }
    }
    delete[] buf;
    delS(tmp);
  }
  return ret;
}

bool ESP_Mail_Client::sendFileBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type)
{

  if (strlen(msg->text.file.name) == 0 && strlen(msg->html.file.name) == 0)
    return true;

  bool ret = true;
  size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE;
  size_t writeLen = 0;
  int pg = 0, _pg = 0;

  if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)
  {

    if (!openFileRead2(smtp, msg, file, msg->text.file.name, msg->text.file.type))
      return false;

    char *tmp = strP(esp_mail_str_326);

    if (strlen(msg->text.transfer_encoding) > 0)
    {
      if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
      {
        ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL);
        delS(tmp);
        return ret;
      }
    }

    if (file.size() > 0)
    {

      if (file.size() < chunkSize)
        chunkSize = file.size();
      uint8_t *buf = new uint8_t[chunkSize];
      while (writeLen < file.size() && file.available())
      {
        if (writeLen > file.size() - chunkSize)
          chunkSize = file.size() - writeLen;
        size_t readLen = file.read(buf, chunkSize);

        if (readLen != chunkSize)
        {
          errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
          break;
        }

        if (!bdat(smtp, msg, chunkSize, false))
        {
          ret = false;
          break;
        }

        if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        {
          ret = false;
          break;
        }

        if (smtp->_sendCallback)
        {
          pg = (float)(100.0f * writeLen / file.size());
          if (pg != _pg)
            uploadReport(tmp, pg);
          _pg = pg;
        }
        writeLen += chunkSize;
      }
      delete[] buf;
      if (smtp->_sendCallback && _pg < 100)
        uploadReport(tmp, 100);

      delS(tmp);
      return ret && writeLen == file.size();
    }
  }
  else if (type == esp_mail_message_type::esp_mail_msg_type_html)
  {

    if (!openFileRead2(smtp, msg, file, msg->html.file.name, msg->html.file.type))
      return false;

    char *tmp = strP(esp_mail_str_326);

    if (strlen(msg->html.transfer_encoding) > 0)
    {
      if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
      {
        ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL);
        delS(tmp);
        return ret;
      }
    }

    if (file.size() > 0)
    {

      if (file.size() < chunkSize)
        chunkSize = file.size();
      uint8_t *buf = new uint8_t[chunkSize];
      while (writeLen < file.size() && file.available())
      {
        if (writeLen > file.size() - chunkSize)
          chunkSize = file.size() - writeLen;
        size_t readLen = file.read(buf, chunkSize);

        if (readLen != chunkSize)
        {
          errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
          break;
        }

        if (!bdat(smtp, msg, chunkSize, false))
        {
          ret = false;
          break;
        }

        if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        {
          ret = false;
          break;
        }

        if (smtp->_sendCallback)
        {
          pg = (float)(100.0f * writeLen / file.size());
          if (pg != _pg)
            uploadReport(tmp, pg);
          _pg = pg;
        }
        writeLen += chunkSize;
      }
      delete[] buf;
      if (smtp->_sendCallback && _pg < 100)
        uploadReport(tmp, 100);

      delS(tmp);
      return ret && writeLen == file.size();
    }
  }

  return false;
}

void ESP_Mail_Client::encodingText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, std::string &content)
{
  if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)
  {
    std::string s = msg->text.content;

    if (msg->text.flowed)
      formatFlowedText(s);

    if (strlen(msg->text.transfer_encoding) > 0)
    {
      if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
        content += encodeBase64Str((const unsigned char *)s.c_str(), s.length());
      else if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0)
      {
        char *out = newS(s.length() * 3 + 1);
        encodeQP(s.c_str(), out);
        content += out;
        delS(out);
      }
      else
        content += s;
    }
    else
      content += s;
  }
  else if (type == esp_mail_message_type::esp_mail_msg_type_html)
  {
    char *tmp = nullptr;
    std::string s = msg->html.content;
    std::string fnd, rep;
    SMTP_Attachment *att = nullptr;
    for (uint8_t i = 0; i < msg->_att.size(); i++)
    {
      att = &msg->_att[i];
      if (att->_int.att_type == esp_mail_att_type_inline)
      {
        std::string filename(att->descr.filename);

        size_t found = filename.find_last_of("/\\");
        if (found != std::string::npos)
          filename = filename.substr(found + 1);

        appendP(fnd, esp_mail_str_136, true);
        fnd += filename;
        appendP(fnd, esp_mail_str_136, false);

        appendP(rep, esp_mail_str_136, true);
        appendP(rep, esp_mail_str_302, false);
        if (strlen(att->descr.content_id) > 0)
          rep += att->descr.content_id;
        else
          rep += att->_int.cid;
        appendP(rep, esp_mail_str_136, false);

        tmp = strReplace((char *)s.c_str(), (char *)fnd.c_str(), (char *)rep.c_str());
        s = tmp;
        delS(tmp);
      }
    }

    if (strlen(msg->html.transfer_encoding) > 0)
    {
      if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0)
        content += encodeBase64Str((const unsigned char *)s.c_str(), s.length());
      else if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0)
      {
        char *out = newS(strlen(msg->html.content) * 3 + 1);
        encodeQP(msg->html.content, out);
        content += out;
        delS(out);
      }
      else
        content += s;
    }
    else
      content += s;
    std::string().swap(s);
  }
}

/* Safe string splitter to avoid strsep bugs*/
void ESP_Mail_Client::splitTk(std::string &str, std::vector<std::string> &tk, const char *delim)
{
  std::size_t current, previous = 0;
  current = str.find(delim, previous);
  std::string s;
  while (current != std::string::npos)
  {
    s = str.substr(previous, current - previous);
    tk.push_back(s);
    previous = current + strlen(delim);
    current = str.find(delim, previous);
  }
  s = str.substr(previous, current - previous);
  tk.push_back(s);
  std::string().swap(s);
}

/** Add the soft line break to the long text line (rfc 3676)
 * and add Format=flowed parameter in the plain text content-type header.
 * We use the existing white space as a part of this soft line break
 * and set delSp="no" parameter to the header.
 *
 * Some servers are not rfc 3676 compliant.
 * This causes the text lines are wrapped instead of joined.
 *
 * Some mail clients trim the space before the line break
 * which makes the soft line break cannot be seen.
*/
void ESP_Mail_Client::formatFlowedText(std::string &content)
{
  int count = 0;
  std::string qms;
  int j = 0;
  std::vector<std::string> tokens = std::vector<std::string>();
  char *stk = strP(esp_mail_str_34);
  char *qm = strP(esp_mail_str_15);
  splitTk(content, tokens, stk);
  content.clear();
  for (size_t i = 0; i < tokens.size(); i++)
  {
    if (tokens[i].length() > 0)
    {
      j = 0;
      qms.clear();
      while (tokens[i][j] == qm[0])
      {
        qms += qm;
        j++;
      }
      softBreak(tokens[i], qms.c_str());
      if (count > 0)
        content += stk;
      content += tokens[i];
    }
    else if (count > 0)
      content += stk;
    count++;
  }

  delS(stk);
  delS(qm);
  tokens.clear();
}

void ESP_Mail_Client::softBreak(std::string &content, const char *quoteMarks)
{
  size_t len = 0;
  char *stk = strP(esp_mail_str_131);
  std::vector<std::string> tokens = std::vector<std::string>();
  splitTk(content, tokens, stk);
  content.clear();
  for (size_t i = 0; i < tokens.size(); i++)
  {
    if (tokens[i].length() > 0)
    {
      if (len + tokens[i].length() + 3 > FLOWED_TEXT_LEN)
      {
        /* insert soft crlf */
        content += stk;
        appendP(content, esp_mail_str_34, false);

        /* insert quote marks */
        if (strlen(quoteMarks) > 0)
          content += quoteMarks;
        content += tokens[i];
        len = tokens[i].length();
      }
      else
      {
        if (len > 0)
        {
          content += stk;
          len += strlen(stk);
        }
        content += tokens[i];
        len += tokens[i].length();
      }
    }
  }
  delS(stk);
  tokens.clear();
}

bool ESP_Mail_Client::sendMSG(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary)
{
  std::string alt = getBoundary(15);
  std::string s;

  if (numAtt(smtp, esp_mail_att_type_inline, msg) > 0)
  {
    appendP(s, esp_mail_str_297, true);
    s += alt;
    appendP(s, esp_mail_str_35, false);

    if (!bdat(smtp, msg, s.length(), false))
      return false;

    if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html)
    {
      if (!sendInline(smtp, msg, alt, msg->type))
        return false;
    }
    else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain))
    {
      if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str()))
        return false;
      if (!sendInline(smtp, msg, alt, esp_mail_msg_type_html))
        return false;
    }

    appendP(s, esp_mail_str_33, true);
    s += alt;
    appendP(s, esp_mail_str_33, false);
    appendP(s, esp_mail_str_34, false);

    if (!bdat(smtp, msg, s.length(), false))
      return false;

    if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;
  }
  else
  {
    if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html)
    {
      if (!sendPartText(smtp, msg, msg->type, ""))
        return false;
    }
    else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain))
    {
      appendP(s, esp_mail_str_33, true);
      s += boundary;
      appendP(s, esp_mail_str_34, false);
      appendP(s, esp_mail_str_297, false);
      s += alt;
      appendP(s, esp_mail_str_35, false);

      if (!bdat(smtp, msg, s.length(), false))
        return false;

      if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str()))
        return false;

      if (!sendPartText(smtp, msg, esp_mail_msg_type_html, alt.c_str()))
        return false;
    }
  }
  return true;
}

void ESP_Mail_Client::getInlineHeader(std::string &header, const std::string &boundary, SMTP_Attachment *inlineAttach, size_t size)
{
  appendP(header, esp_mail_str_33, false);
  header += boundary;
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_25, false);

  if (strlen(inlineAttach->descr.mime) == 0)
  {
    std::string mime;
    mimeFromFile(inlineAttach->descr.filename, mime);
    if (mime.length() > 0)
      header += mime;
    else
      appendP(header, esp_mail_str_32, false);
  }
  else
    header += inlineAttach->descr.mime;

  appendP(header, esp_mail_str_26, false);

  std::string filename = inlineAttach->descr.filename;

  size_t found = filename.find_last_of("/\\");

  if (found != std::string::npos)
    filename = filename.substr(found + 1);

  header += filename;
  appendP(header, esp_mail_str_36, false);

  appendP(header, esp_mail_str_299, false);
  header += filename;
  appendP(header, esp_mail_str_327, false);
  char *tmp = intStr(size);
  header += tmp;
  delS(tmp);
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_300, false);
  header += filename;
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_301, false);
  if (strlen(inlineAttach->descr.content_id) > 0)
    header += inlineAttach->descr.content_id;
  else
    header += inlineAttach->_int.cid;

  appendP(header, esp_mail_str_15, false);

  appendP(header, esp_mail_str_34, false);

  if (strlen(inlineAttach->descr.transfer_encoding) > 0)
  {
    appendP(header, esp_mail_str_272, false);
    header += inlineAttach->descr.transfer_encoding;
    appendP(header, esp_mail_str_34, false);
  }
  appendP(header, esp_mail_str_34, false);

  std::string().swap(filename);
}

void ESP_Mail_Client::getAttachHeader(std::string &header, const std::string &boundary, SMTP_Attachment *attach, size_t size)
{
  appendP(header, esp_mail_str_33, false);
  header += boundary;
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_25, false);

  if (strlen(attach->descr.mime) == 0)
  {
    std::string mime;
    mimeFromFile(attach->descr.filename, mime);
    if (mime.length() > 0)
      header += mime;
    else
      appendP(header, esp_mail_str_32, false);
  }
  else
    header += attach->descr.mime;

  appendP(header, esp_mail_str_26, false);

  std::string filename = attach->descr.filename;

  size_t found = filename.find_last_of("/\\");
  if (found != std::string::npos)
    filename = filename.substr(found + 1);

  header += filename;
  appendP(header, esp_mail_str_36, false);

  if (!attach->_int.parallel)
  {
    appendP(header, esp_mail_str_30, false);
    header += filename;
    appendP(header, esp_mail_str_327, false);
    char *tmp = intStr(size);
    header += tmp;
    delS(tmp);
    appendP(header, esp_mail_str_34, false);
  }

  if (strlen(attach->descr.transfer_encoding) > 0)
  {
    appendP(header, esp_mail_str_272, false);
    header += attach->descr.transfer_encoding;
    appendP(header, esp_mail_str_34, false);
  }

  appendP(header, esp_mail_str_34, false);

  std::string().swap(filename);
}

void ESP_Mail_Client::getRFC822PartHeader(SMTPSession *smtp, std::string &header, const std::string &boundary)
{
  appendP(header, esp_mail_str_33, false);
  header += boundary;
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_25, false);

  appendP(header, esp_mail_str_123, false);
  appendP(header, esp_mail_str_34, false);

  appendP(header, esp_mail_str_98, false);

  appendP(header, esp_mail_str_34, false);
}

void ESP_Mail_Client::smtpCBP(SMTPSession *smtp, PGM_P info, bool success)
{
  std::string s;
  appendP(s, info, true);
  smtp->_cbData._info = s;
  smtp->_cbData._success = success;
  smtp->_sendCallback(smtp->_cbData);
  std::string().swap(s);
}

void ESP_Mail_Client::smtpCB(SMTPSession *smtp, const char *info, bool success)
{
  smtp->_cbData._info = info;
  smtp->_cbData._success = success;
  smtp->_sendCallback(smtp->_cbData);
}

void ESP_Mail_Client::imapCBP(IMAPSession *imap, PGM_P info, bool success)
{
  char *tmp = strP(info);
  imap->_cbData._info = tmp;
  imap->_cbData._success = success;
  imap->_readCallback(imap->_cbData);
  delS(tmp);
}

void ESP_Mail_Client::imapCB(IMAPSession *imap, const char *info, bool success)
{
  imap->_cbData._info = info;
  imap->_cbData._success = success;
  imap->_readCallback(imap->_cbData);
}

void ESP_Mail_Client::strcat_c(char *str, char c)
{
  for (; *str; str++)
    ;
  *str++ = c;
  *str++ = 0;
}
int ESP_Mail_Client::strpos(const char *haystack, const char *needle, int offset)
{
  size_t len = strlen(haystack);
  size_t len2 = strlen(needle);
  if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len || offset < 0)
    return -1;
  char *_haystack = newS(len - offset + 1);
  _haystack[len - offset] = 0;
  strncpy(_haystack, haystack + offset, len - offset);
  char *p = stristr(_haystack, needle);
  int r = -1;
  if (p)
    r = p - _haystack + offset;
  delS(_haystack);
  return r;
}

char *ESP_Mail_Client::stristr(const char *str1, const char *str2)
{
  const char *p1 = str1;
  const char *p2 = str2;
  const char *r = *p2 == 0 ? str1 : 0;

  while (*p1 != 0 && *p2 != 0)
  {
    if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2))
    {
      if (r == 0)
        r = p1;
      p2++;
    }
    else
    {
      p2 = str2;
      if (r != 0)
        p1 = r + 1;

      if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2))
      {
        r = p1;
        p2++;
      }
      else
        r = 0;
    }

    p1++;
  }

  return *p2 == 0 ? (char *)r : 0;
}

char *ESP_Mail_Client::rstrstr(const char *haystack, const char *needle)
{
  size_t needle_length = strlen(needle);
  const char *haystack_end = haystack + strlen(haystack) - needle_length;
  const char *p;
  size_t i;
  for (p = haystack_end; p >= haystack; --p)
  {
    for (i = 0; i < needle_length; ++i)
    {
      if (p[i] != needle[i])
        goto next;
    }
    return (char *)p;
  next:;
  }
  return 0;
}

int ESP_Mail_Client::rstrpos(const char *haystack, const char *needle, int offset)
{
  size_t len = strlen(haystack);
  size_t len2 = strlen(needle);
  if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len)
    return -1;
  char *_haystack = newS(len - offset + 1);
  _haystack[len - offset] = 0;
  strncpy(_haystack, haystack + offset, len - offset);
  char *p = rstrstr(_haystack, needle);
  int r = -1;
  if (p)
    r = p - _haystack + offset;
  delS(_haystack);
  return r;
}

int ESP_Mail_Client::readLine(WiFiClient *stream, char *buf, int bufLen, bool crlf, int &count)
{
  int ret = -1;
  char c = 0;
  char _c = 0;
  int idx = 0;
  if (!stream)
    return idx;
  while (stream->available() && idx < bufLen)
  {
    ret = stream->read();
    if (ret > -1)
    {
      if (idx >= bufLen - 1)
        return idx;

      c = (char)ret;
      strcat_c(buf, c);
      idx++;
      count++;
      if (_c == '\r' && c == '\n')
      {
        if (!crlf)
        {
          buf[idx - 2] = 0;
          idx -= 2;
        }
        return idx;
      }
      _c = c;
    }
    if (!stream)
      return idx;
  }
  return idx;
}

#if defined(ESP32)
int ESP_Mail_Client::_readLine(ESP_Mail_WCS32 *stream, char *buf, int bufLen, bool crlf, int &count)
#elif defined(ESP8266)
int ESP_Mail_Client::_readLine(ESP_Mail::ESP_Mail_WCS *stream, char *buf, int bufLen, bool crlf, int &count)
#endif
{
  int ret = -1;
  char c = 0;
  char _c = 0;
  int idx = 0;
  if (!stream)
    return idx;
  while (stream->_ns_available() && idx < bufLen)
  {
    ret = stream->_ns_read();
    if (ret > -1)
    {
      if (idx >= bufLen - 1)
        return idx;

      c = (char)ret;
      strcat_c(buf, c);
      idx++;
      count++;
      if (_c == '\r' && c == '\n')
      {
        if (!crlf)
        {
          buf[idx - 2] = 0;
          idx -= 2;
        }
        return idx;
      }
      _c = c;
    }
    if (!stream)
      return idx;
  }
  return idx;
}

int ESP_Mail_Client::getMSGNUM(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, bool &endSearch, int &nump, const char *key, const char *pc)
{
  int ret = -1;
  char c = 0;
  int idx = 0;
  int num = 0;
  while (available(imap) > 0 && idx < bufLen)
  {
    delay(0);
    if (!imap->_secure)
      ret = imap->httpClient._stream()->read();
    else
      ret = imap->httpClient.stream()->read();

    if (ret > -1)
    {

      if (idx >= bufLen - 1)
        return idx;

      c = (char)ret;

      if (c == '\n')
        c = ' ';

      strcat_c(buf, c);
      idx++;

      if (chunkIdx == 0)
      {
        if (strcmp(buf, key) == 0)
        {
          chunkIdx++;
          return 0;
        }

        if (strposP(buf, esp_mail_imap_response_1, 0) > -1)
          goto end_search;
      }
      else
      {
        if (c == ' ')
        {
          imap->_mbif._searchCount++;
          if (imap->_config->enable.recent_sort)
          {
            imap->_msgNum.push_back(atoi(buf));
            if (imap->_msgNum.size() > imap->_config->limit.search)
              imap->_msgNum.erase(imap->_msgNum.begin());
          }
          else
          {
            if (imap->_msgNum.size() < imap->_config->limit.search)
              imap->_msgNum.push_back(atoi(buf));
          }

          if (imap->_debug)
          {
            num = (float)(100.0f * imap->_mbif._searchCount / imap->_mbif._msgCount);
            if (nump != num)
            {
              nump = num;
              searchReport(num, pc);
            }
          }

          chunkIdx++;
          return idx;
        }
        else if (c == '$')
        {
          if (imap->_config->enable.recent_sort)
            std::sort(imap->_msgNum.begin(), imap->_msgNum.end(), compFunc);

          goto end_search;
        }
      }
    }
  }

  return idx;

end_search:

  endSearch = true;
  int read = available(imap);

  if (!imap->_secure)
    idx = imap->httpClient._stream()->readBytes(buf + idx, read);
  else
    idx = imap->httpClient.stream()->readBytes(buf + idx, read);

  return idx;
}

struct esp_mail_message_part_info_t *ESP_Mail_Client::cPart(IMAPSession *imap)
{
  return &cHeader(imap)->part_headers[imap->_cPartIdx];
}

struct esp_mail_message_header_t *ESP_Mail_Client::cHeader(IMAPSession *imap)
{
  return &imap->_headers[cIdx(imap)];
}

void ESP_Mail_Client::handleHeader(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, struct esp_mail_message_header_t &header, int &headerState, int &octetCount)
{

  char *tmp = nullptr;
  if (chunkIdx == 0)
  {
    if (strposP(buf, esp_mail_str_324, 0) != -1 && buf[0] == '*')
      chunkIdx++;

    tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0);
    if (tmp)
    {
      octetCount = 2;
      header.header_data_len = atoi(tmp);
      delS(tmp);
    }
  }
  else
  {
    if (octetCount > header.header_data_len + 2)
      return;

    if (strcmpP(buf, 0, esp_mail_str_10))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_from;
      tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1);
      setHeader(imap, tmp, header, headerState);
      delS(tmp);
    }
    else if (strcmpP(buf, 0, esp_mail_str_11))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_to;
      tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1);
      setHeader(imap, tmp, header, headerState);
      delS(tmp);
    }
    else if (strcmpP(buf, 0, esp_mail_str_276))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_cc;
      tmp = subStr(buf, esp_mail_str_276, NULL, 0, -1);
      setHeader(imap, tmp, header, headerState);
      delS(tmp);
    }
    else if (strcmpP(buf, 0, esp_mail_str_279))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_subject;
      tmp = subStr(buf, esp_mail_str_279, NULL, 0, -1);
      setHeader(imap, tmp, header, headerState);
      delS(tmp);
    }
    else if (strcmpP(buf, 0, esp_mail_str_25))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type;
      tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0);
      if (tmp)
      {
        setHeader(imap, buf, header, headerState);
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_172))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding;
      tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1);
      if (tmp)
      {
        setHeader(imap, tmp, header, headerState);
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_190))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_accept_language;
      tmp = subStr(buf, esp_mail_str_190, NULL, 0, -1);
      if (tmp)
      {
        setHeader(imap, tmp, header, headerState);
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_191))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_language;
      tmp = subStr(buf, esp_mail_str_191, NULL, 0, -1);
      if (tmp)
      {
        setHeader(imap, tmp, header, headerState);
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_273))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_date;
      tmp = subStr(buf, esp_mail_str_273, NULL, 0, -1);
      if (tmp)
      {
        setHeader(imap, tmp, header, headerState);
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_274))
    {
      headerState = esp_mail_imap_header_state::esp_mail_imap_state_msg_id;
      tmp = subStr(buf, esp_mail_str_274, NULL, 0, -1);
      if (tmp)
      {
        setHeader(imap, tmp, header, headerState);
        delS(tmp);
      }
    }
    chunkIdx++;
  }
}

void ESP_Mail_Client::setHeader(IMAPSession *imap, char *buf, struct esp_mail_message_header_t &header, int state)
{
  switch (state)
  {
  case esp_mail_imap_header_state::esp_mail_imap_state_from:
    header.from += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_to:
    header.to += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_cc:
    header.cc += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_subject:
    header.subject += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_content_type:
    header.content_type += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding:
    header.content_transfer_encoding += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_accept_language:
    header.accept_language += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_content_language:
    header.content_language += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_date:
    header.date += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_msg_id:
    header.message_id += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_char_set:
    header.char_set += buf;
    break;
  case esp_mail_imap_header_state::esp_mail_imap_state_boundary:
    header.boundary += buf;
    break;
  default:
    break;
  }
}

void ESP_Mail_Client::handlePartHeader(IMAPSession *imap, char *buf, int &chunkIdx, struct esp_mail_message_part_info_t &part)
{
  char *tmp = nullptr;
  if (chunkIdx == 0)
  {
    tmp = subStr(buf, esp_mail_imap_response_7, NULL, 0, -1);
    if (tmp)
    {
      delS(tmp);
      tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0);
      if (tmp)
      {
        chunkIdx++;
        part.octetLen = atoi(tmp);
        delS(tmp);
      }
    }
  }
  else
  {
    if (strcmpP(buf, 0, esp_mail_str_25))
    {
      tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0);
      bool con_type = false;
      if (tmp)
      {
        con_type = true;
        part.content_type = tmp;
        delS(tmp);
        int p1 = strposP(part.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;
          part.multipart = true;
          //inline or embedded images
          if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_related;
          //multiple text formats e.g. plain, html, enriched
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative;
          //medias
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel;
          //rfc822 encapsulated
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_report;
          //others can be attachments
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1)
            part.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed;
        }

        p1 = strposP(part.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(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1)
            part.message_sub_type = esp_mail_imap_message_sub_type_rfc822;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1)
            part.message_sub_type = esp_mail_imap_message_sub_type_partial;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1)
            part.message_sub_type = esp_mail_imap_message_sub_type_external_body;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1)
            part.message_sub_type = esp_mail_imap_message_sub_type_delivery_status;
        }

        p1 = strpos(part.content_type.c_str(), esp_mail_imap_descrete_media_type_t::text, 0);
        if (p1 != -1)
        {
          p1 += strlen(esp_mail_imap_descrete_media_type_t::text) + 1;
          if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::plain, p1) != -1)
            part.msg_type = esp_mail_msg_type_plain;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::enriched, p1) != -1)
            part.msg_type = esp_mail_msg_type_enriched;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::html, p1) != -1)
            part.msg_type = esp_mail_msg_type_html;
          else
            part.msg_type = esp_mail_msg_type_plain;
        }
      }

      if (con_type)
      {
        if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched)
        {
          tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0);
          if (tmp)
          {
            part.charset = tmp;
            delS(tmp);
          }
          else
          {
            tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1);
            if (tmp)
            {
              part.charset = tmp;
              delS(tmp);
            }
          }

          if (strposP(buf, esp_mail_str_275, 0) > -1 || strposP(buf, esp_mail_str_270, 0) > -1)
            part.plain_flowed = true;
          if (strposP(buf, esp_mail_str_259, 0) > -1 || strposP(buf, esp_mail_str_257, 0) > -1)
            part.plain_delsp = true;
        }

        if (part.charset.length() == 0)
        {
          tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0);
          if (tmp)
          {
            part.charset = tmp;
            delS(tmp);
          }
          else
          {
            tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1);
            if (tmp)
            {
              part.charset = tmp;
              delS(tmp);
            }
          }
        }

        tmp = subStr(buf, esp_mail_str_170, esp_mail_str_136, 0);
        if (tmp)
        {
          part.name = tmp;
          delS(tmp);
        }
        else
        {
          tmp = subStr(buf, esp_mail_str_171, NULL, 0, -1);
          if (tmp)
          {
            part.name = tmp;
            delS(tmp);
          }
        }
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_172))
    {
      tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1);
      if (tmp)
      {
        part.content_transfer_encoding = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_174))
    {
      tmp = subStr(buf, esp_mail_str_174, NULL, 0, -1);
      if (tmp)
      {
        part.descr = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_175))
    {
      tmp = subStr(buf, esp_mail_str_175, esp_mail_str_97, 0);
      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)
        {
          part.content_disposition = tmp;
          if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::attachment) == 0)
            part.attach_type = esp_mail_att_type_attachment;
          else if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::inline_) == 0)
            part.attach_type = esp_mail_att_type_inline;
        }
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_150))
    {
      tmp = subStr(buf, esp_mail_str_150, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.sender = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_10))
    {
      tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.from = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_11))
    {
      tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.to = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_12))
    {
      tmp = subStr(buf, esp_mail_str_12, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.cc = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_184))
    {
      tmp = subStr(buf, esp_mail_str_184, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.reply_to = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_134))
    {
      tmp = subStr(buf, esp_mail_str_134, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.comment = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_24))
    {
      tmp = subStr(buf, esp_mail_str_24, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.subject = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_189))
    {
      tmp = subStr(buf, esp_mail_str_189, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.messageID = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_46))
    {
      tmp = subStr(buf, esp_mail_str_46, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.return_path = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_99))
    {
      tmp = subStr(buf, esp_mail_str_99, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.date = tmp;
        delS(tmp);
      }
    }
    else if (strcmpP(buf, 0, esp_mail_str_145))
    {
      tmp = subStr(buf, esp_mail_str_145, NULL, 0, -1);
      if (tmp)
      {
        part.rfc822_header.keyword = tmp;
        delS(tmp);
      }
    }

    if (part.content_disposition.length() > 0)
    {
      tmp = subStr(buf, esp_mail_str_176, esp_mail_str_136, 0);
      if (tmp)
      {
        part.filename = tmp;
        delS(tmp);
      }
      else
      {
        tmp = subStr(buf, esp_mail_str_177, NULL, 0, -1);
        if (tmp)
        {
          part.filename = tmp;
          delS(tmp);
        }
      }

      tmp = subStr(buf, esp_mail_str_178, esp_mail_str_97, 0);
      if (tmp)
      {
        part.attach_data_size = atoi(tmp);
        delS(tmp);
        cHeader(imap)->total_attach_data_size += part.attach_data_size;
        part.sizeProp = true;
      }
      else
      {
        tmp = subStr(buf, esp_mail_str_178, NULL, 0, -1);
        if (tmp)
        {
          part.attach_data_size = atoi(tmp);
          delS(tmp);
          cHeader(imap)->total_attach_data_size += part.attach_data_size;
          part.sizeProp = true;
        }
      }

      tmp = subStr(buf, esp_mail_str_179, esp_mail_str_136, 0);
      if (tmp)
      {
        part.creation_date = tmp;
        delS(tmp);
      }
      else
      {
        tmp = subStr(buf, esp_mail_str_180, NULL, 0, -1);
        if (tmp)
        {
          part.creation_date = tmp;
          delS(tmp);
        }
      }

      tmp = subStr(buf, esp_mail_str_181, esp_mail_str_136, 0);
      if (tmp)
      {
        part.modification_date = tmp;
        delS(tmp);
      }
      else
      {
        tmp = subStr(buf, esp_mail_str_182, NULL, 0, -1);
        if (tmp)
        {
          part.modification_date = tmp;
          delS(tmp);
        }
      }
    }

    chunkIdx++;
  }
}

char *ESP_Mail_Client::subStr(const char *buf, PGM_P beginH, PGM_P endH, int beginPos, int endPos)
{

  char *tmp = nullptr;
  int p1 = strposP(buf, beginH, beginPos);
  if (p1 != -1)
  {
    int p2 = -1;
    if (endPos == 0)
      p2 = strposP(buf, endH, p1 + strlen_P(beginH));

    if (p2 == -1)
      p2 = strlen(buf);

    int len = p2 - p1 - strlen_P(beginH);
    tmp = newS(len + 1);
    memcpy(tmp, &buf[p1 + strlen_P(beginH)], len);
    return tmp;
  }

  return nullptr;
}

void ESP_Mail_Client::handleAuth(SMTPSession *smtp, char *buf)
{
  if (strposP(buf, esp_mail_smtp_response_1, 0) > -1)
  {
    if (strposP(buf, esp_mail_smtp_response_2, 0) > -1)
      smtp->_auth_capability.login = true;
    if (strposP(buf, esp_mail_smtp_response_3, 0) > -1)
      smtp->_auth_capability.plain = true;
    if (strposP(buf, esp_mail_smtp_response_4, 0) > -1)
      smtp->_auth_capability.xoauth2 = true;
    if (strposP(buf, esp_mail_smtp_response_11, 0) > -1)
      smtp->_auth_capability.cram_md5 = true;
    if (strposP(buf, esp_mail_smtp_response_12, 0) > -1)
      smtp->_auth_capability.digest_md5 = true;
  }
  else if (strposP(buf, esp_mail_smtp_response_5, 0) > -1)
    smtp->_auth_capability.start_tls = true;
  else if (strposP(buf, esp_mail_smtp_response_6, 0) > -1)
    smtp->_send_capability._8bitMIME = true;
  else if (strposP(buf, esp_mail_smtp_response_7, 0) > -1)
    smtp->_send_capability.binaryMIME = true;
  else if (strposP(buf, esp_mail_smtp_response_8, 0) > -1)
    smtp->_send_capability.chunking = true;
  else if (strposP(buf, esp_mail_smtp_response_9, 0) > -1)
    smtp->_send_capability.utf8 = true;
  else if (strposP(buf, esp_mail_smtp_response_10, 0) > -1)
    smtp->_send_capability.pipelining = true;
  else if (strposP(buf, esp_mail_smtp_response_13, 0) > -1)
    smtp->_send_capability.dsn = true;
}

int ESP_Mail_Client::available(SMTPSession *smtp)
{
  int sz = 0;
  if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
  {
    if (smtp->httpClient._stream())
      sz = smtp->httpClient._stream()->_ns_available();
  }
  else
  {
    if (smtp->httpClient.stream())
      sz = smtp->httpClient.stream()->available();
  }

  return sz;
}

bool ESP_Mail_Client::handleSMTPResponse(SMTPSession *smtp, esp_mail_smtp_status_code respCode, int errCode)
{
  if (!reconnect(smtp))
    return false;

  bool ret = false;
  char *response = nullptr;
  int readLen = 0;
  long dataTime = millis();
  int chunkBufSize = 0;
  std::string s, r;
  int chunkIndex = 0;
  int count = 0;
  bool completedResponse = false;
  smtp->_smtpStatus.statusCode = 0;
  smtp->_smtpStatus.respCode = 0;
  smtp->_smtpStatus.text.clear();
  uint8_t minResLen = 5;
  struct esp_mail_smtp_response_status_t status;

  chunkBufSize = available(smtp);

  while (smtp->_tcpConnected && chunkBufSize <= 0)
  {
    if (!reconnect(smtp, dataTime))
      return false;
    if (!connected(smtp))
    {
      errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
      return false;
    }
    chunkBufSize = available(smtp);
    delay(0);
  }

  dataTime = millis();

  if (chunkBufSize > 1)
  {
    while (!completedResponse)
    {
      delay(0);

      if (!reconnect(smtp, dataTime))
        return false;

      if (!connected(smtp))
      {
        errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);
        return false;
      }

      chunkBufSize = available(smtp);

      if (chunkBufSize <= 0)
        break;

      if (chunkBufSize > 0)
      {
        chunkBufSize = 512;
        response = newS(chunkBufSize + 1);

        if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
          readLen = _readLine(smtp->httpClient._stream(), response, chunkBufSize, false, count);
        else
          readLen = readLine(smtp->httpClient.stream(), response, chunkBufSize, false, count);

        if (readLen)
        {
          if (smtp->_smtp_cmd != esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state)
          {
            //sometimes server sent multiple lines response
            //sometimes rx buffer may not ready for a while
            if (strlen(response) < minResLen)
            {
              r += response;
              chunkBufSize = 0;
              while (chunkBufSize == 0)
              {
                delay(0);
                if (!reconnect(smtp, dataTime))
                  return false;
                chunkBufSize = available(smtp);
              }
            }
            else
            {
              if (r.length() > 0)
              {
                r += response;
                memset(response, 0, chunkBufSize);
                strcpy(response, r.c_str());
              }

              if (smtp->_debugLevel > esp_mail_debug_level_1)
                esp_mail_debug((const char *)response);
            }

            if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_greeting)
              handleAuth(smtp, response);
          }

          getResponseStatus(response, respCode, 0, status);

          //get the status code again for unexpected return code
          if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls || status.respCode == 0)
            getResponseStatus(response, esp_mail_smtp_status_code_0, 0, status);

          ret = respCode == status.respCode;
          smtp->_smtpStatus = status;

          if (status.respCode > 0 && (status.respCode < 400 || status.respCode == respCode))
            ret = true;

          if (smtp->_debug && strlen(response) >= minResLen)
          {
            appendP(s, esp_mail_str_260, true);
            if (smtp->_smtpStatus.respCode != esp_mail_smtp_status_code_334)
              s += response;
            else
            {
              //base64 response
              size_t olen;
              char *decoded = (char *)decodeBase64((const unsigned char *)status.text.c_str(), status.text.length(), &olen);
              if (decoded && olen > 0)
              {
                olen += s.length();
                s += decoded;
                s[olen] = 0;
                delete[] decoded;
              }
            }
            esp_mail_debug(s.c_str());
            r.clear();
          }

          completedResponse = smtp->_smtpStatus.respCode > 0 && status.text.length() > minResLen;

          if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_auth && smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_334)
          {
            if (authFailed(response, readLen, chunkIndex, 4))
            {
              smtp->_smtpStatus.statusCode = -1;
              ret = false;
            }
          }

          chunkIndex++;

          if (smtp->_chunkedEnable && smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_chunk_termination)
            completedResponse = smtp->_chunkCount == chunkIndex;
        }
        delS(response);
      }
    }

    if (!ret)
      handleSMTPError(smtp, errCode, false);
  }

  return ret;
}

void ESP_Mail_Client::getResponseStatus(const char *buf, esp_mail_smtp_status_code respCode, int beginPos, struct esp_mail_smtp_response_status_t &status)
{
  std::string s;
  char *tmp = nullptr;
  int p1 = 0;
  if (respCode > esp_mail_smtp_status_code_0)
  {
    tmp = intStr((int)respCode);
    s = tmp;
    appendP(s, esp_mail_str_131, false);
    delS(tmp);
    p1 = strpos(buf, (const char *)s.c_str(), beginPos);
  }

  if (p1 != -1)
  {
    int ofs = s.length() - 2;
    if (ofs < 0)
      ofs = 1;

    int p2 = strposP(buf, esp_mail_str_131, p1 + ofs);

    if (p2 < 4 && p2 > -1)
    {
      tmp = newS(p2 + 1);
      memcpy(tmp, &buf[p1], p2);
      status.respCode = atoi(tmp);
      delS(tmp);

      p1 = p2 + 1;
      p2 = strlen(buf);
      if (p2 > p1)
      {
        tmp = newS(p2 + 1);
        memcpy(tmp, &buf[p1], p2 - p1);
        status.text = tmp;
        delS(tmp);
      }
    }
  }
}

void ESP_Mail_Client::closeTCP(SMTPSession *smtp)
{

  if (smtp->_tcpConnected)
  {
    if (smtp->httpClient.stream())
    {
      if (connected(smtp))
      {
        if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure)
          smtp->httpClient._stream()->stop();
        else
          smtp->httpClient.stream()->stop();
      }
    }
    _lastReconnectMillis = millis();
  }
  smtp->_tcpConnected = false;
}

void ESP_Mail_Client::closeTCP(IMAPSession *imap)
{

  if (imap->_tcpConnected)
  {
    if (imap->httpClient.stream())
    {
      if (connected(imap))
      {
        if (!imap->_secure)
          imap->httpClient._stream()->stop();
        else
          imap->httpClient.stream()->stop();
      }
    }
    _lastReconnectMillis = millis();
  }
  imap->_tcpConnected = false;
}

#if defined(ESP32)
void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient32 &httpClient, ESP_Mail_Session *session, std::shared_ptr<const char> caCert)
#elif defined(ESP8266)
void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient &httpClient, ESP_Mail_Session *session, std::shared_ptr<const char> caCert)
#endif
{

#if defined(ESP32)
  if (httpClient._certType == -1)
  {
    if (strlen(session->certificate.cert_file) == 0)
    {
      if (caCert != nullptr)
        httpClient.setCACert(caCert.get());
      else
        httpClient.setCACert(nullptr);
    }
    else
    {
      httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type);
    }
  }
#elif defined(ESP8266)

  if (httpClient._certType == -1)
  {

#ifndef USING_AXTLS
    if (!MailClient._clockReady && (strlen(session->certificate.cert_file) > 0 || caCert != nullptr))
    {
      MailClient.setClock(MailClient._gmtOffset);
      httpClient._clockReady = MailClient._clockReady;
    }
#endif
    if (strlen(session->certificate.cert_file) == 0)
    {
      if (caCert != nullptr)
        httpClient.setCACert(caCert.get());
      else
        httpClient.setCACert(nullptr);
    }
    else
    {
      httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type, MailClient._sdPin);
    }
  }
#endif
}

bool ESP_Mail_Client::ethLinkUp()
{
  bool ret = false;
#if defined(ESP32)
  char *ip = strP(esp_mail_str_328);
  if (strcmp(ETH.localIP().toString().c_str(), ip) != 0)
    ret = ETH.linkUp();
  delS(ip);
#endif
  return ret;
}

bool ESP_Mail_Client::reconnect(SMTPSession *smtp, unsigned long dataTime)
{

  bool status = WiFi.status() == WL_CONNECTED || ethLinkUp();

  if (dataTime > 0)
  {
    if (millis() - dataTime > smtp->httpClient.tcpTimeout)
    {
      closeTCP(smtp);
      errorStatusCB(smtp, MAIL_CLIENT_ERROR_READ_TIMEOUT);
      return false;
    }
  }

  if (!status)
  {
    if (smtp->_tcpConnected)
      closeTCP(smtp);

    errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST);

    if (millis() - _lastReconnectMillis > _reconnectTimeout && !smtp->_tcpConnected)
    {
#if defined(ESP32)
      esp_wifi_connect();
#elif defined(ESP8266)
      WiFi.reconnect();
#endif
      _lastReconnectMillis = millis();
    }

    status = WiFi.status() == WL_CONNECTED || ethLinkUp();
  }

  return status;
}

bool ESP_Mail_Client::reconnect(IMAPSession *imap, unsigned long dataTime, bool downloadRequest)
{

  bool status = WiFi.status() == WL_CONNECTED || ethLinkUp();

  if (dataTime > 0)
  {
    if (millis() - dataTime > imap->httpClient.tcpTimeout)
    {

      closeTCP(imap);

      if (imap->_headers.size() > 0)
      {
        if (downloadRequest)
        {
          errorStatusCB(imap, IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT);
          if (cHeader(imap)->part_headers.size() > 0)
            cPart(imap)->download_error = imap->errorReason().c_str();
        }
        else
        {
          errorStatusCB(imap, MAIL_CLIENT_ERROR_READ_TIMEOUT);
          cHeader(imap)->error_msg = imap->errorReason().c_str();
        }
      }
      return false;
    }
  }

  if (!status)
  {

    if (imap->_tcpConnected)
      closeTCP(imap);

    errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);

    if (imap->_headers.size() > 0)
    {
      if (downloadRequest)
        cPart(imap)->download_error = imap->errorReason().c_str();
      else
        cHeader(imap)->error_msg = imap->errorReason().c_str();
    }

    if (millis() - _lastReconnectMillis > _reconnectTimeout && !imap->_tcpConnected)
    {
#if defined(ESP32)
      esp_wifi_connect();
#elif defined(ESP8266)
      WiFi.reconnect();
#endif
      _lastReconnectMillis = millis();
    }

    status = WiFi.status() == WL_CONNECTED || ethLinkUp();
  }

  return status;
}

void ESP_Mail_Client::delS(char *p)
{
  if (p != nullptr)
    delete[] p;
}

char *ESP_Mail_Client::newS(size_t len)
{
  char *p = new char[len];
  memset(p, 0, len);
  return p;
}

char *ESP_Mail_Client::newS(char *p, size_t len)
{
  delS(p);
  p = newS(len);
  return p;
}

char *ESP_Mail_Client::newS(char *p, size_t len, char *d)
{
  delS(p);
  p = newS(len);
  strcpy(p, d);
  return p;
}

bool ESP_Mail_Client::strcmpP(const char *buf, int ofs, PGM_P beginH)
{
  char *tmp = nullptr;
  if (ofs < 0)
  {
    int p = strposP(buf, beginH, 0);
    if (p == -1)
      return false;
    ofs = p;
  }
  tmp = strP(beginH);
  char *tmp2 = newS(strlen_P(beginH) + 1);
  memcpy(tmp2, &buf[ofs], strlen_P(beginH));
  tmp2[strlen_P(beginH)] = 0;
  bool ret = (strcasecmp(tmp, tmp2) == 0);
  delS(tmp);
  delS(tmp2);
  return ret;
}

int ESP_Mail_Client::strposP(const char *buf, PGM_P beginH, int ofs)
{
  char *tmp = strP(beginH);
  int p = strpos(buf, tmp, ofs);
  delS(tmp);
  return p;
}

char *ESP_Mail_Client::strP(PGM_P pgm)
{
  size_t len = strlen_P(pgm) + 1;
  char *buf = newS(len);
  strcpy_P(buf, pgm);
  buf[len - 1] = 0;
  return buf;
}

void ESP_Mail_Client::appendP(std::string &buf, PGM_P p, bool empty)
{
  if (empty)
    buf.clear();
  char *b = strP(p);
  buf += b;
  delS(b);
}

char *ESP_Mail_Client::intStr(int value)
{
  char *buf = newS(36);
  memset(buf, 0, 36);
  itoa(value, buf, 10);
  return buf;
}

int ESP_Mail_Client::available(IMAPSession *imap)
{
  int sz = 0;
  if (!imap->_secure)
  {
    if (imap->httpClient._stream())
      sz = imap->httpClient._stream()->_ns_available();
  }
  else
  {
    if (imap->httpClient.stream())
      sz = imap->httpClient.stream()->available();
  }
  return sz;
}

bool ESP_Mail_Client::handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession)
{

  if (!reconnect(imap))
    return false;

  esp_mail_imap_response_status imapResp = esp_mail_imap_response_status::esp_mail_imap_resp_unknown;
  char *response = nullptr;
  int readLen = 0;
  long dataTime = millis();
  int chunkBufSize = available(imap);
  int chunkIdx = 0;
  std::string s;
  bool completedResponse = false;
  bool endSearch = false;
  struct esp_mail_message_header_t header;
  struct esp_mail_message_part_info_t part;

  std::string filePath = "";
  bool downloadRequest = false;
  int reportState = 0;
  int octetCount = 0;
  int octetLength = 0;
  int oCount = 0;
  bool tmo = false;
  int headerState = 0;
  int scnt = 0;
  int dcnt = -1;
  char *skey = nullptr;
  char *spc = nullptr;
  char *lastBuf = nullptr;
  char *tmp = nullptr;
  bool crLF = imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text && strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31);

  while (imap->_tcpConnected && chunkBufSize <= 0)
  {
    if (!reconnect(imap, dataTime))
      return false;
    if (!connected(imap))
    {
      errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);
      return false;
    }
    chunkBufSize = available(imap);
    delay(0);
  }

  dataTime = millis();

  if (chunkBufSize > 1)
  {
    if (imap->_imap_cmd == esp_mail_imap_cmd_examine)
    {
      imap->_mbif.clear();
      imap->_mbif._msgCount = 0;
      imap->_nextUID = "";
    }

    if (imap->_imap_cmd == esp_mail_imap_cmd_search)
    {
      imap->_mbif._searchCount = 0;
      imap->_msgNum.clear();
    }

    chunkBufSize = 512;
    response = newS(chunkBufSize + 1);

    if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search)
    {
      skey = strP(esp_mail_imap_response_6);
      spc = strP(esp_mail_str_92);
    }

    if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
      lastBuf = newS(BASE64_CHUNKED_LEN + 1);

    while (!completedResponse)
    {
      delay(0);
      if (!reconnect(imap, dataTime) || !connected(imap))
      {
        if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search)
        {
          delS(skey);
          delS(spc);
        }

        if (!connected(imap))
        {
          errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST);
          return false;
        }
        return false;
      }
      chunkBufSize = available(imap);

      if (chunkBufSize > 0)
      {
        chunkBufSize = 512;

        if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search)
        {
          readLen = getMSGNUM(imap, response, chunkBufSize, chunkIdx, endSearch, scnt, skey, spc);
          imap->_mbif._availableItems = imap->_msgNum.size();
        }
        else
        {
          if (!imap->_secure)
            readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, crLF, octetCount);
          else
            readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, crLF, octetCount);
        }

        if (readLen)
        {

          if (imap->_debugLevel > esp_mail_debug_level_1)
          {
            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((const char *)response);
          }

          if (imap->_imap_cmd != esp_mail_imap_cmd_search || (imap->_imap_cmd == esp_mail_imap_cmd_search && endSearch))
            imapResp = imapResponseStatus(imap, response);

          if (imapResp != esp_mail_imap_response_status::esp_mail_imap_resp_unknown)
          {
            if (imap->_debugLevel > esp_mail_debug_level_1)
            {
              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((const char *)response);
            }

            if (imap->_imap_cmd == esp_mail_imap_cmd_close)
              completedResponse = true;
            else
            {
              //some IMAP servers advertise CAPABILITY in their responses
              //try to read the next available response
              memset(response, 0, chunkBufSize);
              if (!imap->_secure)
                readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, true, octetCount);
              else
                readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, true, octetCount);
              if (readLen)
              {
                completedResponse = false;
                imapResp = imapResponseStatus(imap, response);
                if (imapResp > esp_mail_imap_response_status::esp_mail_imap_resp_unknown)
                  completedResponse = true;
              }
              else
                completedResponse = true;
            }
          }
          else
          {
            if (imap->_imap_cmd == esp_mail_imap_cmd_auth)
            {
              if (authFailed(response, readLen, chunkIdx, 2))
                completedResponse = true;
            }
            else if (imap->_imap_cmd == esp_mail_imap_cmd_capability)
              handleCapability(imap, response, chunkIdx);
            else if (imap->_imap_cmd == esp_mail_imap_cmd_list)
              handleFolders(imap, response);
            else if (imap->_imap_cmd == esp_mail_imap_cmd_select || imap->_imap_cmd == esp_mail_imap_cmd_examine)
              handleExamine(imap, response);
            else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header)
            {
              char *tmp = intStr(cMSG(imap));
              header.message_uid = tmp;
              delS(tmp);

              tmp = intStr(imap->_totalRead);
              header.message_no = tmp;
              delS(tmp);

              int _st = headerState;
              handleHeader(imap, response, readLen, chunkIdx, header, headerState, octetCount);
              if (_st == headerState && headerState > 0 && octetCount <= header.header_data_len)
                setHeader(imap, response, header, headerState);
            }
            else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime)
              handlePartHeader(imap, response, chunkIdx, part);
            else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text)
              decodeText(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetLength, octetCount, dcnt);
            else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline)
            {

              if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31))
              {
                //multi-line chunked base64 string attachment handle
                if (octetCount < octetLength && readLen < BASE64_CHUNKED_LEN)
                {
                  if (strlen(lastBuf) > 0)
                  {
                    tmp = newS(readLen + strlen(lastBuf) + 2);
                    strcpy(tmp, lastBuf);
                    strcat(tmp, response);
                    readLen = strlen(tmp);
                    tmo = handleAttachment(imap, tmp, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt);
                    delS(tmp);
                    memset(lastBuf, 0, BASE64_CHUNKED_LEN + 1);
                    if (!tmo)
                      break;
                  }
                  else if (readLen < BASE64_CHUNKED_LEN + 1)
                    strcpy(lastBuf, response);
                }
                else
                {
                  tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt);
                  if (!tmo)
                    break;
                }
              }
              else
                tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt);
            }
            dataTime = millis();
          }
        }
        memset(response, 0, chunkBufSize);
      }
    }
    delS(response);
    if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search)
    {
      if (imap->_debug && scnt > 0 && scnt < 100)
        searchReport(100, spc);
      delS(skey);
      delS(spc);
    }
    if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment)
      delS(lastBuf);
  }

  if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header && header.header_data_len == 0) || imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no)
  {
    if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no)
      imap->_imapStatus.statusCode = IMAP_STATUS_IMAP_RESPONSE_FAILED;
    else
      imap->_imapStatus.statusCode = IMAP_STATUS_NO_MESSAGE;

    if (imap->_readCallback)
    {
      std::string s;
      appendP(s, esp_mail_str_53, true);
      s += imap->errorReason().c_str();
      imapCB(imap, s.c_str(), false);
    }

    if (imap->_debug)
    {
      std::string s;
      appendP(s, esp_mail_str_185, true);
      s += imap->errorReason().c_str();
      esp_mail_debug_line(s.c_str(), true);
    }

    return false;
  }

  if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_ok)
  {
    if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header)
    {
      char *buf = newS(header.content_type.length() + 1);
      strcpy(buf, header.content_type.c_str());
      header.content_type.clear();

      tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0);
      if (tmp)
      {
        headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type;
        setHeader(imap, tmp, header, headerState);
        delS(tmp);

        int p1 = strposP(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;
          header.multipart = true;
          //inline or embedded images
          if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_related;
          //multiple text formats e.g. plain, html, enriched
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative;
          //medias
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel;
          //rfc822 encapsulated
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_report;
          //others can be attachments
          else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1)
            header.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed;
        }

        p1 = strposP(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(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1)
          {
            header.rfc822_part = true;
            header.message_sub_type = esp_mail_imap_message_sub_type_rfc822;
          }
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1)
            header.message_sub_type = esp_mail_imap_message_sub_type_partial;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1)
            header.message_sub_type = esp_mail_imap_message_sub_type_external_body;
          else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1)
            header.message_sub_type = esp_mail_imap_message_sub_type_delivery_status;
        }

        tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1);
        if (tmp)
        {
          headerState = esp_mail_imap_header_state::esp_mail_imap_state_char_set;
          setHeader(imap, tmp, header, headerState);
          delS(tmp);
        }

        if (header.multipart)
        {
          if (strcmpP(buf, 0, esp_mail_str_277))
          {
            tmp = subStr(buf, esp_mail_str_277, esp_mail_str_136, 0);
            if (tmp)
            {
              headerState = esp_mail_imap_header_state::esp_mail_imap_state_boundary;
              setHeader(imap, tmp, header, headerState);
              delS(tmp);
            }
          }
        }
      }

      delS(buf);

      decodeHeader(header.from, header.from_charset);
      decodeHeader(header.to, header.to_charset);
      decodeHeader(header.cc, header.cc_charset);
      decodeHeader(header.subject, header.subject_charset);

      imap->_headers.push_back(header);
    }

    if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime)
    {
      //expect the octet length in the response for the existent part
      if (part.octetLen > 0)
      {

        part.partNumStr = cHeader(imap)->partNumStr;
        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 = part.rfc822_header;
              imap->_rfc822_part_count++;
              _part->rfc822_msg_Idx = imap->_rfc822_part_count;
            }
          }
        }

        cHeader(imap)->part_headers.push_back(part);
        cHeader(imap)->message_data_count = cHeader(imap)->part_headers.size();

        if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched || part.msg_type == esp_mail_msg_type_html || part.attach_type == esp_mail_att_type_none || (part.attach_type == esp_mail_att_type_attachment && imap->_config->download.attachment) || (part.attach_type == esp_mail_att_type_inline && imap->_config->download.inlineImg))
        {
          if (part.message_sub_type != esp_mail_imap_message_sub_type_rfc822)
          {
            if (part.attach_type != esp_mail_att_type_none && cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative)
              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)->file_open_write)
        file.close();
    }

    if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text)
      cPart(imap)->text[cPart(imap)->textLen] = 0;
  }
  else
  {
    //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::saveHeader(IMAPSession *imap)
{

  std::string headerFilePath;
  prepareFilePath(imap, headerFilePath, true);
  if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk)
    _sdOk = sdTest();
  else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk)
#if defined(ESP32)
    _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
    _flashOk = ESP_MAIL_FLASH_FS.begin();
#endif

  if (_sdOk || _flashOk)
  {
    if (file)
      file.close();

    if (imap->_storageType == esp_mail_file_storage_type_sd) {
      file = ESP_MAIL_SD_FS.open(headerFilePath.c_str(), FILE_WRITE);
    } else if (imap->_storageType == esp_mail_file_storage_type_flash) {
#if defined(ESP32)
      file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), FILE_WRITE);
#elif defined(ESP8266)
      file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), "w");
#endif
    } else if (imap->_storageType == esp_mail_file_storage_type_univ) {
      file = ufsp->open(headerFilePath.c_str(), "w");
    }

    if (file)
    {
      std::string s;
      appendP(s, esp_mail_str_99, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->date.c_str());

      appendP(s, esp_mail_str_100, true);
      file.print(s.c_str());
      if (imap->_uidSearch)
        file.println(cMSG(imap));
      else
        file.println();

      appendP(s, esp_mail_str_101, true);
      file.print(s.c_str());
      file.println(cMSG(imap));

      appendP(s, esp_mail_str_102, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->accept_language.c_str());

      appendP(s, esp_mail_str_103, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->content_language.c_str());

      appendP(s, esp_mail_str_10, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->from.c_str());

      appendP(s, esp_mail_str_105, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->from_charset.c_str());

      appendP(s, esp_mail_str_11, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->to.c_str());

      appendP(s, esp_mail_str_107, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->to_charset.c_str());

      appendP(s, esp_mail_str_108, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->cc.c_str());

      appendP(s, esp_mail_str_109, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->cc_charset.c_str());

      appendP(s, esp_mail_str_24, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->subject.c_str());

      appendP(s, esp_mail_str_111, true);
      file.print(s.c_str());
      file.println(cHeader(imap)->subject_charset.c_str());

      appendP(s, esp_mail_str_112, true);
      file.print(s.c_str());
      file.println(cPart(imap)->charset.c_str());

      if (cHeader(imap)->attachment_count > 0)
      {

        appendP(s, esp_mail_str_113, true);
        file.print(s.c_str());
        file.println(cHeader(imap)->attachment_count);

        for (int j = 0; j < cHeader(imap)->attachment_count; j++)
        {
          if (imap->_headers[cIdx(imap)].part_headers[j].attach_type == esp_mail_att_type_none || imap->_headers[cIdx(imap)].part_headers[j].rfc822_part)
            continue;
          struct esp_mail_attacment_info_t att;
          att.filename = imap->_headers[cIdx(imap)].part_headers[j].filename.c_str();
          att.mime = imap->_headers[cIdx(imap)].part_headers[j].content_type.c_str();
          att.name = imap->_headers[cIdx(imap)].part_headers[j].name.c_str();
          att.size = imap->_headers[cIdx(imap)].part_headers[j].attach_data_size;
          att.creationDate = imap->_headers[cIdx(imap)].part_headers[j].creation_date.c_str();
          att.type = imap->_headers[cIdx(imap)].part_headers[j].attach_type;

          appendP(s, esp_mail_str_114, true);
          file.print(s.c_str());
          file.println(j + 1);

          appendP(s, esp_mail_str_115, true);
          file.print(s.c_str());
          file.println(att.filename);

          appendP(s, esp_mail_str_116, true);
          file.print(s.c_str());
          file.println(att.name);

          appendP(s, esp_mail_str_117, true);
          file.print(s.c_str());
          file.println(att.size);

          appendP(s, esp_mail_str_118, true);
          file.print(s.c_str());
          file.println(att.mime);

          appendP(s, esp_mail_str_119, true);
          file.print(s.c_str());
          file.println(att.creationDate);
        }
      }

      file.close();
    }
    imap->_headerSaved = true;
  }
}

esp_mail_imap_response_status ESP_Mail_Client::imapResponseStatus(IMAPSession *imap, char *response)
{
  imap->_imapStatus.text.clear();
  if (strposP(response, esp_mail_imap_response_1, 0) > -1)
    return esp_mail_imap_response_status::esp_mail_imap_resp_ok;
  else if (strposP(response, esp_mail_imap_response_2, 0) > -1)
  {
    imap->_imapStatus.text = response;
    imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_2));
    return esp_mail_imap_response_status::esp_mail_imap_resp_no;
  }
  else if (strposP(response, esp_mail_imap_response_3, 0) > -1)
  {
    imap->_imapStatus.text = response;
    imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_3));
    return esp_mail_imap_response_status::esp_mail_imap_resp_bad;
  }
  return esp_mail_imap_response_status::esp_mail_imap_resp_unknown;
}

void ESP_Mail_Client::decodeHeader(std::string &headerField, std::string &headerEnc)
{

  size_t p1 = 0, p2 = 0;

  while (headerField[p1] == ' ' && p1 < headerField.length() - 1)
    p1++;

  if (headerField[p1] == '=' && headerField[p1 + 1] == '?')
  {
    p2 = headerField.find("?", p1 + 2);
    if (p2 != std::string::npos)
      headerEnc = headerField.substr(p1 + 2, p2 - p1 - 2);
  }

  int bufSize = 512;
  char *buf = newS(bufSize);

  RFC2047Decoder.rfc2047Decode(buf, headerField.c_str(), bufSize);

  if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_iso8859_1)
  {
    int len = strlen(buf);
    int olen = (len + 1) * 2;
    unsigned char *out = (unsigned char *)newS(olen);
    decodeLatin1_UTF8(out, &olen, (unsigned char *)buf, &len);
    delS(buf);
    buf = (char *)out;
  }
  else if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_tis620)
  {
    size_t len2 = strlen(buf);
    char *tmp = newS((len2 + 1) * 3);
    decodeTIS620_UTF8(tmp, buf, len2);
    delS(buf);
    buf = tmp;
  }

  headerField = buf;
  delS(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;

  if (strposP(enc, esp_mail_str_237, 0) > -1 || strposP(enc, esp_mail_str_231, 0) > -1 || strposP(enc, esp_mail_str_226, 0) > -1)
    scheme = esp_mail_char_decoding_scheme_tis620;
  else if (strposP(enc, esp_mail_str_227, 0) > -1)
    scheme = esp_mail_char_decoding_scheme_iso8859_1;

  return scheme;
}

bool ESP_Mail_Client::handleAttachment(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetCount, int &octetLength, int &oCount, int &reportState, int &downloadCount)
{
  if (chunkIdx == 0)
  {
    char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0);
    if (tmp)
    {
      octetCount = 2; //CRLF counted from first line
      octetLength = atoi(tmp);
      delS(tmp);
      chunkIdx++;
      cHeader(imap)->total_download_size += octetLength;
    }
    return true;
  }

  if (octetLength == 0)
    return true;

  chunkIdx++;

  delay(0);

  if (!cPart(imap)->file_open_write)
  {

    cPart(imap)->file_open_write = true;

    if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk)
      _sdOk = sdTest();
    else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk)
#if defined(ESP32)
      _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
      _flashOk = ESP_MAIL_FLASH_FS.begin();
#endif

    if (_sdOk || _flashOk)
    {

      downloadRequest = true;

      filePath.clear();
      filePath += imap->_config->storage.saved_path;
      appendP(filePath, esp_mail_str_202, false);

      char *tmp = intStr(cMSG(imap));
      filePath += tmp;
      delS(tmp);

      if (imap->_storageType == esp_mail_file_storage_type_sd)
        if (!ESP_MAIL_SD_FS.exists(filePath.c_str()))
          createDirs(filePath);

      appendP(filePath, esp_mail_str_202, false);

      filePath += cPart(imap)->filename;

      if (imap->_storageType == esp_mail_file_storage_type_sd)
        file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE);
      else if (imap->_storageType == esp_mail_file_storage_type_flash)
#if defined(ESP32)
        file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE);
#elif defined(ESP8266)
        file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w");
#endif
    }
  }

  if (_sdOk || _flashOk)
  {
    int nOctet = oCount + bufLen + 2;
    if (nOctet > octetLength)
    {
      if (imap->_readCallback)
        downloadReport(imap, 100);

      if (oCount < octetLength)
      {
        int dLen = nOctet - 2 - octetLength;
        bufLen -= dLen;
        buf[bufLen] = 0;
      }
      else
        return true;
    }

    oCount += bufLen + 2;

    if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31))
    {

      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;
        }

        file.write((const uint8_t *)decoded, olen);
        delay(0);
        delete[] decoded;

        if (imap->_config->enable.download_status)
        {
          int p = 0;
          if (cHeader(imap)->total_download_size > 0)
            p = 100 * octetCount / cHeader(imap)->total_download_size;

          if ((p != downloadCount) && (p <= 100))
          {
            downloadCount = p;
            if (imap->_readCallback && reportState != -1)
              downloadReport(imap, p);
            reportState = -1;
          }
          else
            reportState = 0;
        }
      }

      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;
      }

      file.write((const uint8_t *)buf, bufLen);
      delay(0);

      if (imap->_config->enable.download_status)
      {
        int p = 0;
        if (cHeader(imap)->total_download_size > 0)
          p = 100 * octetCount / cHeader(imap)->total_download_size;

        if ((p != downloadCount) && (p <= 100))
        {
          downloadCount = p;
          if (imap->_readCallback && reportState != -1)
            downloadReport(imap, p);
          reportState = -1;
        }
        else
          reportState = 0;
      }

      if (!reconnect(imap))
        return false;
    }
  }
  return true;
}

void ESP_Mail_Client::downloadReport(IMAPSession *imap, int progress)
{
  if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0)
  {
    std::string s;
    char *tmp = intStr(progress);
    appendP(s, esp_mail_str_90, true);
    appendP(s, esp_mail_str_131, false);
    s += cPart(imap)->filename;
    appendP(s, esp_mail_str_91, false);
    s += tmp;
    delS(tmp);
    appendP(s, esp_mail_str_92, false);
    appendP(s, esp_mail_str_34, false);
    esp_mail_debug_line(s.c_str(), false);
    std::string().swap(s);
  }
}

void ESP_Mail_Client::fetchReport(IMAPSession *imap, int progress, bool download)
{
  if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0)
  {
    std::string s;
    char *tmp = intStr(progress);
    if (download)
      appendP(s, esp_mail_str_90, true);
    else
      appendP(s, esp_mail_str_83, true);
    appendP(s, esp_mail_str_131, false);
    if (cPart(imap)->filename.length() > 0)
    {
      s += cPart(imap)->filename;
      appendP(s, esp_mail_str_91, false);
    }
    s += tmp;
    delS(tmp);
    appendP(s, esp_mail_str_92, false);
    appendP(s, esp_mail_str_34, false);
    esp_mail_debug_line(s.c_str(), false);
    std::string().swap(s);
  }
}

void ESP_Mail_Client::searchReport(int progress, const char *percent)
{
  if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0)
  {
    char *tmp = intStr(progress);
    std::string s;
    appendP(s, esp_mail_str_261, true);
    s += tmp;
    s += percent;
    appendP(s, esp_mail_str_34, false);
    esp_mail_debug_line(s.c_str(), false);
    delS(tmp);
  }
}

void ESP_Mail_Client::uploadReport(const char *filename, int progress)
{
  if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0)
  {
    std::string s;
    char *tmp = intStr(progress);
    appendP(s, esp_mail_str_160, true);
    s += filename;
    appendP(s, esp_mail_str_91, false);
    s += tmp;
    delS(tmp);
    appendP(s, esp_mail_str_92, false);
    appendP(s, esp_mail_str_34, false);
    esp_mail_debug_line(s.c_str(), false);
    std::string().swap(s);
  }
}

int ESP_Mail_Client::cMSG(IMAPSession *imap)
{
  return imap->_msgNum[cIdx(imap)];
}

int ESP_Mail_Client::cIdx(IMAPSession *imap)
{
  return imap->_cMsgIdx;
}

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;
      out[j++] = 0xe0 | ((unicode >> 12) & 0xf);
      out[j++] = 0x80 | ((unicode >> 6) & 0x3f);
      out[j++] = 0x80 | (unicode & 0x3f);
    }
  }
}

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);
}

void ESP_Mail_Client::decodeText(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetLength, int &octetCount, int &readCount)
{
  bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822;
  if (chunkIdx == 0)
  {
    char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0);
    if (tmp)
    {
      octetCount = 2;
      octetLength = atoi(tmp);
      delS(tmp);
      chunkIdx++;
      cPart(imap)->octetLen = octetLength;

      if ((rfc822_body_subtype && imap->_config->download.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))))
        prepareFilePath(imap, filePath, false);

      if (filePath.length() == 0)
      {
        if (!rfc822_body_subtype)
          appendP(filePath, esp_mail_str_67, false);
        else
        {
          appendP(filePath, esp_mail_str_82, false);
          appendP(filePath, esp_mail_str_131, false);
          appendP(filePath, esp_mail_str_67, false);
        }
      }
      cPart(imap)->filename = filePath;

      return;
    }
    else
    {
      if (imap->_debug)
      {
        char *tmp = strP(esp_mail_str_280);
        esp_mail_debug_line(tmp, false);
        delS(tmp);
      }
    }
  }

  delay(0);

  if (octetLength == 0)
    return;

  if (imap->_config->download.rfc822 || imap->_config->download.html || imap->_config->download.text || (rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text))))
  {
    if (imap->_readCallback && octetCount > octetLength + 2 && readCount < 100)
      fetchReport(imap, 100, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))));

    if (octetCount <= octetLength + 2)
    {
      size_t olen = 0;
      char *decoded = nullptr;
      bool newC = true;
      if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31))
      {
        decoded = (char *)decodeBase64((const unsigned char *)buf, bufLen, &olen);
      }
      else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_278))
      {
        decoded = newS(bufLen + 10);
        decodeQP(buf, decoded);
        olen = strlen(decoded);
      }
      else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_29))
      {
        decoded = decode7Bit(buf);
        olen = strlen(decoded);
      }
      else
      {
        //8bit and binary
        newC = false;
        decoded = buf;
        olen = bufLen;
      }

      if (decoded)
      {

        if ((rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text))))
        {

          if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_iso8859_1)
          {
            int ilen = olen;
            int olen2 = (ilen + 1) * 2;
            unsigned char *tmp = (unsigned char *)newS(olen2);
            decodeLatin1_UTF8(tmp, &olen2, (unsigned char *)decoded, &ilen);
            delS(decoded);
            olen = olen2;
            decoded = (char *)tmp;
          }
          else if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_tis620)
          {
            char *out = newS((olen + 1) * 3);
            delS(decoded);
            decodeTIS620_UTF8(out, decoded, olen);
            olen = strlen(out);
            decoded = out;
          }

          int p = 0;

          if (octetLength > 0)
            p = 100 * octetCount / octetLength;

          if ((p != readCount) && (p <= 100))
          {
            readCount = p;
            if (imap->_readCallback)
              fetchReport(imap, p, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))));
          }

          if (cPart(imap)->text.length() < imap->_config->limit.msg_size)
          {

            if (cPart(imap)->text.length() + olen < imap->_config->limit.msg_size)
            {
              cPart(imap)->textLen += olen;
              cPart(imap)->text.append(decoded, olen);
            }
            else
            {
              int d = imap->_config->limit.msg_size - cPart(imap)->text.length();
              cPart(imap)->textLen += d;
              if (d > 0)
                cPart(imap)->text.append(decoded, d);
            }
          }
        }

        if (filePath.length() > 0)
        {
          if (!cPart(imap)->file_open_write)
          {
            cPart(imap)->file_open_write = true;

            if (_sdOk || _flashOk)
            {
              downloadRequest = true;

              if (imap->_storageType == esp_mail_file_storage_type_sd)
                file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE);
              else if (imap->_storageType == esp_mail_file_storage_type_flash)
#if defined(ESP32)
                file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE);
#elif defined(ESP8266)
                file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w");
#endif
            }
          }

          if (_sdOk || _flashOk)
            file.write((const uint8_t *)decoded, olen);
        }

        if (newC)
          delS(decoded);
      }
    }
  }
}
void ESP_Mail_Client::prepareFilePath(IMAPSession *imap, std::string &filePath, bool header)
{
  bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822;
  std::string fpath = imap->_config->storage.saved_path;
  appendP(fpath, esp_mail_str_202, false);
  char *tmp = intStr(cMSG(imap));
  fpath += tmp;
  delS(tmp);

  if (imap->_storageType == esp_mail_file_storage_type_sd)
    if (!ESP_MAIL_SD_FS.exists(fpath.c_str()))
      createDirs(fpath);

  if (header)
  {
    appendP(fpath, esp_mail_str_203, false);
  }
  else
  {
    if (!rfc822_body_subtype)
    {
      appendP(fpath, esp_mail_str_161, false);
      if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
        appendP(fpath, esp_mail_str_95, false);
      else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
        appendP(fpath, esp_mail_str_94, false);
    }
    else
    {
      appendP(fpath, esp_mail_str_163, false);

      if (cPart(imap)->rfc822_msg_Idx > 0)
      {
        char *tmp = intStr(cPart(imap)->rfc822_msg_Idx);
        fpath += tmp;
        delS(tmp);
      }

      if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)
        appendP(fpath, esp_mail_str_95, false);
      else if (cPart(imap)->msg_type == esp_mail_msg_type_html)
        appendP(fpath, esp_mail_str_94, false);
      else
        //possible rfc822 encapsulated message which cannot fetch its header
        appendP(fpath, esp_mail_str_40, false);
    }
  }

  filePath = fpath;
}

char *ESP_Mail_Client::strReplace(char *orig, char *rep, char *with)
{
  char *result = nullptr;
  char *ins = nullptr;
  char *tmp = nullptr;
  int len_rep;
  int len_with;
  int len_front;
  int count;

  len_with = strlen(with);
  len_rep = strlen(rep);

  ins = orig;
  for (count = 0; (tmp = strstr(ins, rep)); ++count)
    ins = tmp + len_rep;

  tmp = result = newS(strlen(orig) + (len_with - len_rep) * count + 1);
  while (count--)
  {
    ins = strstr(orig, rep);
    len_front = ins - orig;
    tmp = strncpy(tmp, orig, len_front) + len_front;
    tmp = strcpy(tmp, with) + len_with;
    orig += len_front + len_rep;
  }
  strcpy(tmp, orig);
  return result;
}

int ESP_Mail_Client::decodeChar(const char *s)
{
  assert(s);
  assert(*s == '=');
  return 16 * hexval(*(s + 1)) + hexval(*(s + 2));
}

void ESP_Mail_Client::decodeQP(const char *buf, char *out)
{
  char *tmp = strP(esp_mail_str_295);
  while (*buf)
  {
    if (*buf != '=')
      strcat_c(out, *buf++);
    else if (*(buf + 1) == '\r' && *(buf + 2) == '\n')
      buf += 3;
    else if (*(buf + 1) == '\n')
      buf += 2;
    else if (!strchr(tmp, *(buf + 1)))
      strcat_c(out, *buf++);
    else if (!strchr(tmp, *(buf + 2)))
      strcat_c(out, *buf++);
    else
    {
      strcat_c(out, decodeChar(buf));
      buf += 3;
    }
  }
  delS(tmp);
}

std::string ESP_Mail_Client::getBoundary(size_t len)
{
  char *tmp = strP(boundary_table);
  char *buf = newS(len);
  if (len)
  {
    --len;
    buf[0] = tmp[0];
    buf[1] = tmp[1];
    for (size_t n = 2; n < len; n++)
    {
      int key = rand() % (int)(strlen(tmp) - 1);
      buf[n] = tmp[key];
    }
    buf[len] = '\0';
  }
  std::string s = buf;
  delS(buf);
  delS(tmp);
  return s;
}

char *ESP_Mail_Client::decode7Bit(char *buf)
{
  char *out = strReplaceP(buf, imap_7bit_key1, imap_7bit_val1);
  char *tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key2, imap_7bit_val2);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key3, imap_7bit_val3);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key4, imap_7bit_val4);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key5, imap_7bit_val5);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key6, imap_7bit_val6);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key7, imap_7bit_val7);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key8, imap_7bit_val8);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key9, imap_7bit_val9);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key10, imap_7bit_val10);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key11, imap_7bit_val11);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key12, imap_7bit_val12);

  delS(tmp);
  tmp = newS(strlen(out) + 10);
  strcpy(tmp, out);
  delS(out);
  out = strReplaceP(tmp, imap_7bit_key13, imap_7bit_val13);
  delS(tmp);
  return out;
}

char *ESP_Mail_Client::strReplaceP(char *buf, PGM_P name, PGM_P value)
{
  char *n = strP(name);
  char *v = strP(value);
  char *out = strReplace(buf, n, v);
  delS(n);
  delS(v);
  return out;
}

void ESP_Mail_Client::handleCapability(IMAPSession *imap, char *buf, int &chunkIdx)
{
  if (chunkIdx == 0)
  {
    if (strposP(buf, esp_mail_imap_response_10, 0) > -1)
    {
      if (strposP(buf, esp_mail_imap_response_11, 0) > -1)
        imap->_auth_capability.login = true;
      if (strposP(buf, esp_mail_imap_response_12, 0) > -1)
        imap->_auth_capability.plain = true;
      if (strposP(buf, esp_mail_imap_response_13, 0) > -1)
        imap->_auth_capability.xoauth2 = true;
      if (strposP(buf, esp_mail_imap_response_14, 0) > -1)
        imap->_auth_capability.start_tls = true;
      if (strposP(buf, esp_mail_imap_response_15, 0) > -1)
        imap->_auth_capability.cram_md5 = true;
      if (strposP(buf, esp_mail_imap_response_16, 0) > -1)
        imap->_auth_capability.digest_md5 = true;
    }
  }
}

bool ESP_Mail_Client::authFailed(char *buf, int bufLen, int &chunkIdx, int ofs)
{
  bool ret = false;
  if (chunkIdx == 0)
  {
    size_t olen;
    unsigned char *decoded = decodeBase64((const unsigned char *)(buf + ofs), bufLen - ofs, &olen);
    if (decoded)
    {
      ret = strposP((char *)decoded, esp_mail_str_294, 0) > -1;
      delete[] decoded;
    }
    chunkIdx++;
  }
  return ret;
}

void ESP_Mail_Client::handleFolders(IMAPSession *imap, char *buf)
{
  struct esp_mail_folder_info_t fd;
  char *tmp = nullptr;
  int p1 = strposP(buf, esp_mail_imap_response_4, 0);
  int p2 = 0;
  if (p1 != -1)
  {
    p1 = strposP(buf, esp_mail_str_198, 0);
    if (p1 != -1)
    {
      p2 = strposP(buf, esp_mail_str_192, p1 + 1);
      if (p2 != -1)
      {
        tmp = newS(p2 - p1);
        strncpy(tmp, buf + p1 + 1, p2 - p1 - 1);
        if (tmp[p2 - p1 - 2] == '\r')
          tmp[p2 - p1 - 2] = 0;
        fd.attributes = tmp;
        delS(tmp);
      }
    }

    p1 = strposP(buf, esp_mail_str_136, 0);
    if (p1 != -1)
    {
      p2 = strposP(buf, esp_mail_str_136, p1 + 1);
      if (p2 != -1)
      {
        tmp = newS(p2 - p1);
        strncpy(tmp, buf + p1 + 1, p2 - p1 - 1);
        if (tmp[p2 - p1 - 2] == '\r')
          tmp[p2 - p1 - 2] = 0;
        fd.delimiter = tmp;
        delS(tmp);
      }
    }

    p1 = strposP(buf, esp_mail_str_131, p2);
    if (p1 != -1)
    {
      p2 = strlen(buf);
      tmp = newS(p2 - p1);
      if (buf[p1 + 1] == '"')
        p1++;
      strncpy(tmp, buf + p1 + 1, p2 - p1 - 1);
      if (tmp[p2 - p1 - 2] == '\r')
        tmp[p2 - p1 - 2] = 0;
      if (tmp[strlen(tmp) - 1] == '"')
        tmp[strlen(tmp) - 1] = 0;
      fd.name = tmp;
      delS(tmp);
    }
    imap->_folders.add(fd);
  }
}

void ESP_Mail_Client::handleExamine(IMAPSession *imap, char *buf)
{
  char *tmp = nullptr;
  int p1, p2;

  if (imap->_mbif._msgCount == 0)
  {
    p1 = strposP(buf, esp_mail_str_199, 0);
    if (p1 != -1)
    {
      tmp = newS(p1);
      strncpy(tmp, buf + 2, p1 - 1);
      imap->_mbif._msgCount = atoi(tmp);
      delS(tmp);
      return;
    }
  }

  if (imap->_mbif._flags.size() == 0)
  {
    p1 = strposP(buf, esp_mail_imap_response_5, 0);
    if (p1 != -1)
    {
      p1 = strposP(buf, esp_mail_str_198, 0);
      if (p1 != -1)
      {
        p2 = strposP(buf, esp_mail_str_192, p1 + 1);
        if (p2 != -1)
        {
          tmp = newS(p2 - p1);
          strncpy(tmp, buf + p1 + 1, p2 - p1 - 1);
          char *stk = strP(esp_mail_str_131);
          char *end_token;
          char *token = strtok_r(tmp, stk, &end_token);
          while (token != NULL)
          {
            imap->_mbif.addFlag(token);
            token = strtok_r(NULL, stk, &end_token);
          }
          if (token)
            delS(token);
          delS(tmp);
          delS(stk);
        }
      }
      return;
    }
  }

  if (imap->_nextUID.length() == 0)
  {
    p1 = strposP(buf, esp_mail_str_200, 0);
    if (p1 != -1)
    {
      p2 = strposP(buf, esp_mail_str_156, p1 + strlen_P(esp_mail_str_200));
      if (p2 != -1)
      {
        tmp = newS(p2 - p1 - strlen_P(esp_mail_str_200) + 1);
        strncpy(tmp, buf + p1 + strlen_P(esp_mail_str_200), p2 - p1 - strlen_P(esp_mail_str_200));
        imap->_nextUID = tmp;
        imap->_mbif._nextUID = atoi(tmp);
        delS(tmp);
      }
      return;
    }
  }
}

bool ESP_Mail_Client::handleIMAPError(IMAPSession *imap, int err, bool ret)
{
  if (err < 0)
  {
    errorStatusCB(imap, err);

    if (imap->_headers.size() > 0)
    {
      if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) && (imap->_config->download.attachment || imap->_config->download.inlineImg))
      {
        if (cHeader(imap)->part_headers.size() > 0)
          cPart(imap)->download_error = imap->errorReason().c_str();
      }
      else
        cHeader(imap)->error_msg = imap->errorReason().c_str();

      cHeader(imap)->error = true;
    }
  }

  if (imap->_tcpConnected)
    closeTCP(imap);

  imap->_cbData.empty();

  return ret;
}

unsigned char *ESP_Mail_Client::decodeBase64(const unsigned char *src, size_t len, size_t *out_len)
{

  unsigned char *out, *pos, block[4], tmp;
  size_t i, count, olen;
  int pad = 0;
  size_t extra_pad;

  unsigned char *dtable = new unsigned char[256];

  memset(dtable, 0x80, 256);

  for (i = 0; i < sizeof(b64_index_table) - 1; i++)
    dtable[b64_index_table[i]] = (unsigned char)i;
  dtable['='] = 0;

  count = 0;
  for (i = 0; i < len; i++)
  {
    if (dtable[src[i]] != 0x80)
      count++;
  }

  if (count == 0)
    goto exit;
  extra_pad = (4 - count % 4) % 4;

  olen = (count + extra_pad) / 4 * 3;
  pos = out = (unsigned char *)malloc(olen);
  if (out == NULL)
    goto exit;

  count = 0;
  for (i = 0; i < len + extra_pad; i++)
  {
    unsigned char val;

    if (i >= len)
      val = '=';
    else
      val = src[i];
    tmp = dtable[val];
    if (tmp == 0x80)
      continue;

    if (val == '=')
      pad++;
    block[count] = tmp;
    count++;
    if (count == 4)
    {
      *pos++ = (block[0] << 2) | (block[1] >> 4);
      *pos++ = (block[1] << 4) | (block[2] >> 2);
      *pos++ = (block[2] << 6) | block[3];
      count = 0;
      if (pad)
      {
        if (pad == 1)
          pos--;
        else if (pad == 2)
          pos -= 2;
        else
        {
          free(out);
          goto exit;
        }
        break;
      }
    }
  }

  *out_len = pos - out;
  delete[] dtable;
  return out;
exit:
  delete[] dtable;
  return nullptr;
}

std::string ESP_Mail_Client::encodeBase64Str(const unsigned char *src, size_t len)
{
  return encodeBase64Str((uint8_t *)src, len);
}

std::string ESP_Mail_Client::encodeBase64Str(uint8_t *src, size_t len)
{
  std::string outStr;
  unsigned char *out, *pos;
  const unsigned char *end, *in;
  size_t olen = 4 * ((len + 2) / 3);
  if (olen < len)
    return outStr;

  outStr.resize(olen);
  out = (unsigned char *)&outStr[0];

  end = src + len;
  in = src;
  pos = out;

  while (end - in >= 3)
  {
    *pos++ = b64_index_table[in[0] >> 2];
    *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
    *pos++ = b64_index_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
    *pos++ = b64_index_table[in[2] & 0x3f];
    in += 3;
  }

  if (end - in)
  {
    *pos++ = b64_index_table[in[0] >> 2];
    if (end - in == 1)
    {
      *pos++ = b64_index_table[(in[0] & 0x03) << 4];
      *pos++ = '=';
    }
    else
    {
      *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
      *pos++ = b64_index_table[(in[1] & 0x0f) << 2];
    }
    *pos++ = '=';
  }

  return outStr;
}

void ESP_Mail_Client::encodeQP(const char *buf, char *out)
{
  int n = 0, p = 0, pos = 0;
  assert(buf);
  for (n = 0; *buf; buf++)
  {
    if (n >= 73 && *buf != 10 && *buf != 13)
    {
      p = sprintf(out + pos, "=\r\n");
      pos += p;
      n = 0;
    }

    if (*buf == 10 || *buf == 13)
    {
      strcat_c(out, *buf);
      pos++;
      n = 0;
    }
    else if (*buf < 32 || *buf == 61 || *buf > 126)
    {
      p = sprintf(out + pos, "=%02X", (unsigned char)*buf);
      n += p;
      pos += p;
    }
    else if (*buf != 32 || (*(buf + 1) != 10 && *(buf + 1) != 13))
    {
      strcat_c(out, *buf);
      n++;
      pos++;
    }
    else
    {
      p = sprintf(out + pos, "=20");
      n += p;
      pos += p;
    }
  }
}

bool ESP_Mail_Client::sendBase64(SMTPSession *smtp, SMTP_Message *msg, const unsigned char *data, size_t len, bool flashMem, const char *filename, bool report)
{
  bool ret = false;
  const unsigned char *end, *in;

  size_t olen = 4 * ((len + 2) / 3);

  if (olen < len)
    return false;

  end = data + len;
  in = data;

  size_t chunkSize = 936;
  size_t byteAdded = 0;
  size_t byteSent = 0;

  int dByte = 0;
  unsigned char *buf = new unsigned char[chunkSize];
  memset(buf, 0, chunkSize);

  unsigned char *tmp = new unsigned char[3];
  int bcnt = 0;
  int pg = 0, _pg = 0;

  if (report)
    uploadReport(filename, bcnt);

  while (end - in >= 3)
  {

    memset(tmp, 0, 3);
    if (flashMem)
      memcpy_P(tmp, in, 3);
    else
      memcpy(tmp, in, 3);
    bcnt += 3;

    buf[byteAdded++] = b64_index_table[tmp[0] >> 2];
    buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)];
    buf[byteAdded++] = b64_index_table[((tmp[1] & 0x0f) << 2) | (tmp[2] >> 6)];
    buf[byteAdded++] = b64_index_table[tmp[2] & 0x3f];
    dByte += 4;
    if (dByte == BASE64_CHUNKED_LEN)
    {
      if (byteAdded + 1 < chunkSize)
      {
        buf[byteAdded++] = 0x0d;
        buf[byteAdded++] = 0x0a;
      }
      dByte = 0;
    }
    if (byteAdded >= chunkSize - 4)
    {
      byteSent += byteAdded;

      if (!bdat(smtp, msg, byteAdded, false))
        goto ex;

      if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        goto ex;
      memset(buf, 0, chunkSize);
      byteAdded = 0;
    }
    in += 3;

    if (report)
    {
      pg = (float)(100.0f * bcnt / len);
      if (pg != _pg)
        uploadReport(filename, pg);
      _pg = pg;
    }
  }

  if (byteAdded > 0)
  {
    if (!bdat(smtp, msg, byteAdded, false))
      goto ex;

    if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      goto ex;
  }

  if (end - in)
  {
    memset(buf, 0, chunkSize);
    byteAdded = 0;
    memset(tmp, 0, 3);
    if (flashMem)
    {
      if (end - in == 1)
        memcpy_P(tmp, in, 1);
      else
        memcpy_P(tmp, in, 2);
    }
    else
    {
      if (end - in == 1)
        memcpy(tmp, in, 1);
      else
        memcpy(tmp, in, 2);
    }

    buf[byteAdded++] = b64_index_table[tmp[0] >> 2];
    if (end - in == 1)
    {
      buf[byteAdded++] = b64_index_table[(tmp[0] & 0x03) << 4];
      buf[byteAdded++] = '=';
    }
    else
    {
      buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)];
      buf[byteAdded++] = b64_index_table[(tmp[1] & 0x0f) << 2];
    }
    buf[byteAdded++] = '=';

    if (!bdat(smtp, msg, byteAdded, false))
      goto ex;

    if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      goto ex;
    memset(buf, 0, chunkSize);
  }

  if (report && _pg < 100)
    uploadReport(filename, 100);

  ret = true;
ex:
  if (report)
    esp_mail_debug("");
  delete[] tmp;
  delete[] buf;
  return ret;
}

bool ESP_Mail_Client::sendBase64Stream(SMTPSession *smtp, SMTP_Message *msg, File file, const char *filename, bool report)
{
  bool ret = false;

  if (!file)
  {
    errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
    return false;
  }

  size_t chunkSize = 936;
  size_t byteAdded = 0;
  size_t byteSent = 0;

  unsigned char *buf = new unsigned char[chunkSize];
  memset(buf, 0, chunkSize);

  size_t len = file.size();
  size_t fbufIndex = 0;
  unsigned char *fbuf = new unsigned char[3];

  int dByte = 0;

  int bcnt = 0;
  int pg = 0, _pg = 0;

  if (report)
    uploadReport(filename, bcnt);

  while (file.available())
  {
    memset(fbuf, 0, 3);
    if (len - fbufIndex >= 3)
    {
      bcnt += 3;
      size_t readLen = file.read(fbuf, 3);
      if (readLen != 3)
      {
        errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
        break;
      }

      buf[byteAdded++] = b64_index_table[fbuf[0] >> 2];
      buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)];
      buf[byteAdded++] = b64_index_table[((fbuf[1] & 0x0f) << 2) | (fbuf[2] >> 6)];
      buf[byteAdded++] = b64_index_table[fbuf[2] & 0x3f];
      dByte += 4;
      if (dByte == BASE64_CHUNKED_LEN)
      {
        if (byteAdded + 1 < chunkSize)
        {
          buf[byteAdded++] = 0x0d;
          buf[byteAdded++] = 0x0a;
        }
        dByte = 0;
      }
      if (byteAdded >= chunkSize - 4)
      {
        byteSent += byteAdded;

        if (!bdat(smtp, msg, byteAdded, false))
          goto ex;

        if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
          goto ex;

        memset(buf, 0, chunkSize);
        byteAdded = 0;
      }
      fbufIndex += 3;

      if (report)
      {
        pg = (float)(100.0f * bcnt / len);
        if (pg != _pg)
          uploadReport(filename, pg);
        _pg = pg;
      }
    }
    else
    {
      size_t readLen = file.read(fbuf, len - fbufIndex);
      if (readLen != len - fbufIndex)
      {
        errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR);
        break;
      }
    }
  }

  file.close();
  if (byteAdded > 0)
  {
    if (!bdat(smtp, msg, byteAdded, false))
      goto ex;

    if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      goto ex;
  }

  if (len - fbufIndex > 0)
  {
    memset(buf, 0, chunkSize);
    byteAdded = 0;
    buf[byteAdded++] = b64_index_table[fbuf[0] >> 2];
    if (len - fbufIndex == 1)
    {
      buf[byteAdded++] = b64_index_table[(fbuf[0] & 0x03) << 4];
      buf[byteAdded++] = '=';
    }
    else
    {
      buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)];
      buf[byteAdded++] = b64_index_table[(fbuf[1] & 0x0f) << 2];
    }
    buf[byteAdded++] = '=';

    if (!bdat(smtp, msg, byteAdded, false))
      goto ex;

    if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      goto ex;
  }
  ret = true;

  if (report && _pg < 100)
    uploadReport(filename, 100);

ex:
  delete[] buf;
  delete[] fbuf;
  file.close();
  return ret;
}

IMAPSession::IMAPSession() {}
IMAPSession::~IMAPSession()
{
  empty();
  _caCert.reset();
  _caCert = nullptr;
}

bool IMAPSession::closeSession()
{
  if (!_tcpConnected)
    return false;
#if defined(ESP32)
  /**
   * 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::connect(ESP_Mail_Session *sesssion, IMAP_Config *config)
{
  if (_tcpConnected)
    MailClient.closeTCP(this);

  _sesson_cfg = sesssion;
  _config = config;
  _caCert = nullptr;

  if (strlen(_sesson_cfg->certificate.cert_data) > 0)
    _caCert = std::shared_ptr<const char>(_sesson_cfg->certificate.cert_data);

  if (strlen(_sesson_cfg->certificate.cert_file) > 0)
  {
    if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk)
      MailClient._sdOk = MailClient.sdTest();
    if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk)
#if defined(ESP32)
      MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
      MailClient._flashOk = ESP_MAIL_FLASH_FS.begin();
#endif
  }

  return MailClient.imapAuth(this);
}

void IMAPSession::debug(int level)
{
  if (level > esp_mail_debug_level_0)
  {
    if (level > esp_mail_debug_level_3)
      level = esp_mail_debug_level_1;
    _debugLevel = level;
    _debug = true;
  }
  else
  {
    _debugLevel = esp_mail_debug_level_0;
    _debug = false;
  }
}

String IMAPSession::errorReason()
{
  std::string ret;

  if (_imapStatus.text.length() > 0)
    return _imapStatus.text.c_str();

  switch (_imapStatus.statusCode)
  {
  case IMAP_STATUS_SERVER_CONNECT_FAILED:
    MailClient.appendP(ret, esp_mail_str_38, true);
    break;
  case MAIL_CLIENT_ERROR_CONNECTION_LOST:
    MailClient.appendP(ret, esp_mail_str_221, true);
    break;
  case MAIL_CLIENT_ERROR_READ_TIMEOUT:
    MailClient.appendP(ret, esp_mail_str_258, true);
    break;
  case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED:
    MailClient.appendP(ret, esp_mail_str_305, true);
    break;
  case IMAP_STATUS_NO_MESSAGE:
    MailClient.appendP(ret, esp_mail_str_306, true);
    break;
  case IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT:
    MailClient.appendP(ret, esp_mail_str_93, true);
    break;
  case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP:
    MailClient.appendP(ret, esp_mail_str_132, true);
    break;
  case IMAP_STATUS_CLOSE_MAILBOX_FAILED:
    MailClient.appendP(ret, esp_mail_str_188, true);
    break;
  case IMAP_STATUS_OPEN_MAILBOX_FAILED:
    MailClient.appendP(ret, esp_mail_str_281, true);
    break;
  case IMAP_STATUS_LIST_MAILBOXS_FAILED:
    MailClient.appendP(ret, esp_mail_str_62, true);
    break;
  case IMAP_STATUS_NO_SUPPORTED_AUTH:
    MailClient.appendP(ret, esp_mail_str_42, true);
    break;
  case IMAP_STATUS_CHECK_CAPABILITIES_FAILED:
    MailClient.appendP(ret, esp_mail_str_63, true);
    break;
  case MAIL_CLIENT_ERROR_OUT_OF_MEMORY:
    MailClient.appendP(ret, esp_mail_str_186, true);
    break;
  case IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED:
    MailClient.appendP(ret, esp_mail_str_153, true);
    break;

  default:
    break;
  }
  return ret.c_str();
}

bool IMAPSession::selectFolder(const char *folderName, bool readOnly)
{
  if (_tcpConnected)
  {
    if (!openFolder(folderName, readOnly))
      return false;
  }
  else
  {
    _currentFolder = folderName;
  }
  return true;
}

bool IMAPSession::openFolder(const char *folderName, bool readOnly)
{
  if (!_tcpConnected)
    return false;
  if (readOnly)
    return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_examine, true);
  else
    return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_select, true);
}

bool IMAPSession::getFolders(FoldersCollection &folders)
{
  if (!_tcpConnected)
    return false;
  return getMailboxes(folders);
}

bool IMAPSession::closeFolder(const char *folderName)
{
  if (!_tcpConnected)
    return false;
  return closeMailbox();
}

void IMAPSession::checkUID()
{
  if (MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_140) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_212) ||
      MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_213) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_214) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_215) ||
      MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_216) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_217) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_218) ||
      MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_219) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_220))
    _config->fetch.uid = "*";
}

void IMAPSession::checkPath()
{
  std::string path = _config->storage.saved_path;
  if (path[0] != '/')
  {
    path = "/";
    path += _config->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 (ESP.getFreeHeap() < ESP_MAIL_MIN_MEM)
      continue;

    struct esp_mail_imap_msg_item_t itm;

    itm.UID = _headers[i].message_uid.c_str();
    itm.ID = _headers[i].message_id.c_str();
    itm.msgNo = _headers[i].message_no.c_str();
    itm.from = _headers[i].from.c_str();
    itm.fromCharset = _headers[i].from_charset.c_str();
    itm.to = _headers[i].to.c_str();
    itm.toCharset = _headers[i].to_charset.c_str();
    itm.cc = _headers[i].cc.c_str();
    itm.ccCharset = _headers[i].cc_charset.c_str();
    itm.subject = _headers[i].subject.c_str();
    itm.subjectCharset = _headers[i].subject_charset.c_str();
    itm.date = _headers[i].date.c_str();
    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)
{
  _readCallback = std::move(imapCallback);
}

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].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_attacment_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.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 sz = _headers[messageIndex].part_headers.size();
    int partIdx = 0;
    int cIdx = 0;
    IMAP_MSG_Item *_rfc822 = nullptr;
    if (sz > 0)
    {
      for (int i = 0; i < sz; i++)
      {
        if (_headers[messageIndex].part_headers[i].message_sub_type == esp_mail_imap_message_sub_type_rfc822)
        {
          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->from = _headers[messageIndex].part_headers[i].rfc822_header.from.c_str();
            _rfc822->sender = _headers[messageIndex].part_headers[i].rfc822_header.sender.c_str();
            _rfc822->to = _headers[messageIndex].part_headers[i].rfc822_header.to.c_str();
            _rfc822->cc = _headers[messageIndex].part_headers[i].rfc822_header.cc.c_str();
            _rfc822->bcc = _headers[messageIndex].part_headers[i].rfc822_header.bcc.c_str();
            _rfc822->return_path = _headers[messageIndex].part_headers[i].rfc822_header.return_path.c_str();
            _rfc822->reply_to = _headers[messageIndex].part_headers[i].rfc822_header.reply_to.c_str();
            _rfc822->subject = _headers[messageIndex].part_headers[i].rfc822_header.subject.c_str();
            _rfc822->comment = _headers[messageIndex].part_headers[i].rfc822_header.comment.c_str();
            _rfc822->keyword = _headers[messageIndex].part_headers[i].rfc822_header.keyword.c_str();
            _rfc822->date = _headers[messageIndex].part_headers[i].rfc822_header.date.c_str();
            _rfc822->messageID = _headers[messageIndex].part_headers[i].rfc822_header.messageID.c_str();
            _rfc822->text.charSet = "";
            _rfc822->text.content_type = "";
            _rfc822->text.transfer_encoding = "";
            _rfc822->html.charSet = "";
            _rfc822->html.content_type = "";
            _rfc822->html.transfer_encoding = "";
          }
          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_attacment_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.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()
{

  if (!MailClient.reconnect(this))
    return false;

  std::string s;

  if (_readCallback)
  {
    MailClient.appendP(s, esp_mail_str_210, true);
    s += _currentFolder;
    MailClient.appendP(s, esp_mail_str_96, false);
    MailClient.imapCB(this, "", false);
    MailClient.imapCB(this, s.c_str(), false);
  }

  if (_debug)
    MailClient.debugInfoP(esp_mail_str_197);

  if (MailClient.imapSendP(this, esp_mail_str_195, 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;

  _currentFolder.clear();
  _mailboxOpened = false;

  return true;
}

bool IMAPSession::openMailbox(const char *folder, esp_mail_imap_auth_mode mode, bool waitResponse)
{

  if (!MailClient.reconnect(this))
    return false;

  if (_currentFolder.length() > 0)
  {
    if (strcmp(_currentFolder.c_str(), folder) != 0)
    {
      if (!closeMailbox())
        return false;
    }
  }

  _currentFolder = folder;
  std::string s;
  if (_readCallback)
  {
    MailClient.appendP(s, esp_mail_str_61, true);
    s += _currentFolder;
    MailClient.appendP(s, esp_mail_str_96, false);
    MailClient.imapCB(this, "", false);
    MailClient.imapCB(this, s.c_str(), false);
  }

  if (_debug)
    MailClient.debugInfoP(esp_mail_str_248);

  if (mode == esp_mail_imap_mode_examine)
  {
    MailClient.appendP(s, esp_mail_str_135, true);
    _imap_cmd = esp_mail_imap_cmd_examine;
  }
  else if (mode == esp_mail_imap_mode_select)
  {
    MailClient.appendP(s, esp_mail_str_247, true);
    _imap_cmd = esp_mail_imap_cmd_select;
  }
  s += _currentFolder;
  MailClient.appendP(s, esp_mail_str_136, false);
  if (MailClient.imapSend(this, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;
  if (waitResponse)
  {

    if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_OPEN_MAILBOX_FAILED, false))
      return false;
  }

  if (mode == esp_mail_imap_mode_examine)
    _readOnlyMode = true;
  else if (mode == esp_mail_imap_mode_select)
    _readOnlyMode = false;

  _mailboxOpened = true;

  return true;
}

bool IMAPSession::getMailboxes(FoldersCollection &folders)
{
  _folders.clear();

  if (_readCallback)
  {
    MailClient.imapCB(this, "", false);
    MailClient.imapCBP(this, esp_mail_str_58, false);
  }

  if (_debug)
    MailClient.debugInfoP(esp_mail_str_230);

  if (MailClient.imapSendP(this, esp_mail_str_133, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;
  _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_list;
  if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false))
    return false;

  folders = _folders;
  return true;
}

bool IMAPSession::checkCapability()
{
  if (_readCallback)
  {
    MailClient.imapCB(this, "", false);
    MailClient.imapCBP(this, esp_mail_str_64, false);
  }

  if (_debug)
    MailClient.debugInfoP(esp_mail_str_65);

  if (MailClient.imapSendP(this, esp_mail_str_2, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_capability;
  if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CHECK_CAPABILITIES_FAILED, false))
    return false;

  return true;
}

bool IMAPSession::createFolder(const char *folderName)
{
  if (_debug)
  {
    MailClient.imapCB(this, "", false);
    MailClient.debugInfoP(esp_mail_str_320);
  }

  std::string cmd;
  MailClient.appendP(cmd, esp_mail_str_322, true);
  cmd += folderName;

  if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_create;
  if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
    return false;

  return true;
}

bool IMAPSession::deleteFolder(const char *folderName)
{
  if (_debug)
  {
    MailClient.imapCB(this, "", false);
    MailClient.debugInfoP(esp_mail_str_321);
  }

  std::string cmd;
  MailClient.appendP(cmd, esp_mail_str_323, true);
  cmd += folderName;

  if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
    return false;

  _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_delete;
  if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
    return false;

  return true;
}

bool IMAPSession::deleteMessages(MessageList *toDelete, bool expunge)
{
  if (toDelete->_list.size() > 0)
  {

    if (!selectFolder(_currentFolder.c_str(), false))
      return false;

    if (_debug)
    {
      MailClient.imapCB(this, "", false);
      MailClient.debugInfoP(esp_mail_str_316);
    }

    std::string cmd;
    char *tmp = nullptr;
    MailClient.appendP(cmd, esp_mail_str_249, true);
    for (size_t i = 0; i < toDelete->_list.size(); i++)
    {
      if (i > 0)
        MailClient.appendP(cmd, esp_mail_str_263, false);
      tmp = MailClient.intStr(toDelete->_list[i]);
      cmd += tmp;
      MailClient.delS(tmp);
    }
    MailClient.appendP(cmd, esp_mail_str_315, false);

    if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store;
    if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
      return false;

    if (expunge)
    {
      if (MailClient.imapSendP(this, esp_mail_str_317, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
        return false;

      _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_expunge;
      if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
        return false;
    }
  }

  return true;
}

bool IMAPSession::copyMessages(MessageList *toCopy, const char *dest)
{
  if (toCopy->_list.size() > 0)
  {

    if (!selectFolder(_currentFolder.c_str(), false))
      return false;

    if (_debug)
    {
      MailClient.imapCB(this, "", false);
      std::string s;
      MailClient.appendP(s, esp_mail_str_318, true);
      s += dest;
      esp_mail_debug(s.c_str());
    }

    std::string cmd;
    char *tmp = nullptr;
    MailClient.appendP(cmd, esp_mail_str_319, true);
    for (size_t i = 0; i < toCopy->_list.size(); i++)
    {
      if (i > 0)
        MailClient.appendP(cmd, esp_mail_str_263, false);
      tmp = MailClient.intStr(toCopy->_list[i]);
      cmd += tmp;
      MailClient.delS(tmp);
    }
    MailClient.appendP(cmd, esp_mail_str_131, false);
    cmd += dest;

    if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED)
      return false;

    _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store;
    if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false))
      return false;
  }

  return true;
}

#if defined(ESP8266)
void ESP_Mail_Client::setClock(float offset)
{
  if (WiFi.status() != WL_CONNECTED)
    WiFi.reconnect();

  time_t now = time(nullptr);

  _clockReady = now > ESP_MAIL_CLIENT_VALID_TS;

  if (!_clockReady)
  {
    char *server1 = strP(esp_mail_str_283);
    char *server2 = strP(esp_mail_str_296);

    configTime(offset * 3600, 0, server1, server2);

    now = time(nullptr);
    uint8_t attempts = 0;
    while (now < ESP_MAIL_CLIENT_VALID_TS)
    {
      now = time(nullptr);
      attempts++;
      if (attempts > 200 || now > ESP_MAIL_CLIENT_VALID_TS)
        break;
      delay(100);
    }

    delS(server1);
    delS(server2);
  }

  _clockReady = now > ESP_MAIL_CLIENT_VALID_TS;
}
#endif

void IMAPSession::empty()
{
  std::string().swap(_nextUID);
  clearMessageData();
}

void IMAPSession::clearMessageData()
{
  for (size_t i = 0; i < _headers.size(); i++)
  {
    _headers[i].part_headers.clear();
    std::vector<struct esp_mail_message_part_info_t>().swap(_headers[i].part_headers);
  }
  std::vector<struct esp_mail_message_header_t>().swap(_headers);
  std::vector<uint32_t>().swap(_msgNum);
  _folders.clear();
  _mbif._flags.clear();
  _mbif._searchCount = 0;
}

SMTPSession::SMTPSession()
{
}

SMTPSession::~SMTPSession()
{
  closeSession();
  _caCert.reset();
  _caCert = nullptr;
}

bool SMTPSession::connect(ESP_Mail_Session *config)
{
  if (_tcpConnected)
    MailClient.closeTCP(this);

  _sesson_cfg = config;
  _caCert = nullptr;

  if (strlen(_sesson_cfg->certificate.cert_data) > 0)
    _caCert = std::shared_ptr<const char>(_sesson_cfg->certificate.cert_data);

  if (strlen(_sesson_cfg->certificate.cert_file) > 0)
  {
    if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk)
      MailClient._sdOk = MailClient.sdTest();
    if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk)
#if defined(ESP32)
      MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH);
#elif defined(ESP8266)
      MailClient._flashOk = ESP_MAIL_FLASH_FS.begin();
#endif
  }
  return MailClient.smtpAuth(this);
}

void SMTPSession::debug(int level)
{
  if (level > esp_mail_debug_level_0)
  {
    if (level > esp_mail_debug_level_3)
      level = esp_mail_debug_level_1;
    _debugLevel = level;
    _debug = true;
  }
  else
  {
    _debugLevel = esp_mail_debug_level_0;
    _debug = false;
  }
}

String SMTPSession::errorReason()
{
  std::string ret;

  switch (_smtpStatus.statusCode)
  {
  case SMTP_STATUS_SERVER_CONNECT_FAILED:
    MailClient.appendP(ret, esp_mail_str_38, true);
    break;
  case SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED:
    MailClient.appendP(ret, esp_mail_str_39, true);
    break;
  case SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED:
    MailClient.appendP(ret, esp_mail_str_39, true);
    break;
  case SMTP_STATUS_AUTHEN_NOT_SUPPORT:
    MailClient.appendP(ret, esp_mail_str_42, true);
    break;
  case SMTP_STATUS_AUTHEN_FAILED:
    MailClient.appendP(ret, esp_mail_str_43, true);
    break;
  case SMTP_STATUS_USER_LOGIN_FAILED:
    MailClient.appendP(ret, esp_mail_str_43, true);
    break;
  case SMTP_STATUS_PASSWORD_LOGIN_FAILED:
    MailClient.appendP(ret, esp_mail_str_47, true);
    break;
  case SMTP_STATUS_SEND_HEADER_SENDER_FAILED:
    MailClient.appendP(ret, esp_mail_str_48, true);
    break;
  case SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED:
    MailClient.appendP(ret, esp_mail_str_222, true);
    break;
  case SMTP_STATUS_SEND_BODY_FAILED:
    MailClient.appendP(ret, esp_mail_str_49, true);
    break;
  case MAIL_CLIENT_ERROR_CONNECTION_LOST:
    MailClient.appendP(ret, esp_mail_str_221, true);
    break;
  case MAIL_CLIENT_ERROR_READ_TIMEOUT:
    MailClient.appendP(ret, esp_mail_str_258, true);
    break;
  case MAIL_CLIENT_ERROR_FILE_IO_ERROR:
    MailClient.appendP(ret, esp_mail_str_282, true);
    break;
  case SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED:
    MailClient.appendP(ret, esp_mail_str_293, true);
    break;
  case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED:
    MailClient.appendP(ret, esp_mail_str_305, true);
    break;
  case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP:
    MailClient.appendP(ret, esp_mail_str_132, true);
    break;
  case SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED:
    MailClient.appendP(ret, esp_mail_str_206, true);
    break;
  case SMTP_STATUS_NO_VALID_SENDER_EXISTED:
    MailClient.appendP(ret, esp_mail_str_205, true);
    break;
  case MAIL_CLIENT_ERROR_OUT_OF_MEMORY:
    MailClient.appendP(ret, esp_mail_str_186, true);
    break;
  case SMTP_STATUS_NO_SUPPORTED_AUTH:
    MailClient.appendP(ret, esp_mail_str_42, true);
    break;

  default:
    break;
  }

  if (_smtpStatus.text.length() > 0 && ret.length() == 0)
  {
    MailClient.appendP(ret, esp_mail_str_312, true);
    char *code = MailClient.intStr(_smtpStatus.respCode);
    ret += code;
    MailClient.delS(code);
    MailClient.appendP(ret, esp_mail_str_313, false);
    ret += _smtpStatus.text;
    return ret.c_str();
  }
  return ret.c_str();
}

bool SMTPSession::closeSession()
{
  if (!_tcpConnected)
    return false;

  if (_sendCallback)
  {
    _cbData._sentSuccess = _sentSuccessCount;
    _cbData._sentFailed = _sentFailedCount;
    MailClient.smtpCB(this, "");
    MailClient.smtpCBP(this, esp_mail_str_128);
  }

  if (_debug)
    MailClient.debugInfoP(esp_mail_str_245);

  bool ret = true;

/* Sign out */
#if defined(ESP32)
  /**
   * The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure
   * The client disposed without memory released after the server close
   * the connection due to QUIT command, which caused the memory leaks.
  */
  MailClient.smtpSendP(this, esp_mail_str_7, true);
  _smtp_cmd = esp_mail_smtp_cmd_logout;
  ret = MailClient.handleSMTPResponse(this, esp_mail_smtp_status_code_221, SMTP_STATUS_SEND_BODY_FAILED);
#endif

  if (ret)
  {

    if (_sendCallback)
    {
      MailClient.smtpCB(this, "");
      MailClient.smtpCBP(this, esp_mail_str_129, false);
    }

    if (_debug)
      MailClient.debugInfoP(esp_mail_str_246);

    if (_sendCallback)
      MailClient.smtpCB(this, "", true);
  }

  return MailClient.handleSMTPError(this, 0, ret);
}

void SMTPSession::callback(smtpStatusCallback smtpCallback)
{
  _sendCallback = std::move(smtpCallback);
}

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()
{
  std::string().swap(_info);
}

SMTP_Status::SMTP_Status()
{
}

SMTP_Status::~SMTP_Status()
{
  empty();
}

const char *SMTP_Status::info()
{
  return _info.c_str();
}

bool SMTP_Status::success()
{
  return _success;
}

size_t SMTP_Status::completedCount()
{
  return _sentSuccess;
}

size_t SMTP_Status::failedCount()
{
  return _sentFailed;
}

void SMTP_Status::empty()
{
  std::string().swap(_info);
}

ESP_Mail_Client MailClient = ESP_Mail_Client();

#endif /* ESP_Mail_Client_CPP */
