diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 86917c8d6..6ec59387b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,5 +8,5 @@ - [ ] Only one feature/fix was added per PR. - [ ] The code change is tested and works on core Tasmota_core - [ ] The code change is tested and works on core ESP32 - - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** + - [ ] The code change pass CI tests. **Your PR cannot be merged unless tests pass** - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). diff --git a/.github/workflows/CI_github.yml b/.github/workflows/CI_github.yml new file mode 100644 index 000000000..806952c3a --- /dev/null +++ b/.github/workflows/CI_github.yml @@ -0,0 +1,441 @@ +name: Tasmota CI + +on: [push, pull_request] + +jobs: + tasmota: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota + + tasmota-minimal: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-minimal + + tasmota-lite: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-lite + + tasmota-knx: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-knx + + tasmota-sensors: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-sensors + + + tasmota-display: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-display + + tasmota-ir: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ir + + tasmota-BG: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BG + + tasmota-BR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-BR + + tasmota-CN: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CN + + tasmota-CZ: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-CZ + + tasmota-DE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-DE + + tasmota-ES: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-ES + + + tasmota-FR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-FR + + tasmota-GR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-GR + + tasmota-HE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HE + + tasmota-HU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-HU + + tasmota-IT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-IT + + tasmota-KO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-KO + + tasmota-NL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-NL + + tasmota-PL: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PL + + tasmota-PT: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-PT + + tasmota-RO: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RO + + tasmota-RU: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-RU + + tasmota-SE: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SE + + tasmota-SK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-SK + + tasmota-TR: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TR + + tasmota-TW: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-TW + + tasmota-UK: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up Python + uses: actions/setup-python@v1 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio upgrade --dev + platformio update + - name: Run PlatformIO + run: platformio run -e tasmota-UK diff --git a/.travis.yml b/.travis.yml.off similarity index 100% rename from .travis.yml rename to .travis.yml.off diff --git a/README.md b/README.md index 09e2d4ae8..142a8037a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases [![Dev Version](https://img.shields.io/badge/development%20version-v8.2.x.x-blue.svg)](https://github.com/arendst/Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) -[![Build Status](https://img.shields.io/travis/arendst/Tasmota.svg)](https://travis-ci.org/arendst/Tasmota) +![Tasmota CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20CI/badge.svg) See [tasmota/CHANGELOG.md](tasmota/CHANGELOG.md) for detailed change information. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d7e8d2bf1..832604ba2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -61,7 +61,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034) - Change remove floating point libs from IRAM - Change remove MQTT Info messages on restart for DeepSleep Wake (#8044) -- Change IRremoteESP8266 library updated to v2.7.5 +- Change IRremoteESP8266 library updated to v2.7.6 - Fix possible Relay toggle on (OTA) restart - Fix PWM flickering during wifi connection (#8046) - Fix Zigbee sending wrong Sat value with Hue emulation @@ -78,6 +78,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add command ``SetOption73 1`` for button decoupling and send multi-press and hold MQTT messages by Federico Leoni (#8235) - Add command ``SetOption90 1`` to disable non-json MQTT messages (#8044) - Add command ``SetOption91 1`` to enable fading at startup / power on +- Add command ``SetOption92 1`` to set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) - Add command ``Sensor10 0/1/2`` to control BH1750 resolution - 0 = High (default), 1 = High2, 2 = Low (#8016) - Add command ``Sensor10 31..254`` to control BH1750 measurement time which defaults to 69 (#8016) - Add command ``Sensor18 0..32000`` to control PMS5003 sensor interval to extend lifetime by Gene Ruebsamen (#8128) @@ -95,3 +96,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add console command history (#7483, #8015) - Add quick wifi reconnect using saved AP parameters when ``SetOption56 0`` (#3189) - Add more accuracy to GPS NTP server (#8088) +- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt diff --git a/libesp32/ESP32-Mail-Client/LICENSE b/libesp32/ESP32-Mail-Client/LICENSE new file mode 100755 index 000000000..2b2f9d774 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 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. diff --git a/libesp32/ESP32-Mail-Client/README.md b/libesp32/ESP32-Mail-Client/README.md new file mode 100755 index 000000000..bd4cb6328 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/README.md @@ -0,0 +1,2138 @@ +# Mail Client Arduino Library for ESP32 v 2.1.4 + +This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download via SMTP and IMAP servers. + +The library was test and work well with ESP32s based module. + +Copyright (c) 2019 K. Suwatchai (Mobizt). + +![ESP32 Mail](/media/images/esp32-mail.png) + +## Tested Devices + +This following devices were tested and work well. + + * Sparkfun ESP32 Thing + * NodeMCU-32 + * WEMOS LOLIN32 + * TTGO T8 V1.8 + * M5Stack ESP32 + + + +## Features + +* Support Email sending with or without attachment via IMAP server. + +* Support SSL/TLS and STARTTLS protocols. + +* Working with SD card allows large file attachment supported or SPIFFS for small file size attachment. + +* Support Email reading via search and fetch modes (with or without attachment downloads). + +* Support large attachment download via SD card or SPIFFS for small file size attachment. + +* Message text and its header are able to download and save to SD card or SPIFFS. + +* Support Email message fetch and search via IMAP command as in RFC 3501 (depending on IMAP server implementation). + +* Support Ethernet. + +* Built-in Time function. + + + + +## Prerequisites + + +For library version 1.2.0 or newer, STARTTLS was supported and can be enable automatically when port 587 for SMTP was used or can set manually thrugh smtpData.setSTARTTLS(true) and for IMAP through imapData.setSTARTTLS(true). + + + +## Installing + + +Click on **Clone or download** dropdown at the top of repository, select **Download ZIP** and save file on your computer. + +From Arduino IDE, goto menu **Sketch** -> **Include Library** -> **Add .ZIP Library...** and choose **ESP32-Mail-Client-master.zip** that previously downloaded. + +Go to menu **Files** -> **Examples** -> **ESP32-Mail-Client-master** and choose one from examples + + + +## Usages + + +__Declaration and Initialization__ + + + +**The first thing to do to use this library.** + +```C++ + + +//1. Include ESP32 Mail Client library (this library) + +#include "ESP32_MailClient.h" + + +//2. For sending Email, declare Email Sending data object in global scope. +SMTPData smtpData; + +//Or + +//For receiving Email, declare Email receiving data object in global scope. +IMAPData imapData; + + +//3 Setup SMTP server login credential in setup() + +smtpData.setLogin("smtp.gmail.com", 587, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + +//Or + +//Setup IMAP server login credential in setup() + +imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + + +//4 For SMTP, set some custom message header (optional) +smtpData.addCustomMessageHeader("Date: Sat, 10 Aug 2019 21:39:56 -0700 (PDT)"); + +smtpData.addCustomMessageHeader("Message-ID: <10000.30000@gmail.com>"); + + +//5 To debug for SMTP + +smtpData.setDebug(true); + +//Or IMAP +imapData.setDebug(true); + + +//6. Send Email +MailClient.sendMail(smtpData)); + +//Or Receive Email + +MailClient.readdMail(imapData)); + + + + + +``` + +___ + + +__Send and Receive Email__ + + +**Compose Email** + +This library allows you to set sender, recipient, importance (priority), CC, BCC and attachment data (binary or from SD card file). + +To set sender, use `smtpData.setSender` e.g. `smtpData.setSender("Jarvis", "SOME_EMAIL_ACCOUNT@SOME_EMAIL.com")`. + +To set priority, use `smtpData.setPriority` e.g. `smtpData.setPriority("High")`. + +To set message subject, use `smtpData.setSubject` e.g. `smtpData.setSubject("ESP32 Send Mail Test")`. + +To set message text, use `smtpData.setMessage` e.g. `smtpData.setMessage("This is plain text message", false);`. + +To set sender, use `smtpData.addRecipient` e.g. `smtpData.addRecipient("SOME_RECIPIENT@SOME_MAIL.com")`. + +To add attachment, use `smtpData.addAttachData` e.g. `smtpData.addAttachData("test.png", "image/png", (uint8_t *)imageData, sizeof imageData);`. + + +When completed all required message data, sending Email `MailClient.sendMail(smtpData)`. + + + +**Get Email** + +To read or receive Email, mailbox folder should be assigned via `imapData.setFolder` e.g. `imapData.setFolder("INBOX")`. + +Then set search criteria to search specified mailbox folder via `imapData.setSearchCriteria` e.g. `imapData.setSearchCriteria("UID SEARCH ALL")`. + +Then set search limit to limut the memory and time usages `imapData.setSearchLimit`. + +From search criteria, UID of message will be available to fetch or read. + +Whit search, body message and attachment can be ignore to reduce the network data usage. + +Begin receive Email `MailClient.readMail(imapData)`. + +From above settings, you will get the following header information + +Messsage UID via `imapData.getUID`. + +Messsage ID via `imapData.getMessageID`. + +Accept Language via `imapData.getAcceptLanguage`. + +Content Language via `imapData.getContentLanguage`. + +Sender via `imapData.getFrom`. + +Sender Charset via `imapData.getFromCharset`. + +Recipient via `imapData.getTo`. + +Recipient Charset via `imapData.getToCharset`. + +CC via `imapData.getCC`. + +CC Charset via `imapData.getCCCharset`. + +Date via `imapData.getDate`. + +Subject via `imapData.getSubject`. + +Subject Charset via `imapData.getSubjectCharset`. + +In addition, by setting search criteria, the following infomation are available. + +Mailbox folder count via `imapData.getFolderCount`. + +Mailbox folder name via `imapData.getFolder`. + +Supported flags count via `imapData.getFlagCount`. + +Supported flags name via `imapData.getFlag`. + +Total message in folder via `imapData.totalMessages`. + +Total message from search result via `imapData.searchCount`. + +Available message from search result (limited by `imapData.setSearchLimit`) via `imapData.availableMessages`. + +When fetch specific message via `imapData.setFetchUID`, availability of attachment file can be determined via +`imapData.getAttachmentCount` for that message which will be automatically download by setting `imapData.setDownloadAttachment(true)` +prior to `MailClient.readMail`. + + + +See [full examples](https://github.com/mobizt/ESP32-Mail-Client/tree/master/examples) for all features usages. + + + + + +## All Supported Functions + + +**These are all functions available from the library and the descriptions.** + + +__Global functions__ + + +**Sending Email via SMTP server.** + +param *`smtpData`* - SMTP Data object to hold data and instances. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool sendMail(SMTPData &smtpData); +``` + + + + +**Reading Email via IMAP server.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool readMail(IMAPData &imapData); +``` + + + + + +**Set the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool setFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + +**Add the argument to the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool addFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + + +**Remove the argument from the Flags for message.** + +param *`imapData`* - IMAP Data object to hold data and instances. + +param *`msgUID`* - The UID of message. + +param *`flags`* - The flag list. + +return - *`Boolean`* type status indicates the success of operation. + +```C++ +bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); +``` + + + + + +**Get the Email sending error details.** + +return - *`Error details string (String object).`* + +```C++ +String smtpErrorReason(); +``` + + + + +**Get the Email reading error details.** + +return - *`Error details string (String object).`* + +```C++ +String imapErrorReason(); +``` + + + + + +**Init SD card with GPIO pins.** + +param *`sck`* -SPI Clock pin. + +param *`miso`* - SPI MISO pin. + +param *`m0si`* - SPI MOSI pin. + +param *`ss`* - SPI Chip/Slave Select pin. + +return *`Boolean`* type status indicates the success of operation. + + +```C++ +bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); +``` + + + + + + +**Init SD card with default GPIO pins.** + +return *`Boolean`* type status indicates the success of operation. + + +```C++ +bool sdBegin(void); +``` + + + + + + + +### IMAPData object call for receiving Email. + + +**Set the IMAP server login credentials.** + +param *`host`* - IMAP server e.g. imap.gmail.com. + +param *`port`* - IMAP port e.g. 993 for gmail. + +param *`loginEmail`* - The Email address of account. + +param *`loginPassword`* - The account password. + +param *`rootCA`* - Root CA certificate base64 string + +```C++ +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + +``` + + + + + +**Set STARTTLS mode to enable STARTTLS protocol.** + +param *`starttls`* - bool flag that enables STARTTLS mode + +```C++ +void setSTARTTLS(bool starttls); +``` + + + + + + +**Set debug print to serial.** + +param *`debug`* - bool flag to enable debug + +```C++ +void setDebug(bool debug); +``` + + + + + + +**Set the mailbox folder to search or fetch.** + +param *`folderName`* - Known mailbox folder. Default value is INBOX + +```C++ +void setFolder(const String &folderName); +``` + + + +**Set the maximum message buffer size for text/html result from search or fetch the message.** + +param *`size`* - The message size in byte. + +```C++ +void setMessageBufferSize(size_t size); +``` + + + +**Set the maximum attachment file size to be downloaded.** + +param *`size`* - The attachement file size in byte. + +```C++ +void setAttachmentSizeLimit(size_t size); +``` + + + +**Set the search criteria used in selected mailbox search.** + +In case of message UID was set via setFetchUID function, search operation will not process, + +you need to clear message UID by calling imapData.setFetchUID("") to clear. + +param *`criteria`* - Search criteria String. + +If folder is not set, the INBOX folder will be used + +Example: + +*`SINCE 10-Feb-2019`* will search all messages that received since 10 Feb 2019 + +*`UID SEARCH ALL`* will seach all message which will return the message UID that can be use later for fetch one or more messages. + + +Search criteria can be consisted these keywords + + +*`ALL`* - All messages in the mailbox; the default initial key for ANDing. + +*`ANSWERED`* - Messages with the \Answered flag set. + +*`BCC`* - Messages that contain the specified string in the envelope structure's BCC field. + +*`BEFORE`* - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. + +*`BODY`* - Messages that contain the specified string in the body of the message. + +*`CC`* - Messages that contain the specified string in the envelope structure's CC field. + +*`DELETED`* - Messages with the \Deleted flag set. + +*`DRAFT`* - Messages with the \Draft flag set. + +*`FLAGGED`* - Messages with the \Flagged flag set. + +*`FROM`* - Messages that contain the specified string in the envelope structure's FROM field. + +*`HEADER`* - Messages that have a header with the specified field-name (as defined in [RFC-2822]) + +and that contains the specified string in the text of the header (what comes after the colon). + +If the string to search is zero-length, this matches all messages that have a header line with + +the specified field-name regardless of the contents. + +*`KEYWORD`* - Messages with the specified keyword flag set. + +*`LARGER`* - Messages with an (RFC-2822) size larger than the specified number of octets. + +*`NEW`* - Messages that have the \Recent flag set but not the \Seen flag. + +This is functionally equivalent to `*"(RECENT UNSEEN)"*`. + +*`NOT`* - Messages that do not match the specified search key. + +*`OLD`* - Messages that do not have the \Recent flag set. This is functionally equivalent to + +*`"NOT RECENT"`* (as opposed to *`"NOT NEW"`*). + +*`ON`* - Messages whose internal date (disregarding time and timezone) is within the specified date. + +*`OR`* - Messages that match either search key. + +*`RECENT`* - Messages that have the \Recent flag set. + +*`SEEN`* - Messages that have the \Seen flag set. + +*`SENTBEFORE`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is earlier than the specified date. + +*`SENTON`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within the specified date. + +*`SENTSINCE`* - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within or later than the specified date. + +*`SINCE`* - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. + +*`SMALLER`* - Messages with an (RFC-2822) size smaller than the specified number of octets. + +*`SUBJECT`* - Messages that contain the specified string in the envelope structure's SUBJECT field. + +*`TEXT`* - Messages that contain the specified string in the header or body of the message. + +*`TO`* - Messages that contain the specified string in the envelope structure's TO field. + +*`UID`* - Messages with unique identifiers corresponding to the specified unique identifier set. + +Sequence set ranges are permitted. + +*`UNANSWERED`* - Messages that do not have the \Answered flag set. + +*`UNDELETED`* - Messages that do not have the \Deleted flag set. + +*`UNDRAFT`* - Messages that do not have the \Draft flag set. + +*`UNFLAGGED`* - Messages that do not have the \Flagged flag set. + +*`UNKEYWORD`* - Messages that do not have the specified keyword flag set. + +*`UNSEEN`* - Messages that do not have the \Seen flag set. + +```C++ +void setSearchCriteria(const String &criteria); +``` + + + + + +**Set to search the unseen message.** + +param *`unseenSearch`* - Boolean flag to enable unseen message search. + +This function will be overridden (omitted) by setFetchUID as setSearchCriteria. + +```C++ +void setSearchUnseenMessage(bool unseenSearch); +``` + + + + + + +**Set the download folder.** + +param *`path`* - Path in SD card. + +All text/html message and attachemnts will be saved to message UID folder which created in defined path + +e.g. *`"/{DEFINED_PATH}/{MESSAGE_UID}/{ATTACHMENT_FILE...}"`*. + +```C++ +void setSaveFilePath(const String &path); +``` + + + + +**Specify message UID to fetch or read.** + +param *`fetchUID`* - The message UID. + +Specify the message UID to fetch (read) only specific message instead of search. + +```C++ +void setFetchUID(const String &fetchUID); +``` + + + + + +**Set storage type to save download attached file or messages.** + +param *`storageType`* - The storage type to save file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + +```C++ +void setFileStorageType(uint8_t storageType); +``` + + + + + + +**Enable/disable attachment download.** + +param *`download`* - Boolean flag to enable/disable attachment download. + +```C++ +void setDownloadAttachment(bool download); +``` + + + +**Enable/disable html message result.** + +param *`htmlFormat`* - Boolean flag to enable/disable html message result. + +The default value is false. + +```C++ +void setHTMLMessage(bool htmlFormat); +``` + + + + +**Enable/disable plain text message result.** + +param *`textFormat`* - Boolean flag to enable/disable plain text message result. + +The default value is true. + +```C++ +void setTextMessage(bool textFormat); +``` + + + + + +**Set the maximum message to search.** + +param *`limit`* - Any number from 0 to 65535. + +The default value is 20. + +```C++ +void setSearchLimit(uint16_t limit); +``` + + + +**Enable/disable recent sort result.** + +param *`recentSort`* - Boolean flag to enable/disable recent message sort result. + +The default value is true. + +```C++ +void setRecentSort(bool recentSort); +``` + + + +**Assign callback function that return status of message fetching or reading.** + +param *`readCallback`* - The function that accept readStatusCallback as parameter. + +```C++ +void setReadCallback(readStatusCallback readCallback); +``` + + + +**Enable/disable attachement download progress while fetching or receiving message.** + +param *`report`* - Boolean flag to enable/disable attachement download progress while fetching or receiving message. + +To get the download status, Callback function should be set via setReadCallback. + +```C++ +void setDownloadReport(bool report); +``` + + + +**Determine only message header is return when search.** + +```C++ +bool isHeaderOnly(); +``` + + + +**Get the sender name/Email for selected message from search result.** + +param *`messageIndex`* - The index of message. + +return *`Sender name/Email String.`* + +```C++ +String getFrom(uint16_t messageIndex); +``` + + + +**Get the sender name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Sender name/Email charactor encoding which use for decoding.`* + +```C++ +String getFromCharset(uint16_t messageIndex); +``` + + + +**Get the recipient name/Email for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Recipient name/Email String.`* + +```C++ +String getTo(uint16_t messageIndex); +``` + + + + +**Get the recipient name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Recipient name/Email charactor encoding which use in decoding to local language.`* + +```C++ +String getToCharset(uint16_t messageIndex); +``` + + + + +**Get the CC name/Email for selected message index of IMAPData result.** + +param *`messageIndex`* - The index of message. + +return *`CC name/Email String.`* + +```C++ +String getCC(uint16_t messageIndex); +``` + + + +**Get the CC name/Email charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`CC name/Email charactor encoding which use in decoding to local language.`* + +```C++ +String getCCCharset(uint16_t messageIndex); +``` + + + + +**Get the message subject for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Message subject name/Email String.`* + +```C++ +String getSubject(uint16_t messageIndex); +``` + + + + +**Get the message subject charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Message subject charactor encoding which use in decoding to local language.`* + +```C++ +String getSubjectCharset(uint16_t messageIndex); +``` + + + +**Get the html message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The html message String or empty String upon the setHTMLMessage was set.`* + +```C++ +String getHTMLMessage(uint16_t messageIndex); +``` + + + +**Get the plain text message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The plain text message String or empty String upon the setTextMessage was set.`* + +```C++ +String getTextMessage(uint16_t messageIndex); +``` + + +**Get the html message charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`Html message charactor encoding which use in decoding to local language.`* + +```C++ +String getHTMLMessgaeCharset(uint16_t messageIndex); +``` + + + + +**Get the text message charactor encoding.** + +param *`messageIndex`* - The index of message. + +return *`The text message charactor encoding which use in decoding to local language.`* + +```C++ +String getTextMessgaeCharset(uint16_t messageIndex); +``` + + + + +**Get the date of received message for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The date String.`* + +```C++ +String getDate(uint16_t messageIndex); +``` + + + + +**Get the message UID for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`UID String that can be use in setFetchUID.`* + +```C++ +String getUID(uint16_t messageIndex); +``` + + + + +**Get the message number for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The message number which vary upon search criteria and sorting.`* + +```C++ +String getNumber(uint16_t messageIndex); +``` + + + + +**Get the message ID for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The message ID String.`* + +```C++ +String getMessageID(uint16_t messageIndex); +``` + + + + +**Get the accept language for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The accept language String.`* + +```C++ +String getAcceptLanguage(uint16_t messageIndex); +``` + + + +**Get the content language of text or html for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`The content language String.`* + +```C++ +String getContentLanguage(uint16_t messageIndex); +``` + + + + +**Determine fetch error status for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch error status.`* + +```C++ +bool isFetchMessageFailed(uint16_t messageIndex); +``` + + + +**Get fetch error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch error reason String for selected message index.`* + +```C++ +String getFetchMessageFailedReason(uint16_t messageIndex); +``` + + + + +**Determine the attachment download error for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Fetch status.`* + +```C++ +bool isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get the attachment download error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Download error reason String for selected message index.`* + +```C++ +String getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex); +``` + + + +**Determine the downloaded/saved text message error status for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Text message download status.`* + +```C++ +bool isDownloadMessageFailed(uint16_t messageIndex); +``` + + + + + +**Get the attachment or message downloadeds error reason for selected message index from search result.** + +param *`messageIndex`* - The index of message. + +return *`Downloaded error reason String for selected message index.`* + +```C++ +String getDownloadMessageFailedReason(uint16_t messageIndex); +``` + + + + +**Assign the download and decode flags for html message download.** + +param *`download`* - Boolean flag to enable/disable message download. + +return *`decoded`* - Boolean flag to enable/disable html message decoding (support utf8 and base64 encoding). + +```C++ +void saveHTMLMessage(bool download, bool decoded); +``` + + + + +**Assign the download and decode flags for plain text message download.** + +param *`download`* - Boolean flag to enable/disable message download. + +return *`decoded`* - Boolean flag to enable/disable plain text message decoding (support utf8 and base64 encoding). + +```C++ +void saveTextMessage(bool download, bool decoded); +``` + + + + +**Determine the mailbox folder count.** + +return *`Folder count number.`* + +```C++ +uint16_t getFolderCount(); +``` + + + + +**Get the mailbox folder name at selected index.** + +param *`folderIndex`* - Index of folder. + +return *`Folder name String.`* + +Use folder name from this function for fetch or search. + +```C++ +String getFolder(uint16_t folderIndex); +``` + + + +**Determin the number of supported flags count.** + +return *`Flag count number.`* + +```C++ +uint16_t getFlagCount(); +``` + + + + +**Get the flag name for selected index.** + +param *`folderIndex`* - Index of folder. + +return *`Flag name String.`* + +Use flags from this function for fetch or search. + +```C++ +String getFlag(uint16_t flagIndex); +``` + + + + +**Get the number of message in selected mailbox folder.** + +return *`Total message number.`* + +```C++ +size_t totalMessages(); +``` + + + + +**Get the number of message from search result.** + +return *`Search result number.`* + +```C++ +size_t searchCount(); +``` + + + + +**Get the number of message available from search result which less than search limit.** + +return *`Available message number.`* + +```C++ +size_t availableMessages(); +``` + + + + + +**Get the number of attachments for selected message index from search result.** + +param *`messageIndex`* - Index of message. + +return *`Number of attachments`* + +```C++ +size_t getAttachmentCount(uint16_t messageIndex); +``` + + + + + +**Get file name of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment file name String at the selected index.`* + +```C++ +String getAttachmentFileName(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get the name of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment name String at the selected index.`* + +```C++ +String getAttachmentName(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get attachment file size for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment file size in byte at the selected index.`* + +```C++ +int getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get creation date of attachment for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`The attachment creation date String at the selected index.`* + +```C++ +String getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Get attachment file type for selected attachment index and message index from search result.** + +param *`messageIndex`* - Index of message. + +param *`attachmentIndex`* - Index of attachment. + +return *`File MIME String at the selected index`* e.g. image/jpeg. + +```C++ +String getAttachmentType(size_t messageIndex, size_t attachmentIndex); +``` + + + + +**Clear IMAPData object data.** + +```C++ +void empty(); +``` + + + + +### SMTPData object call for sending Email. + + +**Set SMTP server login credentials** + +param *`host`* - SMTP server e.g. smtp.gmail.com + +param *`port`* - SMTP port. + +param *`loginEmail`* - The account Email. + +param *`loginPassword`* - The account password. + +param *`rootCA`* - Root CA certificate base64 string + +```C++ +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + +void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + +``` + + + + + +**Set STARTTLS mode to enable STARTTLS protocol.** + +param *`starttls`* - bool flag that enables STARTTLS mode + +```C++ +void setSTARTTLS(bool starttls); +``` + + + + + + +**Set debug print to serial.** + +param *`debug`* - bool flag to enable debug + +```C++ +void setDebug(bool debug); +``` + + + + + + +**Set Sender info** + +param *`fromName`* - Sender's name + +param *`senderEmail`* - Sender's Email. + +```C++ +void setSender(const String &fromName, const String &senderEmail); +``` + + + + + +**Get Sender's name** + +return *`Sender's name String.`* + +```C++ +String getFromName(); +``` + + + + + +**Get Sender's Email.** + +return *`Sender's Email String.`* + +```C++ +String getSenderEmail(); +``` + + + + +**Set Email priority or importance** + +param *`priority`* - Number from 1 to 5, 1 for highest, 3 for normal and 5 for lowest priority + +```C++ +void setPriority(int priority); +``` + + + + + +**Set Email priority or importance.** + +param *`priority`* - String (High, Normal or Low) + +```C++ +void setPriority(const String &priority); +``` + + + + +**Get Email priority** + +return *`Number`* represents Email priority (1 for highest, 3 for normal, 5 for low priority). + +```C++ +uint8_t getPriority(); +``` + + + + +**Add one or more recipient** + +param *`email`* - Recipient Email String of one recipient. + +To add multiple recipients, call addRecipient for each recipient. + +```C++ +void addRecipient(const String &email); +``` + + + + +**Remove recipient** + +param *`email`* - Recipient Email String. + +```C++ +void removeRecipient(const String &email); +``` + + + + +**Remove recipient** + +param *`index`* - Index of recipients in Email object that previously added. + +```C++ +void removeRecipient(uint8_t index); +``` + + + + +**Clear all recipients** +```C++ +void clearRecipient(); +``` + + + + +**Get one recipient** + +param *`index`* - Index of recipients. + +return *`Recipient Email`* String at the index. + +```C++ +String getRecipient(uint8_t index); +``` + + + + + +**Get number of recipients** + +return *`Number`* of recipients. + +```C++ +uint8_t recipientCount(); +``` + + + + +**Set the Email subject.** + +param *`subject`* - The subject. + +```C++ +void setSubject(const String &subject); +``` + + + + +**Get the Email subject.** + +return *`Subject String.`* + +```C++ +String getSubject(); +``` + + + + +**Set the Email message** + +param *`message`* - The message can be in normal text or html format. + +param *`htmlFormat`* - The html format flag, True for send the message as html format + +```C++ +void setMessage(const String &message, bool htmlFormat); +``` + + + + +**Get the message** + +return *`Message String.`* + +```C++ +String getMessage(); +``` + + + + +**Determine message is being send in html format.** + +return *`Boolean`* status. + +```C++ +bool htmlFormat(); +``` + + + + + +**Add Carbon Copy (CC) Email.** + +param *`email`* - The CC Email String. + +```C++ +void addCC(const String &email); +``` + + + + + +**Remove specified Carbon Copy (CC) Email** + +param *`email`* - The CC Email String to remove. + +```C++ +void removeCC(const String &email); +``` + + + + + +**Remove specified Carbon Copy (CC) Email** + +param *`index`* - The CC Email index to remove. + +```C++ +void removeCC(uint8_t index); +``` + + + + + +**Clear all Carbon Copy (CC) Emails** + +```C++ +void clearCC(); +``` + + + + + +**Get Carbon Copy (CC) Email at specified index.** + +param *`index`* - The CC Email index to get. +return *`The CC Email string`* at the index. + +```C++ +String getCC(uint8_t index); +``` + + + + + +**Get the number of Carbon Copy (CC) Email.** + +return *`Number`* of CC Emails. + +```C++ +uint8_t ccCount(); +``` + + + + + +**Add Blind Carbon Copy (BCC) Email** + +param *`email`* - The BCC Email String. + +```C++ +void addBCC(const String &email); +``` + + + + + +**Remove specified Blind Carbon Copy (BCC) Email** + +param *`email`* - The BCC Email String to remove. + +```C++ +void removeBCC(const String &email); +``` + + + + + +**Remove specified Blind Carbon Copy (BCC) Email** + +param *`index`* - The BCC Email index to remove. + +```C++ +void removeBCC(uint8_t index); +``` + + + + + + +**Clear all Blind Carbon Copy (BCC) Emails** + +```C++ +void clearBCC(); +``` + + + + + +**Get Blind Carbon Copy (BCC) Email at specified index.** + +param *`index`* - The BCC Email index to get. + +return *`The BCC Email string`* at the index. + +```C++ +String getBCC(uint8_t index); +``` + + + + + +**Get the number of Blind Carbon Copy (BCC) Email** + +return *`Number`* of BCC Emails. + +```C++ +uint8_t bccCount(); +``` + + + + + +**Add attchement data (binary) from internal memory (flash or ram).** + +param *`fileName`* - The file name String that recipient can be saved. + +param *`mimeType`* - The MIME type of file (image/jpeg, image/png, text/plain...). Can be empty String. + +param *`data`* - The byte array of data (uint8_t). + +param *`size`* - The data length in byte. + +```C++ +void addAttachData(const String &fileName, const String &mimeType, uint8_t *data, uint16_t size); +``` + + + + + +**Remove specified attachment data.** + +param *`fileName`* - The file name of the attachment data to remove. + +```C++ +void removeAttachData(const String &fileName); +``` + + + + + +**Remove specified attachment data.** + +param *`index`* - The index of the attachment data (count only data type attachment) to remove. + +```C++ +void removeAttachData(uint8_t index); +``` + + + + +**Get the number of attachment data.** + +return *`Number`* of attach data. + +```C++ +uint8_t attachDataCount(); +``` + + + +**Add attchement file from SD card** + +param *`fileName`* - The file name String that recipient can be saved. + +param *`mimeType`* - The MIME type of file (image/jpeg, image/png, text/plain...). Can be omitted. + +```C++ +void addAttachFile(const String &filePath, const String &mimeType = ""); +``` + + + + +**Remove specified attachment file from Email object.** + +param *`fileName`* - The file name of the attachment file to remove. + +```C++ +void removeAttachFile(const String &filePath); +``` + + + + +**Set storage type for all attach files.** + +param *`storageType`* - The storage type to read attach file, MailClientStorageType::SD or MailClientStorageType::SPIFFS. + +```C++ +void setFileStorageType(uint8_t storageType); +``` + + + + + + +**Remove specified attachment file.** + +param *`index`* - The index of the attachment file (count only file type attachment) to remove. + +```C++ +void removeAttachFile(uint8_t index); +``` + + + + +**Clear all attachment data.** + +```C++ +void clearAttachData(); +``` + + + + +**Clear all attachment file.** + +```C++ +void clearAttachFile(); +``` + + + + +**Clear all attachments (both data and file type attachments).** + +```C++ +void clearAttachment(); +``` + + + + +**Get number of attachments (both data and file type attachments).** + +return *`Number`* of all attachemnts. + +```C++ +uint8_t attachFileCount(); +``` + + + + + +**Add one or more custom message header field.** + +param *`field`* - custom header String inform of FIELD: VALUE + +This header field will add to message header. + +```C++ +void addCustomMessageHeader(const String &field); +``` + + + + + + +**Remove one custom message header field that previously added..** + +param *`field`* - custom custom message header field String to remove. + +```C++ +void removeCustomMessageHeader(const String &field); +``` + + + + + +**Remove one custom message header field that previously added by its index.** + +param *`index`* - custom message header field index (number) to remove. + + +```C++ +void removeCustomMessageHeader(uint8_t index); +``` + + + + + +**Clear all ccustom message header field that previously added.** + +```C++ +void clearCustomMessageHeader(); +``` + + + + + + +**Get the number of custom message header field that previously added.** + +return *`Number`* of custom message header field. + +```C++ +uint8_t CustomMessageHeaderCount(); +``` + + + + + + +**Get custom message header field that previously added by index.** + +param *`index`* - The custom message header field index to get. + +return *`The custom message header field string at the index`*. + +```C++ +String getCustomMessageHeader(uint8_t index); +``` + + + + + +**Clear all data from Email object to free memory.** + +```C++ +void empty(); +``` + + + + + +**Set the Email sending status callback function to Email object.** + +param *`sendCallback`* - The callback function that accept the sendStatusCallback param. + +```C++ +void setSendCallback(sendStatusCallback sendCallback); +``` + + + + +__MailClient.Time functions__ + + +**Get the time from NTP server and set to device.** + +param *`gmtOffset`* - The GMT time offset in hour. + +param *`daylightOffset`* - The Daylight time offset in hour. + +return - *`Boolean`* type status indicates the success of operation. + +This requires internet connectivity. + +```C++ +bool setClock(float gmtOffset, float daylightOffset); +``` + + + + + + +**Get the Unix time.** + +return - *`uint32_t`* value of current Unix time. + +```C++ +uint32_t getUnixTime(); +``` + + + + + + +**Get timestamp from defined year, month, date, hour, minute, and second.** + +param *`year`* - Year. +param *`mon`* - Month (1 to 12). +param *`date`* - Date. +param *`hour`* - Hour. +param *`mins`* - Minute. +param *`sec`* - Second. + +return - *`time_t`* value of timestamp. + +```C++ +time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); +``` + + + + + + +**Get current year.** + +return - *`int`* value of current year. + +```C++ +int getYear(); +``` + + + + + + +**Get current month.** + +return - *`int`* value of current month. + +```C++ +int getMonth(); +``` + + + + + +**Get current date.** + +return - *`int`* value of current date. + +```C++ +int getDay(); +``` + + + + + + +**Get current day of week.** + +return - *`int`* value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int getDayOfWeek(); +``` + + + + + +**Get current day of week in String.** + +return - *`String`* value of day of week. + +Returns sunday, monday, tuesday, wednesday, thurseday, friday and saturday. + +```C++ +String getDayOfWeekString(); +``` + + + + + + +**Get current hour.** + +return - *`int`* value of current hour (0 to 23). + +```C++ +int getHour(); +``` + + + + + + +**Get current minute.** + +return - *`int`* value of current minute (0 to 59). + +```C++ +int getMin(); +``` + + + + + + +**Get current second.** + +return - *`int`* value of current second (0 to 59). + +```C++ +int getSecond(); +``` + + + + + + + +**Get the total days of current year.** + +return - *`int`* value of total days of current year. + +```C++ +int getNumberOfDayThisYear(); +``` + + + + + + +**Get the total days of from January 1, 1970 to specific date.** + +param *`year`* - Year from 1970. +param *`mon`* - Month (1 to 12). +param *`date`* - Date. + +return - *`int`* value of total days. + +```C++ +int getTotalDays(int year, int month, int day); +``` + + + + + +**Get the day of week from specific date.** + +param *`year`* - Year. +param *`month`* - Month (1 to 12). +param *`day`* - Date. + +return - *`int`* value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int dayofWeek(int year, int month, int day); +``` + + + + + + +**Get the second of current hour.** + +return - *`int`* value of current second. + +```C++ +int getCurrentSecond(); +``` + + + + + +**Get the current timestamp.** + +return - *`uint64_t`* value of current timestamp. + +```C++ +uint64_t getCurrentTimestamp(); +``` + + + + + + +**Get time (year, month, day, hour, minute, and second) from second counted from January 1, 1970.** + +param *`secCount`* - The seconds from January 1, 1970 00.00. +param *`yrs`* - The return year. +param *`months`* - The return month. +param *`days`* - The return day. +param *`hr`* - The return hour. +param *`min`* - The return minute. +param *`sec`* - The return second. + +```C++ +void getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec); +``` + + + + + + +## License + +The MIT License (MIT) + +Copyright (c) 2019 K. Suwatchai (Mobizt) + + +Permission is hereby granted, free of charge, to any person returning 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. + + + + diff --git a/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino b/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino new file mode 100755 index 000000000..4c3540898 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Receive_email/Receive_email.ino @@ -0,0 +1,278 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + +/* + =========================================================================================================================== + To prevent stack overrun in case of you want to download email attachments in IMAP readMail, + increase the stack size in app_main() in esp32 main.cpp will help by change the stack size from 8192 to any more value + as following + + xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE); + to + xTaskCreatePinnedToCore(loopTask, "loopTask", 16384, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE); + + For Arduino, file esp32's main.cpp is at C:\Users\USER_NAME\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.1\cores\esp32\main.cpp + And for platformIO, that file is at C:\Users\USER_NAME\.platformio\packages\framework-arduinoespressif32\cores\esp32\main.cpp + =========================================================================================================================== + +*/ + +#include +#include "ESP32_MailClient.h" +#include "SD.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + + +//The Email Reading data object contains config and data that received +IMAPData imapData; + +//Callback function to get the Email reading status +void readCallback(ReadStatus info); + +//List all files in SD card +void printDirectory(File &dir, int depth); + +void readEmail(); + +unsigned long lastTime = 0; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + MailClient.sdBegin(); + //MailClient.sdBegin(14,2,15,13); //SCK, MISO, MOSI,SS for TTGO T8 v1.7 or 1.8 + + File dir = SD.open("/"); + + printDirectory(dir, 0); + + Serial.println(); + + imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + imapData.setFolder("INBOX"); + + //Clear fetch UID + //If fetch UID was set, no search is perform. + imapData.setFetchUID(""); + + //imapData.setSearchCriteria("UID SINCE 10-Feb-2019"); + //imapData.setSearchCriteria("UID 700:*"); + //imapData.setSearchCriteria("UID SEARCH NOT SEEN"); + //imapData.setSearchCriteria("UID SEARCH UNSEEN"); + imapData.setSearchCriteria("UID SEARCH ALL"); + + //To fetch or read one message UID = 320 + //imapData.setFechUID("320"); + + //Set SD folder to save download messages and attachments + imapData.setSaveFilePath("/email_data"); + + //Save attachament + imapData.setDownloadAttachment(true); + + //Set fetch/search result to return html message + imapData.setHTMLMessage(true); + + //Set fetch/search result to return text message + imapData.setTextMessage(true); + + //Set to save html message in SD card with decoded content. + imapData.saveHTMLMessage(true, true); + + //Set to save text message in SD card with decoded content. + imapData.saveTextMessage(true, true); + + //Set the maximum result when search criteria was set. + imapData.setSearchLimit(10); + + //Set the sort order of returning message upon most recent received email. + imapData.setRecentSort(true); + + //Set the return tex/html message size in byte. + imapData.setMessageBufferSize(200); + + //Set the maximum attachment size 5 MB (each file) + imapData.setAttachmentSizeLimit(1024 * 1024 * 5); + + //Set the Email receive callback function. + imapData.setReadCallback(readCallback); + + //Set to get attachment downloading progress status. + imapData.setDownloadReport(true); + + //Set the storage types to save download attachments or messages (SD is default) + //imapData.setFileStorageType(MailClientStorageType::SPIFFS) + imapData.setFileStorageType(MailClientStorageType::SD); + + MailClient.readMail(imapData); +} + +void readEmail() +{ + + Serial.println(); + Serial.println("Read Email..."); + + imapData.setFetchUID("10"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); + + imapData.setFetchUID("11"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); + + imapData.setFetchUID("12"); + imapData.setSearchCriteria(""); + MailClient.readMail(imapData); +} + +void loop() +{ + + if (millis() - lastTime > 1000 * 60 * 3) + { + + lastTime = millis(); + Serial.println(ESP.getFreeHeap()); + + readEmail(); + } +} + +//Callback function to get the Email reading status +void readCallback(ReadStatus msg) +{ + //Print the current status + Serial.println("INFO: " + msg.info()); + + if (msg.status() != "") + Serial.println("STATUS: " + msg.status()); + + //Show the result when reading finished + if (msg.success()) + { + + for (int i = 0; i < imapData.availableMessages(); i++) + { + Serial.println("================="); + + //Search result number which varied upon search crieria + Serial.println("Messsage Number: " + imapData.getNumber(i)); + + //UID only available when assigned UID keyword in setSearchCriteria + //e.g. imapData.setSearchCriteria("UID SEARCH ALL"); + Serial.println("Messsage UID: " + imapData.getUID(i)); + Serial.println("Messsage ID: " + imapData.getMessageID(i)); + Serial.println("Accept Language: " + imapData.getAcceptLanguage(i)); + Serial.println("Content Language: " + imapData.getContentLanguage(i)); + Serial.println("From: " + imapData.getFrom(i)); + Serial.println("From Charset: " + imapData.getFromCharset(i)); + Serial.println("To: " + imapData.getTo(i)); + Serial.println("To Charset: " + imapData.getToCharset(i)); + Serial.println("CC: " + imapData.getCC(i)); + Serial.println("CC Charset: " + imapData.getCCCharset(i)); + Serial.println("Date: " + imapData.getDate(i)); + Serial.println("Subject: " + imapData.getSubject(i)); + Serial.println("Subject Charset: " + imapData.getSubjectCharset(i)); + + //If setHeaderOnly to false; + if (!imapData.isHeaderOnly()) + { + Serial.println("Text Message: " + imapData.getTextMessage(i)); + Serial.println("Text Message Charset: " + imapData.getTextMessgaeCharset(i)); + Serial.println("HTML Message: " + imapData.getHTMLMessage(i)); + Serial.println("HTML Message Charset: " + imapData.getHTMLMessgaeCharset(i)); + if (imapData.isFetchMessageFailed(i)) + Serial.println("Fetch Error: " + imapData.getFetchMessageFailedReason(i)); + + if (imapData.isDownloadMessageFailed(i)) + Serial.println("Save Content Error: " + imapData.getDownloadMessageFailedReason(i)); + + if (imapData.getAttachmentCount(i) > 0) + { + + Serial.println("**************"); + Serial.println("Attachment: " + String(imapData.getAttachmentCount(i)) + " file(s)"); + + for (int j = 0; j < imapData.getAttachmentCount(i); j++) + { + Serial.println("File Index: " + String(j + 1)); + Serial.println("Filename: " + imapData.getAttachmentFileName(i, j)); + Serial.println("Name: " + imapData.getAttachmentName(i, j)); + Serial.println("Size: " + String(imapData.getAttachmentFileSize(i, j))); + Serial.println("Type: " + imapData.getAttachmentType(i, j)); + Serial.println("Creation Date: " + imapData.getAttachmentCreationDate(i, j)); + if (imapData.isDownloadAttachmentFailed(i, j)) + Serial.println("Download Attachment Error: " + imapData.getDownloadAttachmentFailedReason(i, j)); + } + } + } + + Serial.println(); + } + } +} + +//List all files in SD card +void printDirectory(File &dir, int depth) +{ + while (true) + { + File entry = dir.openNextFile(); + if (!entry) + break; + + for (uint8_t i = 0; i < depth; i++) + Serial.print("| "); + + std::string name = entry.name(); + if (entry.isDirectory()) + { + Serial.print("+----" + String(name.substr(name.find_last_of("/\\") + 1).c_str()) + "\r\n"); + printDirectory(entry, depth + 1); + } + else + { + Serial.print("+--" + String(name.substr(name.find_last_of("/\\") + 1).c_str())); + Serial.print("\t\t\t("); + Serial.print(entry.size(), DEC); + Serial.println(")"); + } + entry.close(); + } +} diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino b/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino new file mode 100755 index 000000000..4d4a5b1aa --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Send_email/Send_email.ino @@ -0,0 +1,180 @@ + + +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + + +#include +#include "ESP32_MailClient.h" +#include "SD.h" + +//For demo only +#include "image.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + +//The Email Sending data object contains config and data to send +SMTPData smtpData; + +//Callback function to get the Email sending status +void sendCallback(SendStatus info); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + + Serial.println("Mounting SD Card..."); + + if (SD.begin()) // MailClient.sdBegin(14,2,15,13) for TTGO T8 v1.7 or 1.8 + { + + Serial.println("Preparing attach file..."); + + File file = SD.open("/text_file.txt", FILE_WRITE); + file.print("Hello World!\r\nHello World!"); + file.close(); + + file = SD.open("/binary_file.dat", FILE_WRITE); + + static uint8_t buf[512]; + + buf[0] = 'H'; + buf[1] = 'E'; + buf[2] = 'A'; + buf[3] = 'D'; + file.write(buf, 4); + + size_t i; + memset(buf, 0xff, 512); + for (i = 0; i < 2048; i++) + { + file.write(buf, 512); + } + + buf[0] = 'T'; + buf[1] = 'A'; + buf[2] = 'I'; + buf[3] = 'L'; + file.write(buf, 4); + + file.close(); + } + else + { + Serial.println("SD Card Monting Failed"); + } + + Serial.println(); + + + Serial.println("Sending email..."); + + //Set the Email host, port, account and password + smtpData.setLogin("outlook.office365.com", 587, "YOUR_EMAIL_ACCOUNT@outlook.com", "YOUR_EMAIL_PASSWORD"); + + //For library version 1.2.0 and later which STARTTLS protocol was supported,the STARTTLS will be + //enabled automatically when port 587 was used, or enable it manually using setSTARTTLS function. + //smtpData.setSTARTTLS(true); + + //Set the sender name and Email + smtpData.setSender("ESP32", "SOME_EMAIL_ACCOUNT@SOME_EMAIL.com"); + + //Set Email priority or importance High, Normal, Low or 1 to 5 (1 is highest) + smtpData.setPriority("High"); + + //Set the subject + smtpData.setSubject("ESP32 SMTP Mail Sending Test"); + + //Set the message - normal text or html format + smtpData.setMessage("
Hello World! - From ESP32
", true); + + //Add recipients, can add more than one recipient + smtpData.addRecipient("SOME_RECIPIENT@SOME_MAIL.com"); + + + + //Add attachments, can add the file or binary data from flash memory, file in SD card + //Data from internal memory + smtpData.addAttachData("firebase_logo.png", "image/png", (uint8_t *)dummyImageData, sizeof dummyImageData); + + //Add attach files from SD card + //Comment these two lines, if no SD card connected + //Two files that previousely created. + smtpData.addAttachFile("/binary_file.dat"); + smtpData.addAttachFile("/text_file.txt"); + + + //Add some custom header to message + //See https://tools.ietf.org/html/rfc822 + //These header fields can be read from raw or source of message when it received) + smtpData.addCustomMessageHeader("Date: Sat, 10 Aug 2019 21:39:56 -0700 (PDT)"); + //Be careful when set Message-ID, it should be unique, otherwise message will not store + //smtpData.addCustomMessageHeader("Message-ID: "); + + //Set the storage types to read the attach files (SD is default) + //smtpData.setFileStorageType(MailClientStorageType::SPIFFS); + smtpData.setFileStorageType(MailClientStorageType::SD); + + + + smtpData.setSendCallback(sendCallback); + + //Start sending Email, can be set callback function to track the status + if (!MailClient.sendMail(smtpData)) + Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); + + //Clear all data from Email object to free memory + smtpData.empty(); + +} + +void loop() +{ +} + +//Callback function to get the Email sending status +void sendCallback(SendStatus msg) +{ + //Print the current status + Serial.println(msg.info()); + + //Do something when complete + if (msg.success()) + { + Serial.println("----------------"); + } +} + diff --git a/libesp32/ESP32-Mail-Client/examples/Send_email/image.h b/libesp32/ESP32-Mail-Client/examples/Send_email/image.h new file mode 100755 index 000000000..4b8b3542d --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Send_email/image.h @@ -0,0 +1,1074 @@ +#include + +static const uint8_t dummyImageData[] PROGMEM = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, +0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, +0x70, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, +0x65, 0x00, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x49, 0x6D, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, +0x64, 0x79, 0x71, 0xC9, 0x65, 0x3C, 0x00, 0x00, 0x42, 0x9A, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, +0xEC, 0xDD, 0x7F, 0x90, 0x6C, 0xD9, 0x41, 0xD8, 0xF7, 0xD3, 0xF3, 0xE6, 0xFD, 0xD8, 0xDD, 0xA7, +0xDD, 0xD1, 0xB2, 0x20, 0x88, 0x70, 0x5E, 0x63, 0xB0, 0xB4, 0x20, 0xE1, 0x37, 0x26, 0x50, 0x05, +0x68, 0x57, 0xAF, 0x57, 0x3F, 0xA1, 0x70, 0x15, 0x4A, 0x59, 0x80, 0x88, 0xA9, 0xF8, 0x29, 0x02, +0x1B, 0x12, 0x5C, 0xBB, 0x40, 0x28, 0x48, 0x19, 0x67, 0xA5, 0x42, 0xC6, 0x8A, 0xA3, 0x64, 0xE5, +0x18, 0x63, 0x91, 0x22, 0x48, 0xB8, 0xCA, 0x95, 0xC4, 0x71, 0x2C, 0x1C, 0x40, 0x6F, 0x77, 0xE3, +0x18, 0xA5, 0xE2, 0x3F, 0x53, 0x05, 0x55, 0xB6, 0x14, 0x6C, 0x09, 0xF4, 0x0B, 0xAD, 0x24, 0x76, +0xB5, 0xDA, 0xDD, 0x37, 0x3F, 0x7B, 0xBA, 0xEF, 0x49, 0x9F, 0xDB, 0x7D, 0x7B, 0x6E, 0xFF, 0x98, +0x79, 0x3D, 0x33, 0xDD, 0x3D, 0x7D, 0xBB, 0x3F, 0x9F, 0xD2, 0xCC, 0x9B, 0xD7, 0x33, 0xD3, 0xD3, +0x73, 0x67, 0xDE, 0xD5, 0xF9, 0xEE, 0xE9, 0x73, 0x6E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0F, 0x35, 0x87, 0x00, +0x58, 0x35, 0xF1, 0xFF, 0xFC, 0xBE, 0x46, 0xEF, 0xCD, 0x17, 0x6A, 0x6F, 0x7E, 0xF2, 0x0F, 0x1D, +0x11, 0x00, 0x10, 0x20, 0x00, 0xD3, 0x8E, 0x8E, 0x9B, 0x9D, 0x3F, 0x1E, 0xED, 0xBC, 0x6C, 0x1E, +0xDE, 0x18, 0x8B, 0xB7, 0x3E, 0xDC, 0x79, 0xF9, 0xAD, 0xDA, 0x5B, 0x9E, 0xFA, 0x98, 0x23, 0x05, +0x00, 0x02, 0x04, 0xE0, 0x2C, 0xE1, 0x51, 0xEF, 0xFC, 0xF1, 0x91, 0x81, 0xF0, 0x18, 0x8C, 0x8F, +0xB2, 0x0F, 0x74, 0x22, 0xE4, 0x67, 0x1C, 0x35, 0x00, 0x10, 0x20, 0x00, 0xA7, 0x89, 0x8F, 0x14, +0x1D, 0xBF, 0xDF, 0x79, 0xD9, 0x08, 0xB1, 0x1D, 0x42, 0xF3, 0xAB, 0x9D, 0x97, 0x17, 0x43, 0x68, +0x37, 0x43, 0xC8, 0x3A, 0x2F, 0x17, 0xEE, 0xEA, 0xBC, 0x5C, 0x09, 0xE1, 0xF2, 0xFD, 0x21, 0xAC, +0xDF, 0x53, 0x7C, 0xDA, 0x87, 0x3B, 0x11, 0xF2, 0x4E, 0x47, 0x0F, 0x00, 0x04, 0x08, 0xC0, 0x49, +0x03, 0xE4, 0x0F, 0x42, 0x9A, 0xF9, 0xD8, 0x7F, 0x2E, 0x84, 0xDD, 0x2F, 0x87, 0x3C, 0x42, 0xC6, +0xCF, 0x7C, 0x74, 0x02, 0xE4, 0x6A, 0x08, 0x57, 0xAF, 0x75, 0xCE, 0x8A, 0x17, 0xD2, 0xDF, 0xFE, +0xE3, 0x4E, 0x84, 0xFC, 0xB6, 0x23, 0x08, 0x00, 0x02, 0x04, 0x60, 0xD2, 0xF8, 0xB8, 0xD9, 0xF9, +0xE3, 0x43, 0x61, 0xFB, 0xF3, 0xDD, 0x99, 0x8F, 0xFC, 0xC6, 0x78, 0x87, 0x33, 0x62, 0x27, 0x3E, +0x5E, 0xF6, 0xE7, 0xD3, 0xCC, 0xC8, 0x0B, 0x9D, 0x0F, 0xFE, 0xA6, 0xDA, 0x5B, 0x9E, 0x7E, 0xC1, +0x91, 0x04, 0x80, 0xE9, 0x5A, 0x73, 0x08, 0x80, 0x25, 0xF5, 0x78, 0xD8, 0xFB, 0xF2, 0xE4, 0xF1, +0x91, 0x7F, 0x4C, 0x3B, 0xE4, 0xC1, 0x12, 0x5B, 0x1B, 0x9D, 0xBF, 0x3D, 0xE6, 0x10, 0x02, 0xC0, +0xF4, 0x99, 0x01, 0x01, 0x96, 0x4E, 0xBE, 0xF0, 0x3C, 0x6B, 0x7E, 0x3A, 0xBC, 0xF8, 0x47, 0x93, +0xC7, 0xC7, 0xE1, 0x67, 0x87, 0x70, 0xD7, 0x2B, 0x42, 0xB8, 0xF2, 0x8A, 0x34, 0xFB, 0x61, 0x16, +0x04, 0x00, 0xA6, 0xCC, 0x0C, 0x08, 0xB0, 0x8C, 0xEA, 0x61, 0xEF, 0xB9, 0xD3, 0xC5, 0x47, 0x92, +0x3E, 0x37, 0xB6, 0xCD, 0x82, 0x00, 0x80, 0x00, 0x01, 0x98, 0x48, 0x23, 0xB4, 0xB6, 0x4E, 0x17, +0x1F, 0xF9, 0x9B, 0xED, 0xEE, 0xA2, 0xF5, 0x10, 0x1E, 0x8F, 0x4F, 0xBF, 0xA5, 0xEE, 0x70, 0x02, +0x80, 0x00, 0x01, 0x38, 0xCE, 0xB5, 0xD0, 0xDA, 0x3D, 0x5D, 0x7C, 0x14, 0xD2, 0xCE, 0x59, 0x69, +0xAB, 0xDE, 0xB4, 0x96, 0x04, 0x00, 0x10, 0x20, 0x00, 0x47, 0x3A, 0xD8, 0xAA, 0x9F, 0x29, 0x3E, +0x0A, 0xDD, 0x59, 0x90, 0x9B, 0x66, 0x41, 0x00, 0x40, 0x80, 0x00, 0x1C, 0x2D, 0x6B, 0x6D, 0x9E, +0x39, 0x3E, 0x92, 0xB4, 0x83, 0x56, 0x3B, 0x9F, 0x49, 0xF9, 0x90, 0x83, 0x0A, 0x00, 0x02, 0x04, +0x60, 0x34, 0x29, 0x9E, 0x7E, 0xEB, 0x46, 0xC8, 0xF6, 0x36, 0xCE, 0x1C, 0x1F, 0x85, 0x9D, 0x2F, +0xA6, 0xD7, 0x8D, 0xF8, 0xF4, 0x5B, 0x1A, 0x8E, 0x2E, 0x00, 0x08, 0x10, 0x80, 0x61, 0x9B, 0x77, +0x5E, 0xFF, 0x71, 0x82, 0xC5, 0xE9, 0x69, 0x31, 0x7B, 0x7A, 0x09, 0xD1, 0x5A, 0x10, 0x00, 0x10, +0x20, 0x00, 0x23, 0xEA, 0x21, 0x66, 0xD3, 0x89, 0x8F, 0x42, 0x77, 0x2D, 0x48, 0x23, 0x3E, 0xFD, +0xE6, 0x86, 0xC3, 0x0B, 0x00, 0x02, 0x04, 0x60, 0x30, 0x40, 0xF2, 0x19, 0x8B, 0x29, 0xC5, 0x47, +0xFA, 0x9C, 0x74, 0x7F, 0xDD, 0x2B, 0xAA, 0x5B, 0x0B, 0x02, 0x00, 0x02, 0x04, 0xA0, 0xA4, 0xBD, +0x77, 0x63, 0xAA, 0xF1, 0x51, 0xE8, 0xCE, 0x82, 0xD4, 0xE3, 0xD3, 0x6F, 0xBE, 0xE9, 0x20, 0x03, +0x80, 0x00, 0x01, 0xE8, 0xCA, 0x0E, 0xEA, 0x53, 0x8F, 0x8F, 0xFC, 0x7E, 0x9B, 0xC5, 0x2C, 0x88, +0xB5, 0x20, 0x00, 0x20, 0x40, 0x00, 0x8A, 0x50, 0xD8, 0xAF, 0x4F, 0x3D, 0x3E, 0x0A, 0x69, 0x16, +0x24, 0xB6, 0xD3, 0x2C, 0xC8, 0xBB, 0x1D, 0x68, 0x00, 0x10, 0x20, 0xC0, 0x8A, 0x8B, 0x4F, 0xBF, +0x75, 0x33, 0xB4, 0x9B, 0xB3, 0x89, 0x8F, 0x3C, 0x6E, 0x9A, 0xDD, 0x2B, 0xA4, 0x87, 0xF0, 0x68, +0x27, 0x42, 0x36, 0x1C, 0x71, 0x00, 0x10, 0x20, 0xC0, 0x6A, 0xAB, 0x87, 0xF6, 0xDE, 0x6C, 0xE2, +0xA3, 0xB0, 0xF7, 0x5C, 0x9A, 0x05, 0x49, 0xF1, 0xF1, 0x98, 0xC3, 0x0D, 0x00, 0x02, 0x04, 0x58, +0x6D, 0x9B, 0xDD, 0x2B, 0x97, 0xCF, 0x28, 0x3E, 0xF2, 0x0F, 0x6B, 0x9B, 0x05, 0x01, 0x00, 0x01, +0x02, 0x90, 0xBB, 0x16, 0x62, 0x6B, 0x76, 0xF1, 0x51, 0x48, 0x6B, 0x41, 0xB2, 0x66, 0x8A, 0x8F, +0x27, 0x1C, 0x72, 0x00, 0x10, 0x20, 0xC0, 0xAA, 0x6A, 0xBE, 0x50, 0x9F, 0x79, 0x7C, 0xE4, 0x9F, +0x12, 0x8B, 0x6D, 0x79, 0x6F, 0xC6, 0xA7, 0xDF, 0x5C, 0x77, 0xE0, 0x01, 0x40, 0x80, 0x00, 0x2B, +0x29, 0x6B, 0xCC, 0x25, 0x3E, 0x92, 0xFD, 0xE7, 0xBB, 0x8B, 0xD2, 0x6D, 0xCB, 0x0B, 0x00, 0x02, +0x04, 0x58, 0x3D, 0xF1, 0xE9, 0xB7, 0xD4, 0x7B, 0x41, 0x30, 0xFB, 0xF8, 0x28, 0xEC, 0x3C, 0x93, +0x5E, 0xA7, 0x59, 0x90, 0x86, 0x9F, 0x00, 0x00, 0x08, 0x10, 0x60, 0xB5, 0xD4, 0x43, 0x6B, 0x77, +0x7E, 0xF1, 0x91, 0x34, 0x5F, 0x0C, 0xA1, 0xB5, 0x95, 0xDE, 0x32, 0x0B, 0x02, 0x00, 0x02, 0x04, +0x58, 0x31, 0x9B, 0x21, 0x3B, 0x98, 0x5F, 0x7C, 0x14, 0xBA, 0x6B, 0x41, 0x1A, 0x66, 0x41, 0x00, +0x40, 0x80, 0x00, 0xAB, 0xE5, 0x5A, 0x77, 0x0B, 0xDE, 0x39, 0xC6, 0x47, 0x72, 0xB0, 0x65, 0x16, +0x04, 0x00, 0x04, 0x08, 0xB0, 0x72, 0x5A, 0xDB, 0x9B, 0x73, 0x8F, 0x8F, 0xC2, 0xD6, 0xE7, 0xD3, +0xEB, 0x34, 0x0B, 0x72, 0xD3, 0x0F, 0x02, 0x00, 0x04, 0x08, 0xB0, 0x0A, 0x62, 0x56, 0x3F, 0x97, +0xF8, 0x48, 0xD2, 0xE2, 0xF7, 0xB4, 0x2B, 0x96, 0x59, 0x10, 0x00, 0x10, 0x20, 0xC0, 0x8A, 0x68, +0xEF, 0xD4, 0xCF, 0x25, 0x3E, 0x0A, 0x69, 0x2D, 0x48, 0x8C, 0xF5, 0xF8, 0xD4, 0x9B, 0x6E, 0xFA, +0x61, 0x00, 0x80, 0x00, 0x01, 0x96, 0x58, 0x7C, 0xFA, 0x2D, 0x8D, 0xD0, 0x6E, 0x9E, 0x5F, 0x7C, +0x24, 0xD9, 0x7E, 0x08, 0xFB, 0xCF, 0xA6, 0xB7, 0x9E, 0xE8, 0x44, 0xC8, 0x86, 0x9F, 0x0A, 0x00, +0x08, 0x10, 0x60, 0x79, 0xD5, 0x47, 0x77, 0xC0, 0x9A, 0x63, 0x7C, 0x14, 0x5F, 0x2B, 0x9F, 0x05, +0x69, 0xA7, 0xF8, 0x78, 0xCC, 0x8F, 0x04, 0x00, 0x04, 0x08, 0xB0, 0xCC, 0x01, 0x32, 0xB0, 0x03, +0xD6, 0x39, 0xC4, 0x47, 0xFE, 0x66, 0x3B, 0x84, 0xFD, 0xE7, 0xD2, 0x5B, 0x8F, 0x9A, 0x05, 0x01, +0x00, 0x01, 0x02, 0x2C, 0xAB, 0xAC, 0x75, 0x3D, 0x1F, 0xFC, 0x9F, 0x67, 0x7C, 0x14, 0xF6, 0x9E, +0x2D, 0x66, 0x41, 0x2C, 0x48, 0x07, 0x00, 0x01, 0x02, 0x2C, 0xA5, 0xFE, 0x02, 0xF4, 0x73, 0x8E, +0x8F, 0xFC, 0xE6, 0x4E, 0x08, 0xED, 0x3C, 0x93, 0xDE, 0x7A, 0x2C, 0x3E, 0xF5, 0xA6, 0xBA, 0x1F, +0x0E, 0x00, 0x08, 0x10, 0x60, 0xD9, 0x64, 0xCD, 0xCD, 0x85, 0x88, 0x8F, 0x42, 0xF3, 0xF9, 0xEE, +0xD6, 0xBC, 0x66, 0x41, 0x00, 0x40, 0x80, 0x00, 0xCB, 0x25, 0x3E, 0xFD, 0x96, 0x7A, 0x88, 0xAD, +0xC5, 0x89, 0x8F, 0x42, 0x5A, 0x90, 0x1E, 0xC2, 0x4D, 0xB3, 0x20, 0x00, 0x20, 0x40, 0x80, 0xE5, +0x4A, 0x90, 0x7A, 0x38, 0xD8, 0x5E, 0xAC, 0xF8, 0x48, 0xD2, 0x2C, 0x48, 0x6B, 0x2B, 0xBD, 0xF5, +0x21, 0x3F, 0x23, 0x00, 0x10, 0x20, 0xC0, 0xF2, 0x68, 0xF4, 0x9E, 0xEE, 0xB4, 0x38, 0xF1, 0x51, +0xE8, 0xCE, 0x82, 0x34, 0xE2, 0x53, 0x6F, 0x6C, 0xF8, 0x31, 0x01, 0x80, 0x00, 0x01, 0x96, 0xC3, +0xB5, 0x89, 0x03, 0x64, 0x9E, 0xF1, 0x91, 0xA4, 0x19, 0x90, 0xD6, 0xED, 0xF4, 0x96, 0xB5, 0x20, +0x00, 0x20, 0x40, 0x80, 0xA5, 0xD0, 0xDA, 0xAE, 0x2F, 0x64, 0x7C, 0x14, 0x5F, 0xF3, 0x70, 0x16, +0xE4, 0x6D, 0x7E, 0x58, 0x00, 0x20, 0x40, 0x80, 0xAA, 0xCB, 0x5A, 0x9B, 0x0B, 0x1B, 0x1F, 0xC9, +0xC1, 0x56, 0x08, 0xFB, 0xCF, 0xA7, 0xB7, 0x9E, 0xF0, 0xC3, 0x02, 0x00, 0x01, 0x02, 0x54, 0x58, +0x7C, 0xFA, 0xCD, 0x1B, 0x21, 0xDB, 0xDB, 0x58, 0xD8, 0xF8, 0x28, 0x74, 0x67, 0x41, 0xEA, 0xF1, +0xA9, 0x37, 0xDE, 0xF4, 0x53, 0x03, 0x40, 0x80, 0x00, 0x54, 0xD7, 0x66, 0x68, 0xED, 0x2E, 0x76, +0x7C, 0x24, 0x69, 0x8D, 0x4A, 0x77, 0x16, 0xC4, 0x5A, 0x10, 0x00, 0x04, 0x88, 0x43, 0x00, 0x54, +0x58, 0x3D, 0xC4, 0x6C, 0xB1, 0xE3, 0xA3, 0x90, 0xAE, 0x8E, 0x1E, 0xDB, 0x69, 0x16, 0xE4, 0xDD, +0x7E, 0x6C, 0x00, 0x08, 0x10, 0x80, 0xAA, 0x06, 0x48, 0xF7, 0x5A, 0x1B, 0x8B, 0x1D, 0x1F, 0xF9, +0xFB, 0xDB, 0x21, 0xEC, 0x3D, 0x97, 0xDE, 0x7A, 0xB4, 0x13, 0x21, 0x1B, 0x7E, 0x74, 0x00, 0x08, +0x10, 0x80, 0xAA, 0x69, 0xEF, 0xDD, 0xA8, 0x44, 0x7C, 0x14, 0xF6, 0x9E, 0x4D, 0x21, 0x92, 0xE2, +0xE3, 0x31, 0x3F, 0x3C, 0x00, 0x04, 0x08, 0x40, 0xD5, 0x64, 0xCD, 0x8D, 0xCA, 0xC4, 0x47, 0xFE, +0xB1, 0xED, 0x6E, 0x84, 0x84, 0xF8, 0x68, 0x7C, 0xEA, 0x0D, 0x75, 0x3F, 0x40, 0x00, 0x04, 0x08, +0x40, 0xB5, 0x02, 0x64, 0xB3, 0x32, 0xF1, 0x51, 0xD8, 0xFD, 0x52, 0x11, 0x4E, 0x16, 0xA4, 0x03, +0xB0, 0x92, 0x6A, 0x0E, 0x01, 0x50, 0x45, 0xF1, 0xE9, 0x37, 0x6F, 0x86, 0x9D, 0x67, 0xFE, 0x20, +0xEC, 0x3F, 0x57, 0x9D, 0xF8, 0x28, 0xBE, 0xDE, 0xE5, 0xFB, 0x43, 0xB8, 0xE7, 0xCF, 0xA5, 0xB7, +0xBE, 0xA9, 0xF6, 0xD6, 0x7F, 0xF5, 0x99, 0xCA, 0x1D, 0xFB, 0x27, 0x1B, 0xF5, 0x90, 0xD6, 0xDF, +0x74, 0x7D, 0xA6, 0xF6, 0x7D, 0x1F, 0xFB, 0x8C, 0xDF, 0x48, 0x00, 0x26, 0xB5, 0xEE, 0x10, 0x00, +0x15, 0x55, 0x0F, 0xED, 0xBD, 0xEA, 0xC5, 0x47, 0x92, 0xB6, 0xE4, 0xBD, 0xEB, 0x15, 0x21, 0xAC, +0x5D, 0x4A, 0xB3, 0x20, 0xEF, 0xAC, 0x48, 0x74, 0x14, 0x6B, 0x57, 0xFE, 0x5A, 0x29, 0x3E, 0x8A, +0xF7, 0xA5, 0x00, 0xF9, 0xAD, 0xCE, 0xCB, 0x07, 0x3A, 0x31, 0xF2, 0x82, 0x5F, 0x4D, 0x00, 0x8E, +0xE3, 0x29, 0x58, 0x40, 0x55, 0x6D, 0x86, 0xD6, 0x4E, 0xF5, 0xE2, 0xA3, 0xB0, 0xFD, 0xF9, 0xF4, +0xFA, 0x66, 0x7C, 0xEA, 0x0D, 0x8D, 0x0A, 0xC4, 0xC7, 0xDB, 0x3A, 0x7F, 0x7C, 0x3A, 0x74, 0x9F, +0x36, 0x56, 0x1F, 0x1B, 0x83, 0xDD, 0xF7, 0x7D, 0xBA, 0xF7, 0xB1, 0x00, 0x20, 0x40, 0x80, 0xA5, +0x73, 0x2D, 0x5F, 0xD4, 0x5D, 0xC5, 0xF8, 0x48, 0x0E, 0xB6, 0x42, 0x6F, 0x0B, 0xE1, 0x85, 0x5E, +0x0B, 0xD2, 0x09, 0x8A, 0x9B, 0x9D, 0x3F, 0x3E, 0xD2, 0x79, 0xD9, 0xE8, 0x2F, 0xA2, 0xBF, 0xFD, +0xA9, 0xC1, 0x97, 0xEE, 0xEE, 0x5E, 0xA1, 0xFB, 0x31, 0xF1, 0x23, 0xF1, 0xD6, 0x8D, 0x9B, 0x7E, +0x3D, 0x01, 0x38, 0x8A, 0x35, 0x20, 0x40, 0x25, 0xC5, 0xDF, 0xFD, 0xAE, 0xDF, 0x0F, 0x5B, 0x9F, +0x6D, 0x54, 0x32, 0x3E, 0x0A, 0x17, 0xAF, 0x86, 0xF0, 0xB2, 0x6F, 0x4E, 0x6F, 0x3D, 0x52, 0x7B, +0xEB, 0xBF, 0xFA, 0xD8, 0x02, 0xC6, 0x47, 0xBD, 0xF3, 0xC7, 0x1F, 0xE4, 0x61, 0x91, 0x16, 0xCF, +0x1F, 0x86, 0xC6, 0x98, 0xFF, 0x37, 0xB9, 0x10, 0xC2, 0x95, 0x07, 0x3A, 0x2F, 0x5F, 0x9F, 0xFE, +0x96, 0x9E, 0x86, 0xF5, 0x97, 0x6A, 0xDF, 0xFF, 0x7F, 0x7F, 0xC6, 0x6F, 0x2A, 0x00, 0xC3, 0xCC, +0x80, 0x00, 0x15, 0x2D, 0x90, 0xAC, 0xDA, 0xF1, 0x91, 0x1C, 0xCE, 0x82, 0x3C, 0xB1, 0xA0, 0x47, +0xF9, 0x89, 0x3C, 0x3E, 0xB6, 0x3F, 0xD7, 0xDD, 0xBD, 0xEB, 0xB8, 0x19, 0xA7, 0xD8, 0xEA, 0x7E, +0x4C, 0xFA, 0xD8, 0xF4, 0x39, 0x21, 0x7C, 0xC8, 0x2F, 0x29, 0x00, 0x02, 0x04, 0x58, 0x8E, 0xF6, +0x78, 0xFA, 0xCD, 0x1B, 0x21, 0x3B, 0xA8, 0x76, 0x7C, 0x14, 0xB6, 0xF2, 0xB5, 0x20, 0x9B, 0xF1, +0xA9, 0x37, 0xDC, 0x5C, 0xA8, 0x63, 0xFC, 0x64, 0x23, 0x05, 0xDE, 0xDB, 0xC2, 0xCE, 0x17, 0xBA, +0x8B, 0xE6, 0x27, 0xFD, 0x7E, 0x9B, 0xCF, 0x17, 0x11, 0xD2, 0x88, 0xB7, 0x6E, 0x34, 0xFC, 0xB6, +0x02, 0x20, 0x40, 0x80, 0x65, 0xB0, 0x19, 0xDA, 0xBB, 0xD5, 0x8F, 0x8F, 0x24, 0x6B, 0x16, 0x03, +0xFC, 0x45, 0x5B, 0x0B, 0xF2, 0x78, 0x3E, 0x3B, 0x93, 0x5F, 0x38, 0xF1, 0x84, 0xDF, 0x6F, 0x8A, +0x90, 0x0A, 0xAC, 0x6F, 0x01, 0x40, 0x80, 0x00, 0x9C, 0x20, 0x40, 0x9A, 0xD5, 0x8F, 0x8F, 0xC2, +0xEE, 0x97, 0xD3, 0xEB, 0x7A, 0x7C, 0xF2, 0x91, 0xC7, 0x16, 0xE1, 0xE0, 0xF6, 0x66, 0x3F, 0x1A, +0xF9, 0x53, 0xAA, 0x4E, 0xFB, 0xFD, 0x76, 0x3F, 0xD7, 0x2C, 0x08, 0x00, 0x02, 0x04, 0x58, 0x0A, +0xD7, 0xEE, 0x3C, 0x03, 0x52, 0x91, 0xF8, 0x48, 0xD2, 0x2C, 0x48, 0x77, 0xC0, 0xFE, 0x78, 0x27, +0x42, 0x36, 0x16, 0xE0, 0xF8, 0x76, 0x67, 0x3F, 0x0E, 0xB6, 0x4E, 0xFF, 0xFD, 0xB6, 0xB6, 0xCC, +0x82, 0x00, 0x20, 0x40, 0x80, 0x25, 0xD1, 0xDA, 0xDA, 0x5C, 0x9A, 0xF8, 0x28, 0x3E, 0xBF, 0xBB, +0xC3, 0x54, 0x71, 0xB1, 0xBF, 0x73, 0xD3, 0xBB, 0x8E, 0x47, 0x23, 0x6C, 0x7D, 0xEE, 0xEC, 0xDF, +0xEF, 0xE1, 0x5A, 0x10, 0xD7, 0x06, 0x01, 0x40, 0x80, 0x00, 0x15, 0x16, 0x63, 0x7D, 0xA9, 0xE2, +0x23, 0xFF, 0xA3, 0x1D, 0xC2, 0xFE, 0x73, 0xE9, 0xAD, 0x47, 0xCF, 0x79, 0x16, 0xE4, 0x89, 0x7C, +0x4D, 0x4A, 0xD6, 0x3C, 0xFB, 0xF7, 0x9B, 0xEE, 0xA3, 0xF9, 0x7C, 0xF7, 0x3E, 0x01, 0x40, 0x80, +0x00, 0x95, 0xD5, 0xDA, 0xA9, 0x2F, 0x55, 0x7C, 0x14, 0xD2, 0x2C, 0x48, 0xD6, 0x4C, 0xF1, 0x71, +0x2E, 0x4F, 0x5B, 0xEA, 0x5D, 0x74, 0xB0, 0x7E, 0xF4, 0xDA, 0x8F, 0x53, 0x7C, 0xBF, 0xDD, 0xFB, +0xAA, 0xC7, 0x5B, 0xAF, 0xBF, 0xE9, 0x17, 0x17, 0x00, 0x01, 0x02, 0x54, 0x4E, 0x7C, 0xFA, 0xCD, +0x8D, 0xF1, 0xFF, 0x75, 0xBE, 0xE2, 0xF1, 0x91, 0xDF, 0xD4, 0x2E, 0x16, 0xA4, 0x3F, 0x16, 0x9F, +0x7C, 0xA4, 0x7E, 0x0E, 0x87, 0xF7, 0xF1, 0xA3, 0x67, 0x3F, 0x4E, 0xF9, 0xFD, 0xE6, 0xBB, 0x7C, +0x7D, 0x25, 0x04, 0x6B, 0x41, 0x00, 0x10, 0x20, 0x40, 0x45, 0x6D, 0x8C, 0x0E, 0x90, 0x97, 0x20, +0x3E, 0x0A, 0xCD, 0x7E, 0x00, 0xCC, 0x75, 0xC0, 0x7E, 0xFC, 0xEC, 0xC7, 0x19, 0x8F, 0x6F, 0xF7, +0x22, 0x86, 0x66, 0x41, 0x00, 0x10, 0x20, 0x40, 0x25, 0x6D, 0x86, 0xD6, 0xEE, 0x72, 0xC6, 0x47, +0xA1, 0x3B, 0x0B, 0x72, 0x33, 0x3E, 0xF9, 0xC8, 0xE6, 0x9C, 0xE2, 0xA3, 0xFB, 0xB4, 0xAF, 0x14, +0x0A, 0xB3, 0x88, 0xBB, 0x74, 0x9F, 0xDD, 0xEB, 0x89, 0x3C, 0xD1, 0x89, 0x90, 0x0D, 0xBF, 0xC2, +0x00, 0x02, 0x04, 0xA0, 0x3A, 0xB2, 0xD6, 0xF5, 0xFC, 0xA9, 0x4A, 0xCB, 0x1A, 0x1F, 0xC9, 0xE1, +0x85, 0xFC, 0xE6, 0xB5, 0x78, 0xFB, 0xB1, 0x34, 0x43, 0x31, 0x7A, 0xD1, 0xC1, 0x29, 0x1E, 0xDF, +0x05, 0xD9, 0xE5, 0x0B, 0x00, 0x01, 0x02, 0x70, 0x32, 0xFD, 0x05, 0xE8, 0x4B, 0x1A, 0x1F, 0x85, +0xEE, 0x2C, 0x48, 0x23, 0x3E, 0xF9, 0x48, 0x63, 0x96, 0x87, 0xB3, 0x37, 0xFB, 0xF1, 0x68, 0x2F, +0x10, 0x66, 0x13, 0x1F, 0xF9, 0x6D, 0xED, 0x62, 0x16, 0xE4, 0x51, 0xB3, 0x20, 0x00, 0x02, 0x04, +0xA0, 0x3A, 0xB2, 0xFD, 0xCD, 0xA5, 0x8F, 0x8F, 0xE4, 0xE0, 0xF6, 0xBC, 0x2E, 0xE4, 0xF7, 0x58, +0x3E, 0x33, 0x31, 0x30, 0xFB, 0x31, 0xA3, 0xE3, 0x6B, 0x16, 0x04, 0x00, 0x01, 0x02, 0x54, 0x49, +0x7C, 0xEA, 0x4D, 0xF5, 0xC1, 0xFF, 0x4A, 0xBF, 0xA4, 0xF1, 0x51, 0x3C, 0xCE, 0xED, 0x2F, 0xA4, +0xD7, 0x69, 0x16, 0x64, 0x26, 0x17, 0xF2, 0x1B, 0x3F, 0xFB, 0x31, 0xC3, 0xB8, 0x33, 0x0B, 0x02, +0x80, 0x00, 0x01, 0x2A, 0xA6, 0x1E, 0x0E, 0xB6, 0x56, 0x23, 0x3E, 0x92, 0xF6, 0x6E, 0xC8, 0xB7, +0xC5, 0x9D, 0xDD, 0x5A, 0x90, 0xC7, 0xF3, 0xEB, 0x8E, 0xF4, 0x77, 0xBE, 0x9A, 0xC3, 0xCC, 0x52, +0x77, 0xA1, 0xFB, 0xB9, 0x5D, 0xEB, 0x04, 0x00, 0x01, 0x02, 0x70, 0x12, 0x8D, 0xA3, 0xAF, 0xD0, +0xBD, 0x64, 0xF1, 0x51, 0x1E, 0xB0, 0xA7, 0x0B, 0xF9, 0x3D, 0xF9, 0xC8, 0xCD, 0x69, 0x1E, 0xC8, +0xF8, 0x64, 0xA3, 0x1E, 0xD2, 0x53, 0xA1, 0xE6, 0x19, 0x1F, 0x83, 0xDF, 0xD3, 0x63, 0xF1, 0xD6, +0xEB, 0xEB, 0x7E, 0xA5, 0x01, 0x04, 0x08, 0xC0, 0x22, 0xBB, 0xEF, 0xC4, 0x01, 0x52, 0xE5, 0xF8, +0x48, 0xF2, 0x0B, 0xF9, 0xE5, 0xB3, 0x20, 0x8F, 0x77, 0x22, 0x64, 0x9A, 0x4F, 0x5B, 0x7A, 0xFC, +0xF0, 0xBE, 0xE7, 0xBC, 0xA6, 0x66, 0xFF, 0x7C, 0xAE, 0x75, 0x02, 0x80, 0x00, 0x01, 0x38, 0x99, +0xD6, 0xD6, 0xC9, 0xAE, 0x8B, 0x51, 0xF5, 0xF8, 0x28, 0xEC, 0x7C, 0x21, 0xBF, 0x90, 0x5F, 0x98, +0xD2, 0xE2, 0xED, 0xDE, 0xEC, 0xC7, 0xCD, 0xEE, 0x4C, 0xC4, 0xBC, 0x17, 0xF4, 0xF7, 0x74, 0x67, +0x41, 0x6E, 0x9A, 0x05, 0x01, 0x58, 0x3D, 0xEB, 0x0E, 0x01, 0x50, 0x19, 0x59, 0x6B, 0xF2, 0x00, +0x59, 0x96, 0xF8, 0xC8, 0x3F, 0xA6, 0xB7, 0x78, 0xFB, 0xAE, 0xAF, 0x7F, 0x34, 0x3E, 0xF9, 0xC8, +0x07, 0x6A, 0xDF, 0xF7, 0xFB, 0x2F, 0x9C, 0xF1, 0x81, 0x3E, 0x9E, 0x5F, 0xCC, 0x71, 0xFF, 0x2B, +0xE7, 0x13, 0x1F, 0x49, 0x9A, 0x05, 0xB9, 0xF2, 0xB5, 0x21, 0x5C, 0xB8, 0x92, 0x66, 0x41, 0xDE, +0xE9, 0x97, 0x7B, 0xCC, 0x21, 0xFE, 0xC4, 0x8F, 0xA6, 0x19, 0xAF, 0xB4, 0x01, 0x41, 0x39, 0xD2, +0xFE, 0xB0, 0xF3, 0xF2, 0xB1, 0xDA, 0xB7, 0xFD, 0xCF, 0x2F, 0x38, 0x42, 0x40, 0x55, 0xD5, 0x1C, +0x02, 0xA0, 0x12, 0x83, 0xB1, 0xA7, 0xDE, 0xB4, 0x11, 0xF6, 0xBE, 0xFC, 0xD5, 0xC3, 0x35, 0x0B, +0x2B, 0x12, 0x1F, 0xFD, 0xB3, 0xF5, 0x85, 0x10, 0x36, 0xBE, 0x2D, 0xFD, 0xF9, 0x9E, 0x4E, 0x80, +0xBC, 0xFB, 0xD4, 0x8F, 0xF2, 0xC9, 0x46, 0x8A, 0xB8, 0x3F, 0x08, 0x2F, 0x7D, 0xB2, 0xD8, 0xE6, +0x77, 0xFE, 0xF1, 0x51, 0xB8, 0x78, 0x4F, 0x08, 0x2F, 0xFB, 0x96, 0xF4, 0xD6, 0x23, 0xB5, 0xEF, +0xFF, 0x7F, 0x3E, 0xE6, 0xB7, 0x7C, 0x20, 0x3C, 0xD2, 0xC6, 0x03, 0x37, 0x8F, 0x0C, 0xD2, 0xDA, +0x85, 0x0F, 0x77, 0xDE, 0xFA, 0x19, 0x21, 0x02, 0x54, 0x91, 0xA7, 0x60, 0x01, 0x55, 0xB1, 0x99, +0xFF, 0x57, 0xFB, 0x55, 0x8C, 0x8F, 0x62, 0xD0, 0xD9, 0x8D, 0xAF, 0x47, 0x7B, 0x4F, 0xA1, 0x3A, +0xAD, 0x27, 0x4A, 0xD7, 0x18, 0x39, 0xBF, 0xF8, 0x48, 0xC7, 0x2C, 0xED, 0x68, 0x36, 0x9F, 0x6B, +0x9D, 0x54, 0x29, 0x3E, 0x52, 0x20, 0x7E, 0x7A, 0x20, 0x3E, 0xE2, 0x41, 0x08, 0xD9, 0xCE, 0xE1, +0x56, 0xC9, 0x29, 0x46, 0xBB, 0xEF, 0xFF, 0x74, 0xEF, 0xE3, 0x01, 0x04, 0x08, 0xC0, 0x0C, 0xDC, +0xF9, 0x1A, 0x20, 0xCB, 0x1A, 0x1F, 0x85, 0xF4, 0x34, 0xAC, 0x6C, 0xFF, 0xD4, 0x5B, 0xD8, 0xC6, +0x5B, 0x37, 0x1A, 0x9D, 0xAF, 0xDD, 0x98, 0x68, 0x16, 0x69, 0xD6, 0xF1, 0x51, 0xE8, 0x3E, 0x96, +0x46, 0xBC, 0xF5, 0x70, 0x43, 0x7C, 0xE4, 0x31, 0xF1, 0xFB, 0x9D, 0x97, 0x8D, 0x3C, 0x3A, 0x9A, +0xCF, 0x74, 0x8E, 0xCF, 0xBF, 0xEF, 0xBC, 0x7C, 0xAA, 0xF3, 0xB3, 0xFF, 0xEC, 0xE1, 0xDB, 0xCD, +0x2F, 0x77, 0xA3, 0x24, 0x7D, 0x5C, 0xE7, 0xE3, 0x45, 0x08, 0x20, 0x40, 0x00, 0x66, 0x15, 0x20, +0xC7, 0xFD, 0x57, 0xFB, 0x65, 0x8F, 0x8F, 0xE2, 0xEB, 0x15, 0x8B, 0xB7, 0x4F, 0x37, 0x0B, 0xF2, +0x78, 0x7E, 0x0C, 0x4F, 0x33, 0xFB, 0x31, 0x8B, 0xF8, 0x48, 0xCC, 0x82, 0x8C, 0xC6, 0xC7, 0xC1, +0x73, 0xDD, 0xD0, 0x68, 0xBD, 0x18, 0x46, 0xA2, 0x3B, 0x85, 0x47, 0xEB, 0xF9, 0x4E, 0x90, 0x7C, +0xBA, 0xFB, 0xFE, 0x6E, 0x84, 0x3C, 0xE1, 0xF4, 0x00, 0x08, 0x10, 0x80, 0x69, 0x6B, 0xEF, 0xDD, +0x58, 0xF9, 0xF8, 0x48, 0x0E, 0xB7, 0xB0, 0x3D, 0xD1, 0xA0, 0x33, 0x9F, 0xFD, 0x48, 0xD7, 0x51, +0x39, 0xCD, 0xEC, 0xC7, 0xAC, 0xE2, 0xA3, 0x50, 0x5C, 0xF1, 0x7D, 0x45, 0x67, 0x41, 0x06, 0xE2, +0x23, 0xCD, 0x7A, 0x1C, 0x3C, 0x3B, 0xC1, 0x27, 0xB5, 0xBB, 0x33, 0x24, 0x29, 0x56, 0xD2, 0xB1, +0xFB, 0xC4, 0x8F, 0xBE, 0xCD, 0x49, 0x02, 0x10, 0x20, 0x00, 0xD3, 0xD4, 0xBD, 0x7A, 0xF6, 0x6A, +0xC7, 0x47, 0x61, 0xEB, 0x73, 0xE9, 0xA6, 0xB7, 0xC5, 0x5B, 0x8D, 0x93, 0x0C, 0xD8, 0x9F, 0x08, +0xCD, 0xE7, 0x4F, 0x3E, 0xFB, 0x31, 0xEB, 0xF8, 0xC8, 0xE3, 0x72, 0xE6, 0x57, 0x7C, 0xAF, 0x4E, +0x7C, 0x74, 0x67, 0x35, 0x26, 0x97, 0x62, 0xA5, 0x7D, 0x3B, 0xBD, 0xF5, 0x83, 0x4E, 0x12, 0x80, +0x00, 0x01, 0x98, 0x6A, 0x80, 0xEC, 0x6F, 0x8A, 0x8F, 0xDE, 0x5F, 0x4F, 0xF8, 0xB4, 0xA5, 0x78, +0xEB, 0xC6, 0xCD, 0x90, 0x16, 0xF1, 0x9F, 0x74, 0xF6, 0x63, 0x1E, 0xF1, 0x51, 0xE8, 0x3E, 0xB6, +0xCD, 0x78, 0xEB, 0xE1, 0x9B, 0xE2, 0xE3, 0x84, 0xD2, 0x9A, 0x90, 0x34, 0xBB, 0x05, 0x20, 0x40, +0x00, 0xA6, 0x34, 0x50, 0x7B, 0xEA, 0x4D, 0x9B, 0xA1, 0x7D, 0xB0, 0xBA, 0xF1, 0x11, 0x07, 0xBF, +0x74, 0xFE, 0xD7, 0xCE, 0x80, 0xBD, 0xF3, 0x67, 0x23, 0x9B, 0x6C, 0x16, 0xE4, 0xF1, 0x7C, 0xF6, +0xE3, 0x24, 0x57, 0x91, 0x9F, 0x67, 0x7C, 0xE4, 0x81, 0x79, 0x78, 0xC5, 0x77, 0xF1, 0x71, 0xD2, +0x3B, 0xEB, 0xFC, 0xDB, 0x68, 0xBF, 0x54, 0x77, 0xA6, 0x00, 0x04, 0x08, 0xC0, 0xF4, 0xD4, 0xF3, +0xA7, 0xE9, 0xAC, 0x6A, 0x7C, 0x24, 0xB5, 0xEE, 0xDD, 0xF5, 0xEF, 0xB1, 0xB9, 0xD5, 0x79, 0xC9, +0x07, 0xAD, 0x1F, 0x3A, 0xF6, 0x5E, 0xBA, 0xB3, 0x1F, 0xF5, 0x13, 0xCD, 0x7E, 0xCC, 0x3B, 0x3E, +0x0A, 0xDD, 0xC7, 0x58, 0x5F, 0xF6, 0x59, 0x90, 0xA9, 0xC6, 0x47, 0x71, 0x9C, 0xB3, 0xFD, 0xCE, +0xFD, 0xBE, 0xA3, 0xE1, 0x54, 0x01, 0x08, 0x10, 0x80, 0xE9, 0xD8, 0xEC, 0x07, 0xC8, 0x2A, 0xC6, +0xC7, 0xF0, 0xDD, 0x15, 0x6F, 0xA7, 0xC5, 0xDB, 0x31, 0xD4, 0xB3, 0x8F, 0x36, 0x8E, 0x1B, 0xB0, +0x3F, 0x1E, 0xF6, 0x9F, 0x9D, 0x7C, 0xF6, 0xE3, 0xBC, 0xE2, 0x23, 0x49, 0x8F, 0x71, 0xEF, 0xD9, +0xEE, 0x63, 0x16, 0x1F, 0x27, 0x3B, 0xCE, 0xD9, 0x76, 0x37, 0xD4, 0x01, 0x04, 0x08, 0xC0, 0x54, +0x5C, 0xCB, 0x77, 0xFD, 0x59, 0xC5, 0x35, 0x1F, 0xC5, 0x2D, 0x71, 0xB4, 0x4D, 0x62, 0x67, 0xC0, +0x1E, 0xF7, 0x9F, 0x4F, 0x6F, 0x8F, 0x1D, 0xB0, 0x67, 0xB7, 0x6E, 0xBC, 0xBB, 0x73, 0xDC, 0xEA, +0x61, 0xE7, 0x4B, 0x73, 0x78, 0xDC, 0x53, 0x3A, 0xE6, 0x69, 0x16, 0xA4, 0xF3, 0x98, 0xE3, 0xAD, +0x87, 0xDF, 0x2D, 0x3E, 0x4E, 0x70, 0x9C, 0xB3, 0x03, 0x01, 0x02, 0x08, 0x10, 0x80, 0xA9, 0x69, +0xBE, 0x50, 0x5F, 0xC9, 0xF8, 0xA8, 0x8D, 0xC6, 0x47, 0x2C, 0xBE, 0x44, 0xF1, 0xD2, 0x1B, 0xB0, +0xB7, 0x3F, 0xDA, 0x78, 0x6C, 0x28, 0x3E, 0xD2, 0xAE, 0x61, 0x8F, 0xE6, 0x33, 0x0A, 0x77, 0xBA, +0x80, 0xE3, 0xA2, 0xC4, 0x47, 0xFE, 0xE9, 0xED, 0x62, 0x16, 0xE4, 0xD1, 0x4E, 0x84, 0x6C, 0x88, +0x8F, 0x09, 0x8F, 0x73, 0xF7, 0xC2, 0x84, 0x37, 0x9C, 0x2C, 0x00, 0x01, 0x02, 0x30, 0x95, 0xD1, +0x5B, 0xBB, 0xB1, 0x52, 0xF1, 0xD1, 0x8B, 0x8B, 0x38, 0x66, 0x03, 0xAC, 0xEE, 0xAB, 0x5A, 0xE7, +0x7D, 0xBD, 0x97, 0x56, 0x67, 0xE0, 0xB9, 0x9B, 0x22, 0x23, 0x3C, 0xDE, 0xFE, 0xBD, 0xC6, 0x46, +0xE9, 0x83, 0x1F, 0xEB, 0x1C, 0xB7, 0x8D, 0xB8, 0xF7, 0xEC, 0x8C, 0x1F, 0xF7, 0x34, 0x8F, 0x79, +0xEF, 0xB1, 0xEC, 0xFE, 0x59, 0xFA, 0x99, 0xA7, 0xEF, 0xE5, 0x31, 0xF1, 0x71, 0x82, 0xE3, 0xDC, +0xBE, 0xBD, 0xE1, 0x64, 0x01, 0x08, 0x10, 0x80, 0xB3, 0x0E, 0xB5, 0x9E, 0x7A, 0xE3, 0x46, 0xEF, +0xE9, 0x25, 0xAB, 0x11, 0x1F, 0xC7, 0x7D, 0x44, 0x3E, 0x03, 0x52, 0xEB, 0x7F, 0xA9, 0xD8, 0x8B, +0x94, 0x6C, 0xE7, 0xD9, 0x81, 0x01, 0x7B, 0xFB, 0xA3, 0x37, 0x36, 0x62, 0x9A, 0xFD, 0xD8, 0xED, +0xCE, 0x7E, 0xC4, 0x52, 0xD4, 0x2C, 0x7C, 0x7C, 0x74, 0x83, 0x73, 0x69, 0x66, 0x41, 0xE6, 0x16, +0x1F, 0xF9, 0xBB, 0xB2, 0x4D, 0x67, 0x0C, 0x40, 0x80, 0x00, 0x9C, 0xDD, 0xE6, 0xC0, 0x0E, 0x58, +0xCB, 0x1E, 0x1F, 0xC3, 0xEB, 0xCF, 0xE3, 0x60, 0x7C, 0x14, 0x33, 0x23, 0x87, 0x4F, 0xC3, 0x4A, +0x05, 0xD2, 0x89, 0x8C, 0x4E, 0x6C, 0x74, 0xDE, 0x7C, 0xB4, 0xF5, 0xBB, 0x8D, 0x6E, 0x88, 0xA4, +0x20, 0xB9, 0xD3, 0xEC, 0xC7, 0x22, 0xC6, 0x47, 0x21, 0xCD, 0x82, 0x74, 0x2F, 0x3E, 0x59, 0xD9, +0x59, 0x90, 0xB9, 0xC6, 0x47, 0xFE, 0xEE, 0x83, 0xB4, 0x13, 0x96, 0x08, 0x01, 0x04, 0x08, 0xC0, +0xD9, 0x03, 0x64, 0xD2, 0xEB, 0x57, 0x2C, 0xC7, 0xCC, 0xC7, 0x9D, 0xE2, 0xA3, 0xF8, 0xB2, 0xB1, +0xF3, 0x6A, 0x60, 0x16, 0xA4, 0x9D, 0x0F, 0xD8, 0x3F, 0x12, 0x62, 0xED, 0xD1, 0xB4, 0x43, 0x56, +0xCC, 0x17, 0xEE, 0x8F, 0xDE, 0xE7, 0xC2, 0xC7, 0x47, 0x7E, 0x5B, 0xE7, 0xB1, 0xEF, 0x7C, 0x31, +0xBD, 0xF5, 0x78, 0xBC, 0xF5, 0x70, 0x5D, 0x7C, 0x4C, 0x70, 0x9C, 0xB3, 0xBD, 0xF4, 0xBA, 0xEE, +0x94, 0x01, 0x08, 0x10, 0x80, 0xB3, 0xB9, 0x36, 0xD9, 0x0C, 0xC8, 0x12, 0xC4, 0xC7, 0xD8, 0xA7, +0x48, 0x75, 0xC2, 0x63, 0x4C, 0x7C, 0xA4, 0x57, 0x31, 0x94, 0x66, 0x43, 0xD2, 0x2C, 0xC8, 0xCE, +0x97, 0x42, 0xFB, 0xC5, 0xD8, 0x88, 0xCD, 0xE6, 0x46, 0xDC, 0xFB, 0xEA, 0x40, 0xC0, 0x0C, 0xDC, +0x7D, 0xB6, 0xE0, 0xF1, 0x51, 0xD8, 0xEF, 0x5F, 0x3C, 0xB1, 0x52, 0xDB, 0xF2, 0x9E, 0x4B, 0x7C, +0xE4, 0x1F, 0x76, 0xD0, 0x0D, 0x76, 0x00, 0x01, 0x02, 0x70, 0x06, 0xAD, 0xAD, 0x09, 0x06, 0x54, +0xCB, 0xB3, 0xE6, 0x63, 0x64, 0xF6, 0x23, 0xC4, 0x81, 0x2F, 0x55, 0x9E, 0xF9, 0x08, 0xA5, 0x97, +0x7C, 0x16, 0x64, 0xFB, 0xF9, 0x90, 0x6D, 0x35, 0x43, 0xEB, 0xCB, 0x5F, 0xEC, 0x7D, 0x66, 0x6D, +0xF4, 0x4B, 0xC5, 0x8A, 0xC4, 0x47, 0xA1, 0x3B, 0x0B, 0x72, 0xB3, 0x2A, 0xB3, 0x20, 0xE7, 0x16, +0x1F, 0x79, 0x58, 0xE6, 0x33, 0x20, 0xD7, 0x9D, 0x34, 0x00, 0x01, 0x02, 0x70, 0xB6, 0x41, 0x6A, +0x7D, 0x25, 0xE2, 0x23, 0xDD, 0x5C, 0x1B, 0xBD, 0x2D, 0xF6, 0x6E, 0x8C, 0xF1, 0x30, 0x3E, 0x0E, +0xD7, 0x81, 0xD4, 0xBA, 0x8B, 0xD2, 0x7B, 0x1F, 0x9B, 0xBD, 0xD4, 0x09, 0x93, 0x17, 0x3F, 0x17, +0xB2, 0xE7, 0x9E, 0x0F, 0xB1, 0x15, 0x06, 0x67, 0x3E, 0x4A, 0xB3, 0x26, 0x95, 0x89, 0x8F, 0xA4, +0x42, 0xB3, 0x20, 0xE7, 0x1A, 0x1F, 0xFD, 0x08, 0xD9, 0xAD, 0x3B, 0x69, 0x00, 0x02, 0x04, 0xE0, +0x2C, 0x5A, 0x3B, 0xF5, 0xA5, 0x8F, 0x8F, 0xE1, 0xBB, 0x8E, 0x43, 0x33, 0x21, 0xF1, 0xF0, 0x69, +0x57, 0xFD, 0xA7, 0x62, 0xC5, 0xDA, 0xC0, 0xDB, 0xB1, 0xD5, 0x79, 0xD9, 0xE9, 0xBC, 0xDD, 0xDC, +0xEA, 0x1E, 0xB6, 0xAF, 0xC4, 0x7E, 0xA4, 0x1C, 0x5E, 0x3C, 0x64, 0x1A, 0x87, 0x6E, 0x8E, 0xF1, +0x51, 0xD8, 0xFE, 0xD3, 0xF4, 0x7A, 0xA1, 0x67, 0x41, 0x16, 0x22, 0x3E, 0xF2, 0x4F, 0x6B, 0x79, +0x0A, 0x16, 0x20, 0x40, 0x00, 0x4E, 0x3D, 0x04, 0x7B, 0xEA, 0x8D, 0x8D, 0xDE, 0x7F, 0xFD, 0x5E, +0xEE, 0xF8, 0xB8, 0xC3, 0xEC, 0x47, 0x71, 0xC3, 0xE1, 0x4C, 0x46, 0x6D, 0xE0, 0x82, 0x84, 0xF9, +0xD3, 0xAF, 0x5E, 0x1C, 0xBC, 0xFF, 0xB8, 0xDF, 0x7D, 0x29, 0x1E, 0xF7, 0xD8, 0xB5, 0x20, 0x55, +0x88, 0x8F, 0xA4, 0xD9, 0x19, 0xCC, 0x1F, 0xE4, 0x61, 0xF5, 0x21, 0xF1, 0x71, 0x07, 0xD9, 0x7E, +0xDA, 0x09, 0xAB, 0xEE, 0xEC, 0x01, 0x08, 0x10, 0x80, 0xD3, 0xD9, 0x18, 0x1F, 0x20, 0x4B, 0x78, +0x9D, 0x8F, 0x63, 0x66, 0x3F, 0x8A, 0x99, 0x8F, 0x10, 0x4B, 0x97, 0xF3, 0x28, 0xDF, 0x65, 0x3A, +0x44, 0xCD, 0xD1, 0xAF, 0xD1, 0x4E, 0x51, 0x92, 0xC5, 0xC3, 0x59, 0x90, 0xB2, 0xDA, 0x49, 0x0F, +0xC3, 0x39, 0xC5, 0x47, 0x61, 0x37, 0x5F, 0x0B, 0xD2, 0x88, 0xB7, 0x1E, 0x6E, 0x88, 0x8F, 0xE3, +0x02, 0xC4, 0x4E, 0x58, 0x80, 0x00, 0x01, 0x38, 0x8B, 0xCD, 0xD0, 0xDA, 0x5D, 0xFA, 0xF8, 0x38, +0x6A, 0xF6, 0xA3, 0xBF, 0x08, 0x3D, 0x94, 0x9F, 0x86, 0x55, 0x8A, 0x89, 0x22, 0x4A, 0xB6, 0x8F, +0xD8, 0xCE, 0x77, 0x2F, 0xA6, 0xFF, 0x20, 0x5E, 0xFE, 0xD0, 0xC3, 0x59, 0x90, 0x2A, 0xC5, 0x47, +0x92, 0x66, 0x40, 0xD2, 0x4B, 0x8C, 0x0B, 0xB3, 0x16, 0x64, 0xE1, 0xE2, 0x23, 0xBF, 0x8B, 0x7C, +0x27, 0xAC, 0x86, 0x53, 0x07, 0x20, 0x40, 0x00, 0x4E, 0x23, 0x6B, 0x5D, 0xCF, 0xAF, 0x07, 0xB1, +0xCC, 0xF1, 0x11, 0x8F, 0x9A, 0xFD, 0xE8, 0xBE, 0x23, 0x96, 0xAF, 0x60, 0x1E, 0x6B, 0x83, 0xBB, +0x5F, 0xA5, 0x3F, 0x8E, 0x98, 0xFD, 0x28, 0xEE, 0xB4, 0xFD, 0xD5, 0x38, 0x1A, 0x2E, 0xE5, 0xF0, +0x89, 0x33, 0x38, 0x66, 0xD3, 0x8E, 0x8F, 0xC2, 0xEE, 0x33, 0xF9, 0xE0, 0x3A, 0x7E, 0xF4, 0xA1, +0x73, 0x1F, 0x60, 0x2F, 0x64, 0x7C, 0xE4, 0xFF, 0x66, 0xF2, 0x19, 0x90, 0x6B, 0x4E, 0x1E, 0x80, +0x00, 0x01, 0x38, 0x8D, 0x81, 0x05, 0xE8, 0x4B, 0x18, 0x1F, 0x45, 0x04, 0x0C, 0x07, 0xC9, 0x98, +0x40, 0x89, 0xB1, 0x36, 0x70, 0x7B, 0x7F, 0x46, 0x64, 0x3B, 0x1E, 0xFB, 0xB8, 0xD3, 0x7F, 0x10, +0xCF, 0xB6, 0x0F, 0x1F, 0xD1, 0xC9, 0x66, 0x41, 0x16, 0x28, 0x3E, 0xD2, 0x63, 0x49, 0x33, 0x20, +0xCD, 0x7C, 0xA0, 0x7F, 0xAE, 0xB3, 0x20, 0x0B, 0x1B, 0x1F, 0xFD, 0x08, 0xD9, 0xAF, 0x3B, 0x79, +0x00, 0x02, 0x04, 0xE0, 0x74, 0x03, 0xA9, 0xCD, 0xA5, 0x8E, 0x8F, 0x78, 0xE7, 0x9D, 0xAF, 0x62, +0xB9, 0x1C, 0x86, 0x17, 0x91, 0xEF, 0xA7, 0x41, 0x79, 0xBC, 0xE3, 0xE3, 0xCE, 0xB7, 0xE7, 0x3D, +0x6E, 0x16, 0xA4, 0x0A, 0xF1, 0x51, 0xD8, 0xC9, 0x77, 0xC4, 0x4A, 0xB3, 0x20, 0x37, 0xC5, 0xC7, +0x51, 0xDA, 0x0D, 0x27, 0x0F, 0x40, 0x80, 0x00, 0x9C, 0x74, 0x58, 0xF6, 0xD4, 0x1B, 0xEB, 0xDD, +0xA7, 0x5F, 0x2D, 0x69, 0x7C, 0x1C, 0x15, 0x24, 0x61, 0x30, 0x44, 0x06, 0xD7, 0x83, 0x0C, 0xC5, +0xC8, 0x76, 0x9C, 0xE8, 0x71, 0xA7, 0x6B, 0x82, 0x14, 0xB3, 0x20, 0xFD, 0x47, 0x38, 0x70, 0x55, +0xF5, 0x8A, 0xC4, 0x47, 0x5E, 0x53, 0xCD, 0xEE, 0xB5, 0x41, 0xCE, 0x61, 0x16, 0xA4, 0x1A, 0xF1, +0x91, 0xFA, 0x63, 0xDB, 0x4E, 0x58, 0x80, 0x00, 0x01, 0x38, 0x85, 0x7A, 0x68, 0x6D, 0x2D, 0x67, +0x7C, 0x94, 0x76, 0xB4, 0x1A, 0x78, 0xBB, 0xFC, 0xF7, 0x71, 0xB3, 0x1F, 0xE5, 0x19, 0x93, 0x34, +0xFB, 0xD1, 0x9E, 0xFC, 0x71, 0x17, 0xB3, 0x20, 0xF1, 0x8E, 0xB3, 0x20, 0x0B, 0x1C, 0x1F, 0x85, +0xEE, 0x8E, 0x58, 0xF5, 0x79, 0xCE, 0x82, 0x54, 0x26, 0x3E, 0xF2, 0x3D, 0x99, 0x0F, 0xBA, 0xFF, +0x7E, 0x00, 0x04, 0x08, 0xC0, 0x89, 0x46, 0x52, 0x8D, 0xD0, 0x6E, 0x2E, 0x5F, 0x7C, 0x84, 0xD1, +0xC1, 0x7F, 0xEC, 0x7D, 0xED, 0x71, 0xB3, 0x1C, 0x47, 0x4E, 0x50, 0xEC, 0xC4, 0x13, 0x3D, 0xEE, +0x7C, 0x16, 0xE4, 0xA5, 0xC1, 0xBB, 0x1F, 0x88, 0x9A, 0x58, 0x91, 0xF8, 0xC8, 0x6B, 0x6A, 0xBE, +0xB3, 0x20, 0x95, 0x8A, 0x8F, 0xFC, 0xCF, 0x3C, 0x40, 0x5C, 0x90, 0x10, 0x10, 0x20, 0x00, 0x27, +0x74, 0xDF, 0xD1, 0x17, 0x21, 0xAC, 0x70, 0x7C, 0x0C, 0xAF, 0xE3, 0xE8, 0x0D, 0xFC, 0x63, 0xA8, +0x0D, 0xCC, 0x7C, 0x94, 0x2F, 0x3A, 0x38, 0x32, 0x4B, 0x52, 0x9E, 0xFD, 0x38, 0xC1, 0xE3, 0x6E, +0xDF, 0x8E, 0xBD, 0x67, 0xB5, 0x95, 0x66, 0x41, 0x6A, 0x53, 0x1C, 0x14, 0xCF, 0x23, 0x3E, 0x0A, +0x69, 0x16, 0x24, 0xB6, 0x67, 0x3E, 0x0B, 0x52, 0xB9, 0xF8, 0xC8, 0x7F, 0xD0, 0xF9, 0xF3, 0xED, +0xEC, 0x84, 0x05, 0x08, 0x10, 0x80, 0x13, 0x69, 0x6D, 0x9D, 0xF2, 0xBF, 0xE0, 0x56, 0x2B, 0x3E, +0x62, 0x88, 0x63, 0x9F, 0x92, 0xD5, 0xBF, 0x79, 0x60, 0x1B, 0xDE, 0xCE, 0x4B, 0xD6, 0x79, 0xD9, +0x8A, 0xA7, 0x7B, 0xDC, 0x9D, 0xCF, 0xCD, 0xFA, 0xCF, 0x6A, 0xAB, 0x95, 0xBE, 0x5E, 0x1C, 0xFC, +0xDA, 0x8B, 0x1E, 0x1F, 0xF9, 0xF7, 0xD2, 0x89, 0xD3, 0xBD, 0x67, 0xD3, 0x5B, 0x4F, 0x74, 0x22, +0x64, 0x43, 0x7C, 0x0C, 0x1F, 0x9F, 0x7D, 0x33, 0x20, 0x80, 0x00, 0x01, 0x38, 0xD9, 0x60, 0xB9, +0x75, 0x8A, 0x01, 0x54, 0x85, 0x16, 0x9C, 0x1F, 0x16, 0x48, 0x77, 0xF6, 0x23, 0x0C, 0x46, 0x47, +0x7F, 0xD7, 0xAA, 0xE1, 0x87, 0xB9, 0x5B, 0x2E, 0x93, 0x93, 0x2B, 0x66, 0x41, 0x62, 0x2C, 0x05, +0x50, 0x6D, 0x06, 0x03, 0xE0, 0x79, 0x1C, 0xCF, 0xBD, 0x3F, 0x4B, 0xB3, 0x20, 0x29, 0x3E, 0x1E, +0x13, 0x1F, 0xC3, 0xB7, 0xB7, 0x04, 0x08, 0x20, 0x40, 0x00, 0x26, 0x1E, 0x53, 0x3D, 0xF5, 0x86, +0x8D, 0xD0, 0xDE, 0x3F, 0xE1, 0x7F, 0xD5, 0xAE, 0x46, 0x7C, 0x94, 0x2F, 0x34, 0x38, 0x6E, 0xDB, +0xDD, 0xC3, 0x1B, 0x6B, 0xA3, 0x3B, 0x54, 0x65, 0x45, 0x80, 0x9C, 0xE1, 0x71, 0x77, 0xEE, 0xA3, +0xFD, 0x52, 0x3C, 0x7C, 0x2C, 0xE3, 0xB6, 0xF7, 0x8D, 0xF3, 0x3A, 0x86, 0x67, 0xFD, 0xF9, 0xB5, +0x8B, 0x59, 0x90, 0x47, 0xA7, 0x39, 0x0B, 0x52, 0xF9, 0xF8, 0xC8, 0x7F, 0xCE, 0xFB, 0x1B, 0xF1, +0x13, 0xEF, 0xD8, 0x70, 0x36, 0x01, 0x04, 0x08, 0xC0, 0x64, 0x36, 0x43, 0x7B, 0x77, 0xB9, 0xE2, +0x23, 0x8E, 0xF9, 0xCB, 0xF0, 0x16, 0xBB, 0x21, 0x8C, 0xDD, 0xA9, 0x6A, 0x60, 0xF6, 0x23, 0x3B, +0xFB, 0x60, 0x36, 0xBB, 0x9D, 0xD6, 0x29, 0xC7, 0xC1, 0xF0, 0x38, 0x4D, 0x84, 0x9C, 0x67, 0x7C, +0x14, 0xA6, 0x3C, 0x0B, 0xB2, 0x14, 0xF1, 0x91, 0xBF, 0xBF, 0xD9, 0xFD, 0x77, 0x04, 0x20, 0x40, +0x00, 0x26, 0xD2, 0xBB, 0x06, 0xC8, 0x92, 0xC4, 0xC7, 0xC0, 0x67, 0xC6, 0x23, 0xB7, 0xDD, 0x1D, +0x89, 0x8E, 0x91, 0xD9, 0x8F, 0x6C, 0x6A, 0x03, 0xFE, 0xF6, 0x71, 0xDB, 0xF2, 0xCE, 0xFC, 0x18, +0x4E, 0x71, 0x80, 0x9E, 0x7E, 0x4F, 0xF2, 0x6D, 0x79, 0xE3, 0xA3, 0xF1, 0xA3, 0xAF, 0x3B, 0xD3, +0x7F, 0xF1, 0x5F, 0x9A, 0xF8, 0xC8, 0x7F, 0x5F, 0xF6, 0x05, 0x08, 0x20, 0x40, 0x00, 0x4E, 0x14, +0x20, 0x07, 0x93, 0x5C, 0x03, 0xA4, 0x22, 0xF1, 0x91, 0x3E, 0xAD, 0x36, 0xF4, 0xF9, 0xE5, 0xB5, +0x1F, 0xA5, 0x87, 0x76, 0xE4, 0xEC, 0xC7, 0x56, 0xD6, 0x8D, 0x90, 0x29, 0x0D, 0xF8, 0xD3, 0x85, +0x09, 0xD3, 0xD6, 0xBC, 0xA5, 0x06, 0x1A, 0x9D, 0x05, 0x59, 0xF4, 0xF8, 0x28, 0xA4, 0x59, 0x90, +0xAC, 0x99, 0xE2, 0xE3, 0x09, 0xF1, 0x51, 0x04, 0xC8, 0x5E, 0x7A, 0x6D, 0x27, 0x2C, 0x40, 0x80, +0x00, 0x4C, 0xA4, 0xBD, 0x77, 0x63, 0x69, 0xE2, 0xA3, 0xFF, 0xA5, 0xCB, 0x4F, 0xBB, 0x2A, 0x45, +0x48, 0xF9, 0x42, 0x83, 0xE5, 0x87, 0x59, 0x5E, 0xEA, 0xD1, 0xEE, 0xBC, 0xB1, 0x37, 0x8D, 0x01, +0xED, 0xE0, 0x7D, 0x14, 0x17, 0x27, 0x0C, 0x47, 0xCD, 0x82, 0xC4, 0x0A, 0xC4, 0x47, 0x71, 0x3F, +0x3B, 0x69, 0x5B, 0xDE, 0x70, 0x33, 0xFE, 0xDE, 0xEB, 0xEA, 0x2B, 0x1F, 0x1F, 0xF9, 0xC7, 0xA6, +0x9D, 0x06, 0xDA, 0x66, 0x40, 0x00, 0x01, 0x02, 0x30, 0x59, 0x80, 0x34, 0x37, 0x96, 0x26, 0x3E, +0xF2, 0xD9, 0x8F, 0x38, 0x72, 0x5B, 0x79, 0xED, 0xC7, 0xE1, 0xCE, 0x57, 0xB5, 0xD1, 0x87, 0x99, +0x6E, 0xD8, 0x9E, 0x7E, 0x7C, 0xE4, 0x01, 0x92, 0x66, 0x41, 0xF6, 0x87, 0x3E, 0xE2, 0xB8, 0x59, +0x90, 0x45, 0x8D, 0x8F, 0xF4, 0x47, 0xBA, 0x30, 0x61, 0xF7, 0xBA, 0x31, 0x27, 0xBA, 0x38, 0xE1, +0x52, 0xC6, 0x47, 0xFF, 0x07, 0xBC, 0x5B, 0x77, 0x32, 0x01, 0x04, 0x08, 0xC0, 0x44, 0x03, 0xA7, +0xE3, 0xAE, 0x61, 0x50, 0xBD, 0xAD, 0x76, 0x63, 0x2C, 0x87, 0x47, 0xB9, 0x3A, 0xC6, 0xAC, 0xF5, +0x18, 0x78, 0xC8, 0xB1, 0xFB, 0xB4, 0xAB, 0xBD, 0x29, 0x6C, 0xF7, 0x7B, 0x54, 0xEB, 0xBD, 0xD8, +0x5B, 0x93, 0x32, 0x6E, 0x16, 0xA4, 0x56, 0xFA, 0xD4, 0x45, 0x8E, 0x8F, 0xDE, 0x63, 0x8D, 0xBB, +0x5F, 0x4C, 0x7F, 0xBD, 0x99, 0xFD, 0xEE, 0x64, 0xB3, 0x20, 0x4B, 0x1D, 0x1F, 0xF9, 0xBF, 0xA3, +0xA6, 0x00, 0x01, 0x04, 0x08, 0xC0, 0x1D, 0xC7, 0x5A, 0x4F, 0xBD, 0x61, 0x33, 0x64, 0x07, 0xCB, +0x11, 0x1F, 0x71, 0xCC, 0x35, 0x36, 0x86, 0x77, 0xBE, 0x8A, 0x43, 0xB3, 0x1F, 0xE5, 0x05, 0x19, +0xE9, 0xCF, 0xED, 0xD9, 0xC5, 0x47, 0xFE, 0xDE, 0xFD, 0x62, 0xBD, 0xF2, 0x98, 0xB5, 0x20, 0x55, +0x8A, 0x8F, 0x62, 0x16, 0xE4, 0x60, 0x2B, 0xDD, 0x7C, 0xC7, 0xB5, 0x20, 0x4B, 0x1F, 0x1F, 0xF9, +0xE7, 0x36, 0x3B, 0xDF, 0xE7, 0x3B, 0x1A, 0xCE, 0x2A, 0x80, 0x00, 0x01, 0x38, 0xDE, 0xC6, 0xF8, +0x2D, 0x78, 0x2B, 0x78, 0x91, 0xC1, 0x81, 0x41, 0xFC, 0x11, 0xB3, 0x1F, 0x63, 0x3F, 0xB3, 0xF7, +0x8E, 0xD4, 0x61, 0x7B, 0xB3, 0xFF, 0x1E, 0xD2, 0x2C, 0xC8, 0xD1, 0x6B, 0x41, 0x2A, 0x12, 0x1F, +0xC5, 0x31, 0x4E, 0x3B, 0x62, 0xC5, 0xDA, 0xDB, 0xDA, 0xBF, 0xF3, 0x50, 0x63, 0xA5, 0xE3, 0x23, +0xE9, 0x96, 0x65, 0xDD, 0x29, 0x05, 0x10, 0x20, 0x00, 0xC7, 0x6B, 0x84, 0xD6, 0x6E, 0xF5, 0xE3, +0xA3, 0xBF, 0xF3, 0xD5, 0xF0, 0xCD, 0xC7, 0xCF, 0x7E, 0xE4, 0x8B, 0xD5, 0x8B, 0xA9, 0x88, 0xED, +0xF9, 0x7C, 0x0F, 0xF9, 0x2C, 0xC8, 0xEE, 0x40, 0x1B, 0xF5, 0x5F, 0x4D, 0x67, 0x68, 0x3D, 0xA7, +0xF8, 0x48, 0x7F, 0x36, 0xB7, 0xBB, 0xB3, 0x20, 0x71, 0xFC, 0x5A, 0x90, 0x95, 0x89, 0x8F, 0x3C, +0x40, 0x9A, 0x02, 0x04, 0x10, 0x20, 0x00, 0x13, 0xB8, 0x36, 0x78, 0x0D, 0x90, 0x6A, 0xCE, 0x7C, +0x0C, 0x3C, 0x8C, 0x09, 0x67, 0x3F, 0x06, 0x86, 0xFB, 0x69, 0xF6, 0xE3, 0x20, 0xCE, 0xED, 0x7B, +0xC8, 0x5E, 0x18, 0x9E, 0x05, 0xA9, 0xD8, 0xCC, 0x47, 0xE8, 0x3E, 0xF6, 0xBC, 0xDF, 0xBA, 0x3B, +0x62, 0x35, 0x0E, 0x7E, 0x7B, 0x70, 0x16, 0x64, 0xA5, 0xE2, 0x23, 0xBF, 0x9F, 0xFC, 0xA9, 0x8C, +0x37, 0x9C, 0x52, 0x00, 0x01, 0x02, 0x70, 0x9C, 0xE6, 0x0B, 0xF5, 0x65, 0x88, 0x8F, 0x93, 0xCF, +0x7E, 0x94, 0x66, 0x3E, 0xCE, 0x34, 0xFB, 0x71, 0xBA, 0xCF, 0x4B, 0xD7, 0x04, 0x49, 0xBB, 0x62, +0x95, 0x1A, 0x69, 0xF2, 0xEB, 0x82, 0x2C, 0x52, 0x7C, 0xA4, 0x97, 0xE6, 0x56, 0xC8, 0x9A, 0x69, +0x16, 0xA4, 0xF6, 0xF8, 0xCA, 0xC6, 0x47, 0xA1, 0xBD, 0x55, 0x77, 0x52, 0x01, 0x16, 0xCD, 0xBA, +0x43, 0x00, 0x2C, 0x94, 0x98, 0x35, 0x2A, 0x1F, 0x1F, 0xC3, 0xD7, 0xFA, 0x28, 0x7F, 0xC8, 0xF0, +0xEC, 0x47, 0x2F, 0x3E, 0x06, 0xBE, 0xFA, 0xA9, 0x67, 0x3F, 0xCE, 0xF6, 0x3D, 0xA4, 0xEB, 0x82, +0xAC, 0xDD, 0x53, 0x6A, 0xA7, 0xDA, 0x22, 0x0C, 0xD0, 0x4F, 0x16, 0x1F, 0xC5, 0xF1, 0xCD, 0xB6, +0xBE, 0x10, 0x6A, 0xF7, 0x3D, 0xD8, 0xD8, 0xFF, 0xDF, 0x1F, 0x6E, 0x5C, 0xFA, 0xD6, 0x6F, 0x7C, +0x61, 0x25, 0xE3, 0xA3, 0x5B, 0x96, 0x02, 0x04, 0x58, 0x38, 0x66, 0x40, 0x80, 0xC5, 0x69, 0x8F, +0xA7, 0xDE, 0xB0, 0xD1, 0x7D, 0xDE, 0x7A, 0x85, 0xE3, 0x63, 0xDC, 0xC0, 0x7D, 0xF8, 0xAA, 0xE7, +0xE5, 0xD9, 0x8F, 0xF2, 0x00, 0xBB, 0x78, 0xB9, 0x9D, 0x9D, 0xC3, 0xF7, 0xD0, 0x19, 0xB4, 0x77, +0xA2, 0xA7, 0x7D, 0x7B, 0xE8, 0x1E, 0x4F, 0x35, 0x0B, 0x72, 0xCE, 0xF1, 0x91, 0x75, 0x6E, 0x3B, +0xD8, 0x0D, 0xD9, 0xEE, 0xF3, 0x21, 0x5C, 0xB9, 0xF8, 0xA1, 0x95, 0x8D, 0x8F, 0xFC, 0x7E, 0xD3, +0x4E, 0x58, 0x3F, 0xE2, 0x82, 0x84, 0x80, 0x00, 0x01, 0x38, 0x62, 0x10, 0xB6, 0x39, 0xBA, 0x00, +0xBD, 0x62, 0xF1, 0x11, 0x8F, 0x5E, 0xFB, 0xD1, 0x7F, 0x8A, 0x50, 0x3F, 0x38, 0xE2, 0xE8, 0xB5, +0x40, 0xD2, 0xAE, 0x57, 0xED, 0xF9, 0x0F, 0x8A, 0x8B, 0x2B, 0xB5, 0xE7, 0x3B, 0x62, 0xB5, 0x8F, +0xB8, 0x2E, 0x48, 0x55, 0xE2, 0x23, 0xBD, 0x9D, 0x37, 0xDC, 0x76, 0xB8, 0xF4, 0x1F, 0x7E, 0x6D, +0x7D, 0x65, 0xE3, 0x23, 0x3D, 0xDE, 0xF6, 0x4E, 0x7A, 0xA3, 0xEE, 0xE4, 0x02, 0x08, 0x10, 0x80, +0xF1, 0x36, 0x7B, 0x3B, 0xF7, 0x54, 0x33, 0x3E, 0x8E, 0xF8, 0x90, 0xE1, 0xB5, 0x1F, 0xA1, 0xB4, +0xDC, 0x63, 0xE4, 0xEE, 0x76, 0xE2, 0x9C, 0xBF, 0x87, 0xC3, 0xF8, 0xC8, 0x75, 0x06, 0xEE, 0xC5, +0x2C, 0xC8, 0xC0, 0x5A, 0x90, 0x38, 0xC9, 0x57, 0x5A, 0x9C, 0xF8, 0xA8, 0xDD, 0x73, 0x6F, 0xB8, +0xFC, 0xED, 0xDF, 0x19, 0xC2, 0x85, 0xCE, 0xFF, 0xCD, 0xAD, 0x6A, 0x7C, 0xE4, 0x3F, 0xCF, 0x66, +0xF7, 0xDF, 0x15, 0x80, 0x00, 0x01, 0x18, 0xEB, 0x88, 0x6B, 0x80, 0x54, 0x20, 0x3E, 0xCA, 0x83, +0xF4, 0x81, 0x01, 0x7B, 0x6D, 0x60, 0xED, 0x47, 0x2C, 0x1E, 0x77, 0xF9, 0x62, 0x7F, 0xA7, 0x9E, +0xFD, 0x98, 0x72, 0x7C, 0xF4, 0xB4, 0x6F, 0x77, 0x67, 0x41, 0x06, 0xAE, 0x0B, 0x52, 0x9B, 0xFD, +0x63, 0x99, 0x6A, 0x7C, 0x5C, 0xFF, 0xDE, 0x50, 0x5B, 0xBF, 0xB8, 0xDA, 0xF1, 0x91, 0xBF, 0x99, +0xEF, 0x84, 0x75, 0xDD, 0xA9, 0x05, 0x10, 0x20, 0x00, 0xE3, 0xB4, 0xB6, 0x26, 0xDF, 0x32, 0x74, +0x91, 0x67, 0x3E, 0x6A, 0x83, 0x17, 0x33, 0x2F, 0x07, 0x4A, 0xB1, 0xE0, 0x7C, 0x64, 0xA7, 0xDB, +0x13, 0xCF, 0x7E, 0xCC, 0x26, 0x3E, 0x72, 0xA5, 0x59, 0x90, 0x50, 0xCC, 0xDE, 0x1C, 0x3B, 0x0B, +0x22, 0x3E, 0x16, 0x32, 0x3E, 0xFA, 0x45, 0xB9, 0x53, 0x77, 0x72, 0x01, 0x04, 0x08, 0xC0, 0xF8, +0x41, 0x59, 0x7D, 0xC2, 0x8F, 0x5B, 0xAC, 0xF8, 0x18, 0x5E, 0xA8, 0xDD, 0xFF, 0x7B, 0x6D, 0x60, +0xE6, 0x23, 0xF6, 0x9F, 0x7B, 0x55, 0x1B, 0xDD, 0x0D, 0x6B, 0xE7, 0x24, 0xB3, 0x1F, 0x33, 0x8C, +0x8F, 0x62, 0xCC, 0x9A, 0x66, 0x41, 0x0E, 0x4A, 0x03, 0xFD, 0x5E, 0x10, 0x88, 0x8F, 0x8A, 0xC5, +0x47, 0xF7, 0xA7, 0xE9, 0x29, 0x58, 0x80, 0x00, 0x01, 0x18, 0x3F, 0x4E, 0x9A, 0xE0, 0xBF, 0xD4, +0x56, 0x20, 0x3E, 0x46, 0x66, 0x0C, 0xCA, 0x0B, 0xCE, 0xC3, 0xE1, 0x62, 0xF4, 0xFE, 0x27, 0x66, +0xE1, 0x04, 0xB3, 0x1F, 0xB3, 0x8F, 0x8F, 0x5C, 0x96, 0xC6, 0xEF, 0x71, 0x20, 0xA4, 0x46, 0xBF, +0x57, 0xF1, 0xB1, 0xF8, 0xF1, 0x91, 0x7E, 0x96, 0x7B, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xC0, 0xC8, +0xF0, 0xE9, 0xC9, 0x47, 0x1A, 0xA1, 0xDD, 0xAC, 0x56, 0x7C, 0x1C, 0xF3, 0xA1, 0xFD, 0x6D, 0x77, +0x63, 0x77, 0xE6, 0xE3, 0x70, 0xF0, 0x5C, 0x1B, 0xBD, 0xCB, 0xDD, 0x38, 0xE1, 0xDD, 0xCF, 0x29, +0x3E, 0x8A, 0x71, 0xEB, 0x76, 0xDA, 0x9A, 0x77, 0xA8, 0x0F, 0xE2, 0xD0, 0xFD, 0x9C, 0xF9, 0x21, +0x89, 0x8F, 0x99, 0xC6, 0x47, 0x1E, 0xF6, 0x7B, 0xE9, 0xF5, 0x86, 0xB3, 0x0C, 0x20, 0x40, 0x00, +0x06, 0x6D, 0x1C, 0xBB, 0x03, 0xD6, 0x02, 0xC7, 0xC7, 0x51, 0x17, 0x1D, 0xEC, 0x0E, 0x9A, 0xCB, +0x37, 0xD6, 0x06, 0x67, 0x3F, 0xF2, 0x51, 0x7E, 0x98, 0x70, 0xF6, 0x63, 0xBE, 0xF1, 0xD1, 0x1F, +0xBB, 0xBE, 0x18, 0xFB, 0xD7, 0x2C, 0xE9, 0x3F, 0x04, 0x33, 0x1F, 0xD5, 0x89, 0x8F, 0xFC, 0x43, +0xF2, 0x85, 0xE8, 0x0D, 0xA7, 0x18, 0x40, 0x80, 0x00, 0x0C, 0xDA, 0x3C, 0x72, 0x07, 0xAC, 0x45, +0x8D, 0x8F, 0x38, 0xFE, 0xB6, 0xD8, 0x0B, 0x8D, 0xF2, 0x75, 0x3E, 0x46, 0xAE, 0xAB, 0x71, 0xA2, +0xD9, 0x8F, 0xF3, 0x89, 0x8F, 0xBC, 0x8F, 0xB6, 0xF3, 0x6B, 0xD9, 0xF5, 0x1F, 0x45, 0x39, 0xA8, +0xE2, 0x34, 0x8E, 0xAB, 0xF8, 0x98, 0xFD, 0xE3, 0xCD, 0xF2, 0x19, 0x90, 0x6B, 0x4E, 0x31, 0x80, +0x00, 0x01, 0x18, 0x18, 0x24, 0xB5, 0xAE, 0x77, 0xF7, 0x7E, 0xAD, 0x48, 0x7C, 0x0C, 0x7F, 0xC4, +0xC8, 0x8E, 0x57, 0x71, 0xE4, 0xB3, 0x47, 0x66, 0x3F, 0xD2, 0xB7, 0xBB, 0x1D, 0x67, 0xFC, 0x3D, +0x9C, 0x3E, 0x3E, 0xFA, 0x0F, 0xF3, 0xAB, 0xDD, 0x59, 0x90, 0x7C, 0xB6, 0x26, 0x4E, 0xE3, 0x21, +0x8A, 0x8F, 0xB9, 0xC5, 0x47, 0xFF, 0x87, 0x68, 0x27, 0x2C, 0x40, 0x80, 0x00, 0x0C, 0x6A, 0x6D, +0x6D, 0x54, 0x2A, 0x3E, 0x62, 0x18, 0xDD, 0x15, 0xAA, 0x3F, 0xD3, 0x11, 0x4B, 0x7F, 0x3F, 0x66, +0xF6, 0xA3, 0x02, 0xF1, 0x91, 0xB7, 0xE1, 0x7E, 0xE7, 0x7E, 0xF6, 0xE2, 0xE0, 0xA3, 0x3A, 0xF5, +0x2C, 0x88, 0xF8, 0x98, 0x7B, 0x7C, 0x74, 0x35, 0x9C, 0x64, 0x00, 0x01, 0x02, 0x30, 0x30, 0xA6, +0x3A, 0x68, 0x54, 0x26, 0x3E, 0x86, 0x1F, 0x62, 0x1C, 0xDC, 0x19, 0x6A, 0xDC, 0x43, 0x2F, 0x0F, +0xA8, 0x73, 0x69, 0xF6, 0x63, 0x2F, 0x2E, 0x7C, 0x7C, 0x14, 0x8F, 0x25, 0xAD, 0x05, 0x09, 0xE3, +0x62, 0xAA, 0x17, 0x11, 0x27, 0x5A, 0x44, 0x2F, 0x3E, 0xE6, 0xFF, 0x78, 0xB3, 0xED, 0xB4, 0x13, +0x56, 0xDD, 0x89, 0x06, 0x10, 0x20, 0x00, 0x21, 0xDF, 0x01, 0xAB, 0x9E, 0x8F, 0x2E, 0xAB, 0x12, +0x1F, 0x47, 0xCE, 0x7E, 0x0C, 0xAE, 0xD3, 0x2E, 0x66, 0x3F, 0x46, 0xBE, 0x9D, 0xF4, 0xF7, 0xED, +0xEA, 0xC4, 0x47, 0x3E, 0x7E, 0xDD, 0xEF, 0xBE, 0x14, 0xB7, 0x0C, 0xEE, 0x88, 0x25, 0x3E, 0x16, +0x3A, 0x3E, 0xF2, 0x1F, 0x60, 0xBE, 0x10, 0x5D, 0x80, 0x00, 0x02, 0x04, 0xA0, 0xA7, 0x1E, 0x0E, +0xB6, 0xAA, 0x11, 0x1F, 0xC3, 0x63, 0xCC, 0xFE, 0xE0, 0x39, 0x1E, 0x5E, 0xFB, 0xE3, 0xA8, 0xCB, +0x85, 0x14, 0xB7, 0xB7, 0xC2, 0x31, 0xB3, 0x1F, 0x8B, 0x17, 0x1F, 0x85, 0xD6, 0xF3, 0xF1, 0x70, +0x2B, 0xE1, 0x13, 0xCD, 0x82, 0x88, 0x8F, 0x73, 0x8D, 0x8F, 0xFC, 0xD3, 0xED, 0x84, 0x05, 0x08, +0x10, 0x80, 0xB2, 0x46, 0xBE, 0x05, 0x6F, 0x15, 0xE2, 0x63, 0xEC, 0xEC, 0x47, 0xEC, 0xEE, 0x7C, +0x35, 0x34, 0x0E, 0xED, 0xCF, 0x7E, 0x94, 0x9F, 0x7A, 0x95, 0xFE, 0xDC, 0xAA, 0x5E, 0x7C, 0xE4, +0xB7, 0xB4, 0xBA, 0xBB, 0x62, 0x85, 0x70, 0x92, 0x59, 0x10, 0xF1, 0x71, 0xEE, 0xF1, 0x91, 0xB4, +0xF3, 0x1F, 0x9C, 0x9D, 0xB0, 0x00, 0x01, 0x02, 0xD0, 0x73, 0x5F, 0x68, 0xEF, 0x2F, 0x7E, 0x7C, +0x0C, 0x8F, 0x33, 0xCB, 0xD7, 0xF9, 0xB8, 0xD3, 0xEC, 0x47, 0x21, 0xFD, 0x87, 0xE8, 0x83, 0x38, +0x83, 0xEF, 0x61, 0xB6, 0xF1, 0xD1, 0x1F, 0xC7, 0xBE, 0x74, 0x87, 0x59, 0x10, 0xF1, 0xB1, 0x78, +0xF1, 0x51, 0xC8, 0xF6, 0xEB, 0x4E, 0x35, 0x80, 0x00, 0x01, 0x48, 0x5A, 0x5B, 0x9B, 0x55, 0x89, +0x8F, 0x81, 0x41, 0x76, 0x71, 0x9D, 0x8F, 0x49, 0x66, 0x3F, 0x8A, 0x97, 0xED, 0xEA, 0xC6, 0x47, +0xFE, 0xDE, 0xD2, 0x2C, 0x48, 0xFF, 0xA3, 0xCB, 0xB3, 0x20, 0x51, 0x7C, 0x2C, 0x64, 0x7C, 0x74, +0x7F, 0x78, 0x9B, 0x4E, 0x36, 0x80, 0x00, 0x01, 0x48, 0xB2, 0xD3, 0x0E, 0x8C, 0xE6, 0x1C, 0x1F, +0xE5, 0x19, 0x8E, 0xF2, 0x75, 0x3E, 0xC6, 0xCD, 0x7E, 0xC4, 0x30, 0xB8, 0x3B, 0x56, 0x32, 0x76, +0xF6, 0xA3, 0x3A, 0xF1, 0x51, 0xC8, 0x67, 0x41, 0xDA, 0xC7, 0xEC, 0x88, 0x25, 0x3E, 0x16, 0x2F, +0x3E, 0xF2, 0x7F, 0x67, 0x3B, 0x1B, 0xF1, 0x13, 0x3F, 0xBC, 0xE1, 0x84, 0x03, 0x08, 0x10, 0x80, +0xF6, 0xDE, 0x29, 0x06, 0x45, 0x73, 0x8E, 0x8F, 0xFE, 0xE0, 0x3A, 0x0C, 0x2E, 0x38, 0x1F, 0x7A, +0xDE, 0x51, 0x7F, 0xF6, 0x63, 0x28, 0x44, 0xC6, 0xCF, 0x7E, 0x54, 0x2F, 0x3E, 0xF2, 0x8F, 0x6C, +0x75, 0x7E, 0x64, 0x5B, 0x83, 0xDF, 0xDE, 0x61, 0x84, 0xC5, 0xC1, 0x35, 0x2F, 0xE2, 0x63, 0x71, +0x1E, 0x6F, 0x96, 0x5F, 0xD2, 0xDE, 0x2C, 0x08, 0x20, 0x40, 0x80, 0xD5, 0x16, 0x9F, 0x7C, 0xA4, +0x11, 0xDA, 0xBB, 0x8B, 0x1F, 0x1F, 0xFD, 0x01, 0x73, 0x1C, 0xB9, 0xEA, 0x79, 0x2C, 0x6F, 0xC1, +0x3B, 0xBC, 0x1E, 0xA4, 0xF8, 0xC0, 0xB4, 0xEB, 0xD5, 0x41, 0xAC, 0x7C, 0x7C, 0xF4, 0x9B, 0xF1, +0x76, 0x77, 0x16, 0x64, 0x60, 0x2D, 0x48, 0xCD, 0xCC, 0xC7, 0xC2, 0xC6, 0x47, 0x7E, 0xB0, 0x0F, +0x04, 0x08, 0x20, 0x40, 0x00, 0x42, 0xDA, 0x82, 0x37, 0x1F, 0xC9, 0x2E, 0x70, 0x7C, 0x8C, 0x1B, +0x74, 0x8E, 0x59, 0xFB, 0x31, 0xF6, 0xB6, 0xE2, 0x4B, 0xEE, 0x2C, 0x4F, 0x7C, 0xE4, 0xB2, 0x14, +0x21, 0xE5, 0x7B, 0x89, 0x23, 0x4F, 0x3B, 0x2B, 0xAF, 0x81, 0x11, 0x1F, 0xE7, 0x1C, 0x1F, 0xF9, +0xCF, 0x6C, 0x2F, 0xBD, 0xB6, 0x13, 0x16, 0x20, 0x40, 0x00, 0x01, 0xD2, 0xBF, 0x06, 0xC8, 0x22, +0xC6, 0xC7, 0x98, 0xA7, 0x16, 0x75, 0xEF, 0xA5, 0x36, 0x30, 0xD3, 0x31, 0x30, 0xFB, 0x11, 0xC6, +0xCC, 0x7E, 0xB4, 0x97, 0x28, 0x3E, 0x7A, 0xDA, 0x5B, 0xC5, 0x5A, 0x90, 0x30, 0xB8, 0x06, 0xA6, +0x36, 0xF8, 0xFD, 0x8B, 0x8F, 0x05, 0x88, 0x8F, 0xFC, 0xCD, 0xF4, 0xC3, 0x6A, 0x9B, 0x01, 0x01, +0x04, 0x08, 0xB0, 0xE2, 0xDA, 0x7B, 0x37, 0x16, 0x36, 0x3E, 0x86, 0x07, 0x9D, 0xB5, 0xC3, 0xD0, +0x18, 0x37, 0xCB, 0x31, 0x7C, 0x6D, 0x8C, 0xFE, 0x97, 0xDD, 0x89, 0x4B, 0x17, 0x1F, 0xB9, 0x4E, +0x38, 0xB4, 0xBE, 0x9A, 0xF5, 0x1E, 0x57, 0x6D, 0xE0, 0x70, 0xF5, 0x8F, 0x53, 0x14, 0x1F, 0x0B, +0x11, 0x1F, 0xFD, 0x9F, 0xD9, 0x9E, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xEA, 0x01, 0xB2, 0x3F, 0xC1, +0x02, 0xF4, 0x73, 0x8A, 0x8F, 0xDE, 0x9A, 0x8F, 0xD1, 0x9D, 0x65, 0x6B, 0x47, 0xAC, 0xFD, 0xA8, +0x8D, 0xEE, 0x86, 0xB5, 0x5B, 0xCC, 0x7E, 0x2C, 0x59, 0x7C, 0xF4, 0x06, 0xE4, 0x69, 0x4B, 0xDE, +0xEC, 0x60, 0x68, 0x8C, 0x5E, 0x9A, 0x29, 0xCA, 0x17, 0xE4, 0x67, 0xE2, 0x63, 0x21, 0xE2, 0x23, +0x0F, 0x90, 0x7D, 0xBB, 0x60, 0x01, 0x02, 0x04, 0x58, 0x71, 0x59, 0x73, 0xB3, 0x2A, 0xF1, 0x31, +0xB0, 0xDB, 0xD3, 0xD0, 0xC7, 0xF6, 0xD7, 0xA7, 0x97, 0x27, 0x3B, 0xD2, 0xE4, 0xC0, 0x76, 0x5C, +0xDA, 0xF8, 0xE8, 0x37, 0xE4, 0x8B, 0x71, 0x20, 0x2C, 0xCA, 0xF1, 0xD1, 0x0F, 0xB2, 0x4C, 0x7C, +0x9C, 0x7B, 0x7C, 0xE4, 0xEF, 0x6A, 0x86, 0xF8, 0x89, 0x1F, 0x6E, 0x38, 0xF1, 0x00, 0x02, 0x04, +0x58, 0x49, 0xF1, 0xC9, 0x47, 0x36, 0x7B, 0x5B, 0x83, 0x2E, 0x56, 0x7C, 0x1C, 0x35, 0xE8, 0x2C, +0x6F, 0xBB, 0x5B, 0x9E, 0x01, 0x29, 0x66, 0x3F, 0x86, 0xBF, 0xEC, 0x6E, 0x9C, 0xCA, 0xE0, 0x75, +0x91, 0xE3, 0x23, 0x6F, 0xC8, 0xED, 0xEE, 0xD6, 0xBC, 0xFD, 0x19, 0x8E, 0x70, 0x78, 0x21, 0xC6, +0xF2, 0x8B, 0xF8, 0x38, 0xE7, 0xF8, 0xC8, 0x7F, 0x58, 0xFB, 0xE9, 0x75, 0xDD, 0xD9, 0x07, 0x10, +0x20, 0xC0, 0xAA, 0xDA, 0x38, 0x7A, 0x0B, 0xDE, 0xF3, 0x8F, 0x8F, 0x91, 0xD9, 0x8F, 0x72, 0x78, +0x0C, 0x7C, 0x42, 0x6D, 0x70, 0xF6, 0x23, 0x1F, 0xE8, 0x75, 0x5E, 0x76, 0xB2, 0xA5, 0x8F, 0x8F, +0xE2, 0xA6, 0xD6, 0x73, 0xF1, 0xF0, 0x1A, 0x28, 0x9D, 0xD0, 0x28, 0x66, 0x3C, 0x06, 0xE2, 0x23, +0x13, 0x1F, 0xE7, 0x1A, 0x1F, 0xF9, 0xEF, 0xE5, 0x9E, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xD2, 0x1A, +0xA1, 0xB5, 0xBB, 0x78, 0xF1, 0x11, 0xC7, 0xDF, 0xE5, 0xC0, 0x16, 0xBB, 0xF1, 0xF0, 0xA2, 0x83, +0x61, 0xDC, 0x0E, 0xBB, 0xBB, 0xD9, 0x99, 0xBF, 0x8D, 0xAA, 0xC4, 0x47, 0x3E, 0xAE, 0xDD, 0xEF, +0xBE, 0x94, 0x9F, 0x76, 0x55, 0x04, 0x47, 0x7F, 0xE6, 0x23, 0x8A, 0x8F, 0x73, 0x8D, 0x8F, 0xFC, +0xC3, 0xF2, 0x05, 0x49, 0x37, 0x9C, 0x7A, 0x00, 0x01, 0x02, 0xAC, 0xAA, 0x6B, 0xA3, 0xD7, 0x00, +0x59, 0x8C, 0xA7, 0x5D, 0x1D, 0x35, 0xFB, 0x31, 0x6E, 0x07, 0xAC, 0xD1, 0xD9, 0x8F, 0x38, 0x74, +0xDD, 0x8F, 0xE5, 0x8E, 0x8F, 0xE2, 0xED, 0xF6, 0x0B, 0x45, 0x98, 0xD5, 0x3A, 0xD1, 0xD1, 0x9B, +0xF9, 0x28, 0x3D, 0x15, 0xAB, 0x76, 0xB7, 0xF8, 0x58, 0x88, 0xFB, 0x6D, 0x6F, 0xD5, 0x9D, 0x7A, +0x00, 0x01, 0x02, 0xAC, 0xA6, 0xE6, 0x0B, 0xF5, 0x85, 0x8B, 0x8F, 0xF4, 0x47, 0x6D, 0xF4, 0x6E, +0x63, 0x18, 0x5C, 0xE3, 0x71, 0xF4, 0xEC, 0x47, 0xE7, 0xD5, 0x56, 0x3C, 0xE3, 0x43, 0xA9, 0x5E, +0x7C, 0xA4, 0x83, 0x96, 0x9E, 0xDD, 0x93, 0x9E, 0x51, 0x57, 0xC4, 0x47, 0x2C, 0x3D, 0xED, 0x2A, +0xA4, 0xF8, 0xD8, 0x14, 0x1F, 0x0B, 0x71, 0xBF, 0xB1, 0x25, 0x40, 0x00, 0x01, 0x02, 0xAC, 0xA8, +0x98, 0x35, 0x16, 0x2A, 0x3E, 0x86, 0xFF, 0x3A, 0x66, 0xED, 0xC7, 0xB8, 0x4F, 0x8D, 0xE5, 0x0B, +0x80, 0xA4, 0x09, 0x9D, 0xBD, 0x78, 0x86, 0x87, 0x52, 0xCD, 0xF8, 0x28, 0x8E, 0x4F, 0xBE, 0x23, +0x56, 0x7A, 0xDA, 0x55, 0xBB, 0x37, 0x0B, 0xD2, 0x5B, 0xF3, 0x71, 0x45, 0x7C, 0x2C, 0xCE, 0xFD, +0xC6, 0x03, 0x3B, 0x61, 0x01, 0x02, 0x04, 0x58, 0xC1, 0xF6, 0x78, 0xF2, 0x91, 0x8D, 0xC3, 0x1D, +0xB0, 0x16, 0x24, 0x3E, 0x8E, 0x99, 0xFD, 0x88, 0x43, 0xC1, 0x51, 0xEC, 0xF2, 0x34, 0xF4, 0x81, +0xBD, 0x6D, 0x77, 0x57, 0x33, 0x3E, 0x92, 0x7C, 0x16, 0x64, 0xEB, 0x70, 0xF6, 0x43, 0x7C, 0x2C, +0xE0, 0xFD, 0xB6, 0x77, 0xD2, 0x6B, 0xD7, 0x03, 0x01, 0x04, 0x08, 0xB0, 0x72, 0x36, 0xBB, 0x3B, +0x60, 0x2D, 0xD6, 0x56, 0xBB, 0x47, 0xCD, 0x7E, 0x1C, 0x37, 0x03, 0x72, 0x38, 0xFA, 0x0E, 0xA7, +0x9E, 0xFD, 0x58, 0x86, 0xF8, 0x28, 0xC2, 0x2C, 0x35, 0x46, 0xB1, 0xE0, 0xFC, 0xCA, 0x5F, 0x12, +0x1F, 0x0B, 0x77, 0xBF, 0xDD, 0xF0, 0x77, 0x45, 0x74, 0x40, 0x80, 0x00, 0xAB, 0x18, 0x20, 0xFB, +0x8B, 0x13, 0x1F, 0x13, 0xCC, 0x7E, 0x94, 0x07, 0xD9, 0x87, 0x57, 0xFC, 0x2E, 0xAD, 0x1D, 0xD9, +0x12, 0x1F, 0xF9, 0x9F, 0xAD, 0x74, 0x59, 0x94, 0x97, 0x85, 0xBB, 0xBE, 0x43, 0x7C, 0x2C, 0xE4, +0xFD, 0xC6, 0xFC, 0xD2, 0xF5, 0xD7, 0x9D, 0x82, 0x00, 0x01, 0x02, 0xAC, 0x98, 0x78, 0xCC, 0x35, +0x40, 0xE6, 0x1C, 0x1F, 0xC3, 0x37, 0x4F, 0x32, 0xFB, 0x11, 0x87, 0xC2, 0x21, 0x8D, 0xE9, 0xF6, +0xE3, 0x29, 0x1E, 0xCA, 0x92, 0xC5, 0x47, 0xE7, 0x65, 0x6D, 0xE3, 0xDE, 0x70, 0xF5, 0x75, 0xE2, +0x63, 0xA1, 0xEF, 0xB7, 0x7D, 0xBB, 0xEE, 0x1C, 0x04, 0x08, 0x10, 0x60, 0xB5, 0xB4, 0xB6, 0xCE, +0x70, 0x2D, 0x82, 0xE9, 0x5F, 0xE1, 0xFC, 0xE4, 0xB3, 0x1F, 0xA5, 0x4A, 0x39, 0xE5, 0xDA, 0x8F, +0x65, 0x8D, 0x8F, 0x7B, 0xDF, 0xDA, 0x89, 0x8F, 0x4B, 0xE2, 0x63, 0xA1, 0xEF, 0x37, 0xB6, 0x3D, +0x05, 0x0B, 0x10, 0x20, 0xC0, 0x8A, 0x89, 0x59, 0x7D, 0x21, 0xE2, 0x63, 0xF8, 0x5D, 0x77, 0x9A, +0xFD, 0xE8, 0x8D, 0xB4, 0x07, 0xEE, 0x29, 0xCD, 0x7E, 0x1C, 0xC4, 0x13, 0x3E, 0x14, 0xF1, 0x21, +0x3E, 0xCE, 0xF1, 0x7E, 0xBB, 0x3B, 0x61, 0x89, 0x10, 0x40, 0x80, 0x00, 0x2B, 0xA4, 0xB5, 0x73, +0x8A, 0x00, 0x99, 0x4D, 0x7C, 0x4C, 0x3C, 0xFB, 0x91, 0x95, 0xB6, 0xDC, 0x3D, 0xC3, 0xEC, 0x87, +0xF8, 0x10, 0x1F, 0xE7, 0x7E, 0xBF, 0xED, 0xBD, 0xF4, 0xBA, 0x1E, 0x00, 0x04, 0x08, 0xB0, 0x0A, +0xE2, 0x93, 0x8D, 0xC6, 0xE1, 0x16, 0xBC, 0xE7, 0x1C, 0x1F, 0x71, 0xC2, 0xD9, 0x8F, 0x52, 0x78, +0x0C, 0xDC, 0x5B, 0x33, 0x9E, 0x68, 0xF6, 0x43, 0x7C, 0x88, 0x8F, 0xF3, 0xBF, 0xDF, 0x50, 0x2C, +0x44, 0x37, 0x03, 0x02, 0x08, 0x10, 0x60, 0x65, 0x6C, 0x9C, 0x2C, 0x40, 0x66, 0x14, 0x1F, 0xBD, +0xB1, 0xF5, 0x48, 0x90, 0x0C, 0xDD, 0x58, 0xCC, 0x7C, 0xC4, 0xE1, 0x87, 0x73, 0xC2, 0x9D, 0xAF, +0xC4, 0x87, 0xF8, 0x58, 0x88, 0xF8, 0x48, 0xB2, 0x7C, 0x06, 0xC4, 0x4E, 0x58, 0x80, 0x00, 0x01, +0x56, 0xC6, 0x66, 0x68, 0x4D, 0xBA, 0x03, 0xD6, 0x0C, 0xE3, 0x23, 0x1E, 0x7F, 0xD5, 0xF3, 0xC3, +0xAB, 0x9C, 0xC7, 0xF1, 0xEB, 0x41, 0xF6, 0x7A, 0x57, 0x3E, 0x17, 0x1F, 0xE2, 0xA3, 0x4A, 0xF1, +0x51, 0x68, 0xEF, 0xB8, 0x18, 0x21, 0x20, 0x40, 0x80, 0x15, 0x91, 0xB5, 0xAE, 0x87, 0x38, 0xC9, +0xC8, 0x7D, 0x86, 0xF1, 0x71, 0x54, 0x90, 0x0C, 0xAD, 0xFD, 0xE8, 0x2E, 0x38, 0xAF, 0x8D, 0x3E, +0x92, 0x74, 0xC3, 0x4E, 0x9C, 0xF0, 0xA1, 0x88, 0x0F, 0xF1, 0xB1, 0x60, 0xF1, 0xD1, 0x2D, 0x90, +0x86, 0x93, 0x11, 0x20, 0x40, 0x80, 0xD5, 0xD0, 0xDA, 0x9A, 0xE0, 0xBF, 0xBC, 0xCE, 0x76, 0xE6, +0xA3, 0x3F, 0xE3, 0x31, 0xB0, 0xAE, 0xA3, 0x36, 0x30, 0xD3, 0x11, 0x4B, 0x33, 0x1F, 0x23, 0x77, +0x3B, 0xE1, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0x8B, 0x19, 0x1F, 0x31, 0x7F, 0x1A, 0x56, 0xFC, 0xC4, +0x0F, 0xD7, 0x9D, 0x90, 0x00, 0x01, 0x02, 0x2C, 0xBF, 0xEC, 0xA0, 0x71, 0x6E, 0xF1, 0x31, 0xAC, +0x76, 0x18, 0x22, 0xC3, 0x33, 0x1F, 0xA1, 0xDC, 0x1F, 0xE5, 0x77, 0x66, 0x61, 0xA2, 0xD9, 0x0F, +0xF1, 0x21, 0x3E, 0x16, 0x36, 0x3E, 0x92, 0xEE, 0x85, 0x40, 0x05, 0x08, 0x20, 0x40, 0x80, 0xE5, +0x16, 0x9F, 0x6C, 0xD4, 0x8F, 0x7F, 0xFA, 0xD5, 0x8C, 0xE3, 0x23, 0x0E, 0x7D, 0x95, 0xFE, 0xDF, +0x6B, 0x03, 0x33, 0x1F, 0x87, 0xB3, 0x23, 0xB5, 0xD1, 0xDD, 0xB0, 0x76, 0xEF, 0x3C, 0xFB, 0x21, +0x3E, 0xC4, 0xC7, 0x42, 0xC7, 0x47, 0xBF, 0xA4, 0x43, 0xC3, 0x59, 0x09, 0x10, 0x20, 0xC0, 0xB2, +0xAB, 0x87, 0xD6, 0xD6, 0xC2, 0xC4, 0xC7, 0xC8, 0xD3, 0xB0, 0xCA, 0x0B, 0xCE, 0x43, 0x69, 0x21, +0x7A, 0xF9, 0x3E, 0xEE, 0x30, 0xFB, 0x21, 0x3E, 0xC4, 0xC7, 0xE2, 0xC7, 0x47, 0x47, 0x7B, 0x3B, +0xBD, 0xBE, 0xE6, 0x94, 0x04, 0x08, 0x10, 0x60, 0xD9, 0x35, 0x42, 0xBB, 0x39, 0xFF, 0xF8, 0x38, +0x66, 0x4C, 0xD6, 0xDF, 0x76, 0xB7, 0x37, 0xF3, 0x71, 0xB8, 0xFB, 0x55, 0x6D, 0xF4, 0x21, 0xEE, +0xC6, 0x63, 0x1F, 0xAA, 0xF8, 0x10, 0x1F, 0x95, 0x88, 0x8F, 0x42, 0xB6, 0x5F, 0x77, 0x4A, 0x02, +0xE6, 0x6D, 0xDD, 0x21, 0x00, 0xE6, 0xEC, 0xBE, 0xD1, 0x6B, 0x80, 0xCC, 0x2F, 0x3E, 0x46, 0x66, +0x3F, 0x8A, 0xF0, 0x88, 0xA5, 0xAB, 0x9C, 0x87, 0xEE, 0xA0, 0xFC, 0xA4, 0xB3, 0x1F, 0xE2, 0x43, +0x7C, 0x54, 0x2A, 0x3E, 0xF2, 0x77, 0xB5, 0x1A, 0x4E, 0x49, 0xC0, 0xBC, 0x99, 0x01, 0x01, 0xE6, +0xAB, 0xB5, 0xB5, 0x79, 0x2E, 0xF1, 0x11, 0xC7, 0xDF, 0x16, 0x87, 0xDE, 0x55, 0x0C, 0xC0, 0xC7, +0x7E, 0xFC, 0xF6, 0xD1, 0xB3, 0x1F, 0x8B, 0x1C, 0x1F, 0x51, 0x7C, 0x88, 0x8F, 0xA3, 0x64, 0x3B, +0x69, 0x27, 0x2C, 0xD7, 0x03, 0x01, 0x04, 0x08, 0xB0, 0xC4, 0xB2, 0xD6, 0xE6, 0xDC, 0xE3, 0x63, +0xF8, 0xAB, 0x0D, 0x5F, 0xD5, 0x7C, 0x64, 0xF6, 0x63, 0xCC, 0xDA, 0x8F, 0xB4, 0xE8, 0x7C, 0x37, +0x56, 0x32, 0x3E, 0x6A, 0xC7, 0xC4, 0xC7, 0xC0, 0x6D, 0xE2, 0x63, 0xB5, 0xE2, 0x23, 0xFF, 0xF7, +0x98, 0xCF, 0x46, 0x6E, 0x3A, 0x31, 0x01, 0x02, 0x04, 0x58, 0x5E, 0xED, 0xBD, 0x8D, 0xB9, 0xC7, +0x47, 0x31, 0x0A, 0x1F, 0xBA, 0x2D, 0x8E, 0xB9, 0xDB, 0x63, 0x67, 0x3F, 0x2A, 0x16, 0x1F, 0xE1, +0xC8, 0xF8, 0x08, 0x03, 0x8B, 0xEC, 0x8B, 0xDD, 0xBE, 0x32, 0xF1, 0xB1, 0x5A, 0xF1, 0x91, 0x7F, +0xD8, 0x81, 0x00, 0x01, 0x04, 0x08, 0xB0, 0xBC, 0xE2, 0x93, 0x8D, 0x46, 0xF7, 0xDA, 0x03, 0xF3, +0x5F, 0x70, 0x3E, 0x3C, 0xF0, 0x2E, 0xDE, 0x38, 0x1C, 0x80, 0x0F, 0x87, 0x48, 0x39, 0x9A, 0x42, +0xF7, 0xC2, 0x83, 0x15, 0x8B, 0x8F, 0x91, 0x9B, 0xFA, 0xC7, 0xA0, 0x76, 0x78, 0x2C, 0x86, 0x9E, +0x76, 0x75, 0x41, 0x7C, 0xAC, 0x4E, 0x7C, 0xE4, 0xBF, 0xDB, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xB0, +0xD4, 0x05, 0x12, 0xEB, 0x21, 0x6B, 0xCD, 0x77, 0xD0, 0x39, 0x76, 0xF6, 0x23, 0xE6, 0x3B, 0x5F, +0x95, 0x2F, 0xED, 0x51, 0x1E, 0x88, 0x8F, 0x7C, 0xFE, 0x76, 0xF5, 0xE3, 0x23, 0xF6, 0xB7, 0x19, +0xAE, 0x1D, 0xBE, 0xAF, 0xF4, 0xB4, 0xAB, 0xAC, 0x17, 0x1F, 0x2F, 0x13, 0x1F, 0xAB, 0x13, 0x1F, +0xFD, 0x4F, 0x69, 0x99, 0x01, 0x01, 0x04, 0x08, 0xB0, 0xB4, 0x8E, 0xB9, 0x06, 0xC8, 0xEC, 0x06, +0x9D, 0x83, 0xB3, 0x1F, 0xA5, 0x8B, 0x81, 0x8C, 0x59, 0xFB, 0x31, 0x32, 0x84, 0x4B, 0xBD, 0x34, +0x34, 0xFB, 0x51, 0x85, 0xF8, 0x18, 0xFF, 0x71, 0x83, 0xF1, 0x31, 0x3C, 0xF3, 0x21, 0x3E, 0x56, +0x30, 0x3E, 0x92, 0x6C, 0x5F, 0x80, 0x00, 0x02, 0x04, 0x58, 0x52, 0xED, 0xBD, 0x1B, 0x73, 0x1D, +0x74, 0x8E, 0xCC, 0x7E, 0xC4, 0xFE, 0x75, 0x3F, 0x06, 0x16, 0xA4, 0x97, 0x67, 0x3F, 0xCA, 0x41, +0x92, 0xFE, 0xDC, 0xAA, 0x66, 0x7C, 0x8C, 0x5D, 0x74, 0x5E, 0xBA, 0xBD, 0x7C, 0xBD, 0x93, 0x35, +0xF1, 0xB1, 0xBA, 0xF1, 0x91, 0x07, 0xC8, 0xDE, 0x86, 0x9D, 0xB0, 0x00, 0x01, 0x02, 0x2C, 0xA7, +0x6C, 0xFF, 0xE4, 0x83, 0x9C, 0x33, 0x0E, 0x3A, 0x63, 0x69, 0x0F, 0xDA, 0xB1, 0x3B, 0x5F, 0x1D, +0x37, 0x8C, 0x4B, 0xEB, 0x73, 0x0F, 0xE2, 0x72, 0xC4, 0x47, 0x2C, 0xCF, 0x7C, 0x1C, 0x06, 0x97, +0xF8, 0x58, 0xF1, 0xF8, 0xC8, 0x3F, 0xDD, 0x42, 0x74, 0x60, 0xBE, 0x5C, 0x88, 0x10, 0x98, 0x63, +0x80, 0x34, 0x4F, 0x36, 0xC8, 0x39, 0xEB, 0xA0, 0xB3, 0x56, 0x8C, 0xCD, 0x0E, 0xFF, 0xB3, 0xFF, +0x89, 0x66, 0x3F, 0xB6, 0x97, 0x34, 0x3E, 0x7A, 0x17, 0x59, 0x5C, 0x7B, 0x79, 0x27, 0x3E, 0xDE, +0x22, 0x3E, 0x56, 0x3A, 0x3E, 0xF2, 0x7F, 0x97, 0x7B, 0xE9, 0x75, 0xDD, 0x09, 0x0A, 0x98, 0x17, +0x33, 0x20, 0xC0, 0x7C, 0x86, 0x49, 0xB7, 0x6E, 0x6C, 0x86, 0x76, 0x73, 0x7E, 0x83, 0xCE, 0xFE, +0x0C, 0x47, 0x3C, 0xFA, 0xBA, 0x1F, 0xC3, 0xB7, 0x95, 0x3F, 0xBF, 0x34, 0xFB, 0x51, 0xF9, 0xF8, +0x18, 0xF8, 0x36, 0x4B, 0x33, 0x1F, 0xE2, 0x43, 0x7C, 0x08, 0x10, 0x40, 0x80, 0x00, 0x4B, 0x6C, +0xA3, 0xBB, 0x05, 0xEF, 0x9C, 0x06, 0x9D, 0xB5, 0xA1, 0x01, 0x5A, 0xEF, 0xBF, 0xFC, 0x0F, 0x07, +0x4A, 0x3E, 0xFB, 0x31, 0x3C, 0x9E, 0xCB, 0xD7, 0x7E, 0x64, 0x95, 0x8D, 0x8F, 0x30, 0x1C, 0x1F, +0xE3, 0x9E, 0x76, 0x25, 0x3E, 0xC4, 0x47, 0xFF, 0xAE, 0xDA, 0x69, 0x27, 0xAC, 0x1B, 0x01, 0x60, +0x4E, 0x3C, 0x05, 0x0B, 0x98, 0x97, 0x46, 0xEF, 0xAA, 0xCB, 0xB3, 0x1F, 0x74, 0x0E, 0xED, 0x76, +0x35, 0xFA, 0x94, 0xAB, 0xD2, 0x33, 0xB3, 0xC6, 0xCD, 0x88, 0xA4, 0x5D, 0xAF, 0x5A, 0xD5, 0x8D, +0x8F, 0x28, 0x3E, 0xC4, 0xC7, 0x49, 0x65, 0x7B, 0x75, 0xA7, 0x28, 0x40, 0x80, 0x00, 0xCB, 0xE6, +0xDA, 0x1D, 0x03, 0x64, 0x56, 0x03, 0xFE, 0x31, 0x6B, 0x3F, 0x46, 0x6E, 0x2B, 0xBF, 0x6F, 0x27, +0x2E, 0xC7, 0xD3, 0xAE, 0xC4, 0x87, 0xF8, 0x98, 0x38, 0x40, 0xF6, 0x05, 0x08, 0x30, 0x37, 0x9E, +0x82, 0x05, 0xCC, 0x47, 0xF3, 0x85, 0xFA, 0xCC, 0x07, 0x9D, 0xBD, 0xBD, 0x65, 0xE3, 0x40, 0x64, +0x1C, 0x0E, 0xCA, 0xC3, 0xB8, 0xD9, 0x8F, 0x30, 0x3A, 0xFB, 0x11, 0x5B, 0xCB, 0xB7, 0xDB, 0x55, +0x58, 0x5F, 0x0F, 0x57, 0x1B, 0xFF, 0x51, 0x2F, 0x3E, 0xBE, 0x2C, 0x3E, 0xC4, 0xC7, 0x70, 0x81, +0x84, 0xF8, 0x89, 0x1F, 0x6E, 0x38, 0x51, 0x01, 0x02, 0x04, 0x58, 0x1E, 0xB1, 0xBD, 0x39, 0xFB, +0x41, 0x67, 0xE9, 0x7E, 0x6A, 0xA5, 0x59, 0x80, 0x81, 0x40, 0x39, 0xBC, 0x2D, 0x0E, 0x8F, 0xE9, +0xD2, 0xED, 0xDB, 0x4B, 0x10, 0x1F, 0x61, 0x34, 0x3E, 0xD2, 0xCC, 0xC7, 0xDA, 0xD5, 0xAB, 0xDD, +0xF0, 0x68, 0x3D, 0x2F, 0x3E, 0xC4, 0xC7, 0xA0, 0xF6, 0x76, 0x7A, 0x5D, 0x77, 0xA2, 0x02, 0x04, +0x08, 0xB0, 0x1C, 0xED, 0x71, 0xEB, 0xC6, 0x46, 0x88, 0x07, 0x1B, 0x33, 0x1D, 0x74, 0xF6, 0xEE, +0x67, 0x78, 0x41, 0x79, 0x2C, 0x0D, 0xCC, 0x07, 0xA2, 0x23, 0x0E, 0x0E, 0xD8, 0xF3, 0xB7, 0x77, +0x3A, 0xAF, 0xDA, 0xD3, 0x78, 0x3C, 0x8B, 0x17, 0x1F, 0x17, 0xEE, 0xBF, 0xAF, 0x1B, 0x1F, 0xE9, +0xA9, 0x57, 0xE2, 0x43, 0x7C, 0x0C, 0xEB, 0x3E, 0x3D, 0x52, 0x80, 0x00, 0x02, 0x04, 0x58, 0x1A, +0x9B, 0xA1, 0xB5, 0x3B, 0xD7, 0xF8, 0x18, 0x79, 0x1A, 0xD6, 0xF0, 0xEC, 0xC7, 0xD0, 0xC2, 0xF3, +0x98, 0x75, 0xFE, 0xB2, 0x9D, 0x89, 0x0F, 0xF1, 0xB1, 0x7A, 0xF1, 0x91, 0x7F, 0x99, 0xFC, 0x62, +0x84, 0x76, 0xC2, 0x02, 0x04, 0x08, 0xB0, 0x44, 0x01, 0x32, 0xBC, 0x00, 0x7D, 0xD6, 0x8B, 0xBC, +0xCB, 0xDB, 0xEE, 0x0E, 0x47, 0x47, 0xAC, 0x0D, 0x3D, 0x94, 0xCE, 0x8D, 0xBB, 0x71, 0x0A, 0x63, +0x3D, 0xF1, 0x21, 0x3E, 0xAA, 0xFA, 0xB5, 0x3A, 0x5A, 0x2F, 0x6D, 0x38, 0x55, 0x01, 0x02, 0x04, +0x58, 0x16, 0x83, 0xD7, 0x00, 0x99, 0x41, 0x7C, 0x8C, 0xCC, 0x7E, 0x14, 0x6F, 0x97, 0x16, 0x65, +0x77, 0xD5, 0x06, 0x66, 0x3F, 0xF2, 0xF8, 0x48, 0x13, 0x1F, 0x3B, 0x71, 0x6A, 0x8F, 0x45, 0x7C, +0x88, 0x8F, 0xCA, 0xC5, 0x47, 0xFE, 0x25, 0x8F, 0x59, 0xA7, 0x05, 0x20, 0x40, 0x80, 0x4A, 0x69, +0xEF, 0xDD, 0x98, 0x59, 0x7C, 0xC4, 0xF1, 0xEF, 0x1A, 0xDE, 0x62, 0xB7, 0x18, 0xA0, 0x0F, 0x7E, +0x7C, 0xEF, 0x2F, 0x67, 0x9E, 0xFD, 0x10, 0x1F, 0xE2, 0xA3, 0xE2, 0xF1, 0x91, 0x7F, 0xD9, 0x83, +0x10, 0x3F, 0xFE, 0x43, 0x22, 0x04, 0x10, 0x20, 0xC0, 0x12, 0xC8, 0x9A, 0xF5, 0x99, 0xC4, 0xC7, +0xF0, 0xDF, 0xC6, 0xEC, 0x78, 0x35, 0x38, 0xFB, 0x51, 0x5E, 0xFB, 0x11, 0xBB, 0x7F, 0xB6, 0xC3, +0x19, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x58, 0x82, 0xF8, 0xC8, 0xFF, 0x9D, 0xEE, 0xA4, 0xD7, 0x75, +0x27, 0x2C, 0x40, 0x80, 0x00, 0xD5, 0xD7, 0xDA, 0xA9, 0xCF, 0x24, 0x3E, 0x62, 0x77, 0x3C, 0x3E, +0xFC, 0xEE, 0x18, 0x6A, 0x23, 0xE3, 0xDD, 0xC1, 0xD9, 0x8F, 0x78, 0xF8, 0x70, 0xB6, 0xCF, 0x32, +0xFB, 0x21, 0x3E, 0xC4, 0xC7, 0x92, 0xC4, 0x47, 0xFE, 0xE5, 0x53, 0x8D, 0x07, 0x33, 0x20, 0x80, +0x00, 0x01, 0xAA, 0x2D, 0xDE, 0xBA, 0xB1, 0x19, 0xB2, 0xFD, 0x99, 0x0D, 0xD0, 0x06, 0xB6, 0xD1, +0x2D, 0x47, 0x48, 0x69, 0xD7, 0xAB, 0xC1, 0x10, 0x29, 0xC5, 0x47, 0x1A, 0x6F, 0xED, 0xC5, 0xA9, +0x3D, 0x16, 0xF1, 0x21, 0x3E, 0x2A, 0x1B, 0x1F, 0x49, 0xB6, 0x97, 0x5E, 0x5F, 0x77, 0xD6, 0x02, +0x04, 0x08, 0x50, 0xF5, 0x04, 0xA9, 0x8F, 0xEC, 0x80, 0x35, 0x8D, 0x01, 0xDA, 0x31, 0xB3, 0x1F, +0xA5, 0x26, 0x19, 0x1C, 0xA8, 0x0F, 0xCF, 0x9E, 0x6C, 0x8B, 0x0F, 0xF1, 0x21, 0x3E, 0x06, 0x23, +0x64, 0xA7, 0xEE, 0x9C, 0x05, 0x08, 0x10, 0xA0, 0xEA, 0xC6, 0x5F, 0x03, 0x64, 0x0A, 0x03, 0xB4, +0xA3, 0x66, 0x3F, 0xC6, 0xAD, 0xFD, 0xC8, 0x9F, 0x76, 0x55, 0xBE, 0xAB, 0xB4, 0xF3, 0xD5, 0xA9, +0x66, 0x3F, 0xC4, 0x87, 0xF8, 0x58, 0xD2, 0xF8, 0xC8, 0x1F, 0x8E, 0x9D, 0xB0, 0x00, 0x01, 0x02, +0x54, 0x5D, 0xD6, 0xBA, 0xDE, 0x7B, 0x6E, 0xF9, 0xF4, 0x06, 0x68, 0x13, 0xCC, 0x7E, 0x1C, 0x6E, +0xC1, 0x5B, 0xEB, 0x6E, 0xB5, 0x3B, 0x7C, 0x51, 0xC2, 0x97, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0xA3, +0xFF, 0x5E, 0xF7, 0xD2, 0x4E, 0x58, 0x75, 0x27, 0x2E, 0x40, 0x80, 0x00, 0xD5, 0xD5, 0xDA, 0x3A, +0xC3, 0xC5, 0xCD, 0xE2, 0x9D, 0xC7, 0xB5, 0x47, 0xCD, 0x7E, 0x0C, 0xDD, 0xC7, 0xC0, 0x4D, 0xE9, +0xA2, 0xCF, 0x07, 0x71, 0x6A, 0x8F, 0x45, 0x7C, 0x88, 0x8F, 0xA5, 0x88, 0x8F, 0x5E, 0x80, 0x04, +0x3B, 0x61, 0x01, 0x02, 0x04, 0xA8, 0xB4, 0x98, 0x35, 0xA6, 0x3E, 0x40, 0x9B, 0x78, 0xF6, 0x63, +0x70, 0x31, 0x7A, 0xFF, 0xED, 0x6D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xF8, 0x87, 0x96, 0xEA, 0x3C, +0x34, 0x02, 0x80, 0x00, 0x01, 0x2A, 0xD9, 0x1E, 0xB7, 0x5E, 0x7F, 0xCA, 0x05, 0xE8, 0xF1, 0xD8, +0x77, 0x4D, 0x36, 0xFB, 0x31, 0xF4, 0xB4, 0xAB, 0xC2, 0x89, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x58, +0x91, 0xF8, 0x48, 0xBA, 0x33, 0x20, 0xD7, 0x9C, 0xBD, 0x00, 0x01, 0x02, 0x54, 0x55, 0x3D, 0xB4, +0xB6, 0xA6, 0x3B, 0x40, 0xBB, 0xD3, 0x75, 0x3F, 0xFA, 0x5B, 0xED, 0xD6, 0xC6, 0x6F, 0xCB, 0xBB, +0x2D, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0x3E, 0x42, 0xEC, 0x84, 0x05, 0xCC, 0xD6, 0xBA, 0x43, 0x00, +0xCC, 0xD0, 0x66, 0x68, 0x9F, 0x64, 0x06, 0x24, 0xDE, 0xF1, 0xDD, 0x47, 0x5D, 0xF5, 0xBC, 0xFF, +0x94, 0xAB, 0xD2, 0xCC, 0x47, 0xF9, 0xF6, 0x5C, 0x33, 0x9E, 0x60, 0xF6, 0x43, 0x7C, 0x88, 0x8F, +0x15, 0x8C, 0x8F, 0xAE, 0x86, 0x53, 0x17, 0x30, 0x4B, 0x66, 0x40, 0x80, 0x59, 0xBA, 0x36, 0xF9, +0x53, 0xB0, 0x4E, 0x31, 0x40, 0x1B, 0x59, 0xFB, 0x11, 0x47, 0x6F, 0x2B, 0xDF, 0xFD, 0x96, 0xF8, +0x10, 0x1F, 0xE2, 0xE3, 0x8E, 0xDA, 0xDB, 0x76, 0xC2, 0x02, 0x04, 0x08, 0x50, 0x51, 0xAD, 0xAD, +0x09, 0xAF, 0x29, 0x70, 0xE7, 0x99, 0x8F, 0x81, 0x2D, 0x74, 0xFB, 0xB3, 0x1F, 0xB5, 0xD2, 0x4C, +0x47, 0x1C, 0x78, 0xFF, 0xE1, 0x40, 0xBE, 0x77, 0x1F, 0xE9, 0x9A, 0x1F, 0xED, 0x29, 0x3C, 0x16, +0xF1, 0x21, 0x3E, 0x96, 0x39, 0x3E, 0xF2, 0x87, 0x9C, 0x2F, 0x44, 0x17, 0x20, 0x80, 0x00, 0x01, +0x2A, 0x28, 0x6B, 0x4D, 0x10, 0x20, 0x27, 0x1C, 0xA0, 0xD5, 0x0E, 0x43, 0x64, 0x78, 0xE6, 0xA3, +0xFC, 0xEC, 0xAB, 0x81, 0xBB, 0x4D, 0x6F, 0xEF, 0x4C, 0xF2, 0x75, 0xC4, 0x87, 0xF8, 0x58, 0xF1, +0xF8, 0x38, 0x0C, 0x10, 0x17, 0x24, 0x04, 0x04, 0x08, 0x50, 0x41, 0xED, 0xBD, 0x8D, 0x33, 0x0F, +0xD0, 0xE2, 0xD0, 0x47, 0xF6, 0xFF, 0x5E, 0x1B, 0x98, 0xF9, 0x38, 0x9C, 0xFD, 0x38, 0x1C, 0xD4, +0xF7, 0x3F, 0x67, 0xA2, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0xF7, 0xDF, 0xED, 0x76, 0x7A, +0x6D, 0x27, 0x2C, 0x40, 0x80, 0x00, 0xD5, 0x12, 0x6F, 0xBD, 0xBE, 0x71, 0xFC, 0xFA, 0x8F, 0xD3, +0xC5, 0xC7, 0xE0, 0xD3, 0xB0, 0x86, 0x16, 0x9C, 0x87, 0xA1, 0xA7, 0x5D, 0xA5, 0x3F, 0xB3, 0x30, +0xC1, 0xDA, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x80, 0x6C, 0xDF, 0x0C, 0x08, 0x20, 0x40, 0x80, +0xCA, 0x39, 0xE6, 0x1A, 0x20, 0x67, 0x18, 0xA0, 0xF5, 0xB7, 0xDD, 0x8D, 0xA3, 0xD1, 0x11, 0x6B, +0xA3, 0xE3, 0xE3, 0xDD, 0x78, 0x87, 0x2F, 0x27, 0x3E, 0xC4, 0x87, 0xF8, 0x18, 0xFD, 0x36, 0x5A, +0x02, 0x04, 0x10, 0x20, 0x40, 0x05, 0x03, 0xE4, 0x60, 0xEB, 0xCC, 0x03, 0xB4, 0xF1, 0xDB, 0xEE, +0xC6, 0xC1, 0xAB, 0x9C, 0x87, 0xEE, 0xC0, 0x7E, 0x60, 0xF6, 0x23, 0x49, 0xB3, 0x1F, 0xC7, 0xAE, +0xFD, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x8C, 0x95, 0xED, 0x6C, 0xC4, 0x8F, 0xFF, 0xD0, 0x86, 0xD3, +0x18, 0x20, 0x40, 0x80, 0xEA, 0x68, 0xEF, 0x5E, 0x3F, 0xD3, 0x00, 0x2D, 0x8E, 0xBF, 0x2D, 0x0E, +0xBD, 0xAB, 0x18, 0xC4, 0x87, 0x38, 0x66, 0x8C, 0x7C, 0xEC, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0xE2, +0xE3, 0xE8, 0x6F, 0x27, 0x5F, 0x34, 0x65, 0x16, 0x04, 0x10, 0x20, 0x40, 0x95, 0x02, 0xA4, 0x59, +0x3F, 0xEB, 0x00, 0x6D, 0x74, 0xF6, 0x23, 0x0E, 0x5C, 0x60, 0x70, 0x38, 0x3A, 0x26, 0x9F, 0xFD, +0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x1C, 0xFF, 0x2D, 0xED, 0x0B, 0x10, 0x40, 0x80, 0x00, 0x15, 0x93, +0x35, 0x37, 0x4F, 0x3D, 0x40, 0x8B, 0xDD, 0x31, 0xFB, 0xF0, 0x00, 0x38, 0x0E, 0xDD, 0x78, 0xEC, +0xEC, 0xC7, 0xF6, 0x51, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x3B, 0xFF, 0xFB, 0xDD, 0x4B, +0xAF, 0xED, 0x84, 0x05, 0x08, 0x10, 0xA0, 0x1A, 0xE2, 0xAD, 0xD7, 0x6F, 0x1E, 0x2E, 0x40, 0x3F, +0xDD, 0x00, 0xAD, 0xBC, 0x93, 0x55, 0x2C, 0x6D, 0x87, 0x15, 0x4B, 0xBB, 0x5E, 0x0D, 0x86, 0x48, +0xE9, 0x73, 0xD2, 0xB3, 0x47, 0x76, 0xA3, 0xF8, 0x10, 0x1F, 0xE2, 0xE3, 0xD4, 0xDF, 0x5A, 0x3B, +0xBD, 0x98, 0x01, 0x01, 0x04, 0x08, 0x50, 0x19, 0x1B, 0xA1, 0xBD, 0x7B, 0xBA, 0x01, 0xDA, 0xC8, +0xEC, 0x47, 0xEC, 0xEF, 0x7C, 0x55, 0xDE, 0x5D, 0x77, 0x60, 0x30, 0x3F, 0xFC, 0xF9, 0xDB, 0xE2, +0x43, 0x7C, 0x88, 0x8F, 0x33, 0xCB, 0x76, 0xEB, 0x4E, 0x65, 0x80, 0x00, 0x01, 0x2A, 0x22, 0x36, +0x42, 0x7B, 0xFF, 0xEC, 0xE3, 0xDE, 0x18, 0x07, 0x2F, 0x40, 0x38, 0x66, 0xED, 0x47, 0x7F, 0x28, +0x58, 0xDC, 0x96, 0xD6, 0x7E, 0xEC, 0x45, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x99, 0xBF, 0xCD, 0x7D, +0x01, 0x02, 0x08, 0x10, 0xA0, 0x32, 0xAE, 0x1D, 0x7F, 0x11, 0xC2, 0x63, 0xC6, 0x75, 0xB5, 0xA1, +0x41, 0xDE, 0xD0, 0xEC, 0x47, 0xFF, 0x2A, 0xE7, 0xC5, 0x80, 0xBE, 0x74, 0x11, 0xC2, 0xF1, 0xB3, +0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x53, 0xE9, 0xFC, 0x1B, 0x8E, 0x1F, 0x7F, 0x7B, 0xC3, 0xE9, +0x0C, 0x10, 0x20, 0xC0, 0xE2, 0x6B, 0xBE, 0x50, 0x3F, 0xDB, 0xD8, 0x37, 0x0E, 0x5D, 0xF7, 0x23, +0x0C, 0x0C, 0xEA, 0x8F, 0x1C, 0x0E, 0x1E, 0x84, 0xA1, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, +0xA9, 0x7F, 0x0F, 0xB2, 0x7C, 0x16, 0xB3, 0x1E, 0x00, 0x04, 0x08, 0xB0, 0xF8, 0x83, 0x97, 0x53, +0x2C, 0x5E, 0xED, 0xCF, 0x7E, 0xC4, 0x81, 0xDB, 0x26, 0x9A, 0xFD, 0x28, 0xDE, 0xDE, 0x16, 0x1F, +0xE2, 0x43, 0x7C, 0x4C, 0xED, 0xF7, 0xA0, 0x3B, 0x8B, 0x29, 0x40, 0x00, 0x01, 0x02, 0x2C, 0xF8, +0xD8, 0xE5, 0xD6, 0xC3, 0x1B, 0x21, 0x3B, 0x38, 0xD5, 0x15, 0x94, 0x63, 0x2C, 0x3F, 0xED, 0xAA, +0x34, 0xEE, 0x9B, 0x74, 0xF6, 0xE3, 0x20, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0xA6, 0xF5, 0x7B, 0x10, +0xD3, 0x3F, 0xAA, 0x70, 0xC3, 0x59, 0x0D, 0x10, 0x20, 0xC0, 0xA2, 0xDB, 0xEC, 0xEE, 0x80, 0x75, +0xC6, 0x41, 0xDE, 0xA9, 0x67, 0x3F, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x53, 0xFB, 0x3D, 0x68, 0x6F, +0xD5, 0x9D, 0xD2, 0x00, 0x01, 0x02, 0x54, 0x20, 0x40, 0x4E, 0xB8, 0x00, 0x7D, 0x68, 0xB7, 0xAB, +0x63, 0x67, 0x3F, 0xE2, 0xD0, 0xC7, 0x24, 0xFD, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x54, +0x7F, 0x0F, 0x62, 0x4B, 0x80, 0x00, 0x02, 0x04, 0x58, 0x78, 0x1B, 0x27, 0x9A, 0x01, 0x39, 0xE2, +0xB9, 0x55, 0x03, 0x57, 0x3D, 0x2F, 0xCF, 0x7E, 0x0C, 0xDD, 0x96, 0xDF, 0x70, 0x3B, 0x13, 0x1F, +0xE2, 0x43, 0x7C, 0xCC, 0xE2, 0xF7, 0x20, 0x1E, 0xA4, 0x9D, 0xB0, 0x5C, 0x90, 0x10, 0x10, 0x20, +0xC0, 0x02, 0x6B, 0xEF, 0x4D, 0xFE, 0x9C, 0xF1, 0xDE, 0xC0, 0xA7, 0xBF, 0x85, 0xEE, 0xD0, 0xEC, +0x47, 0xFF, 0x0A, 0xE7, 0xA5, 0xF7, 0x87, 0xE1, 0xAB, 0x9E, 0xA7, 0x5D, 0xAF, 0xDA, 0xE2, 0x43, +0x7C, 0x88, 0x8F, 0x99, 0xFC, 0x1E, 0xB4, 0x77, 0xD2, 0xEB, 0xBA, 0x13, 0x1B, 0x20, 0x40, 0x80, +0xC5, 0x95, 0x35, 0x27, 0x1B, 0xAC, 0x0C, 0x0F, 0x7C, 0x6A, 0xA5, 0xF0, 0x18, 0x5A, 0xFB, 0x31, +0x72, 0x5B, 0xF9, 0x7D, 0x3B, 0x99, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0x56, 0xBF, 0x07, 0xDD, 0x9D, +0xB0, 0xCC, 0x80, 0x00, 0x53, 0xB5, 0xEE, 0x10, 0x00, 0x53, 0xD5, 0xDA, 0xB9, 0x73, 0x80, 0x94, +0x06, 0x3E, 0xE5, 0xAB, 0x98, 0xC7, 0x70, 0x38, 0xC0, 0x0F, 0xE3, 0x66, 0x3F, 0xC2, 0xF0, 0xEC, +0x47, 0x27, 0x3E, 0xDA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x99, 0xFD, 0x1E, 0x74, 0x77, 0xC2, 0xBA, +0xEE, 0xC4, 0x06, 0x4C, 0x93, 0x19, 0x10, 0x60, 0x7A, 0x63, 0x9A, 0x5B, 0x0F, 0x6F, 0xDE, 0xF1, +0x0A, 0xE8, 0xE3, 0x06, 0x3E, 0xB5, 0xC3, 0xD0, 0x18, 0x9E, 0xF9, 0x28, 0x6E, 0x8B, 0xC3, 0xE3, +0xC3, 0x74, 0x3F, 0x3B, 0x53, 0x18, 0x30, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0x8E, 0xD7, 0xBE, 0x5D, +0x0F, 0x00, 0x02, 0x04, 0x58, 0x50, 0xF5, 0x63, 0x03, 0x64, 0x68, 0xE0, 0x13, 0x87, 0xDE, 0x88, +0xA5, 0x41, 0xFE, 0x40, 0x74, 0xC4, 0xC1, 0xDB, 0xF3, 0xD7, 0x29, 0x3E, 0xCE, 0x3A, 0xFB, 0x21, +0x3E, 0xC4, 0x87, 0xF8, 0x98, 0x84, 0xA7, 0x60, 0x01, 0x02, 0x04, 0x58, 0x58, 0x9B, 0xA1, 0xB5, +0x7B, 0xE2, 0xF8, 0x18, 0x59, 0x84, 0x3E, 0x3C, 0xFB, 0x51, 0x7E, 0xDA, 0x55, 0x7A, 0x23, 0x2D, +0xFB, 0x38, 0xEB, 0xEC, 0x87, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0x32, 0xD9, 0x9E, 0x9D, 0xB0, 0x00, +0x01, 0x02, 0x2C, 0xA8, 0xAC, 0x75, 0x3D, 0xC4, 0xF6, 0xC9, 0x07, 0x3E, 0xE5, 0x6D, 0x77, 0x87, +0xA3, 0x23, 0xD6, 0x4A, 0x77, 0xD3, 0xBB, 0x9F, 0xDD, 0x78, 0xB6, 0x71, 0xA3, 0xF8, 0x10, 0x1F, +0xE2, 0x63, 0x72, 0xED, 0xBD, 0xF4, 0x7A, 0xC3, 0x09, 0x0E, 0x10, 0x20, 0xC0, 0xE2, 0x69, 0x6D, +0x6D, 0x4C, 0x32, 0xF0, 0x39, 0xEA, 0xA2, 0x83, 0xB1, 0x7C, 0x6D, 0x8F, 0xD0, 0x0D, 0x80, 0xC3, +0xD9, 0x8F, 0xDE, 0x8D, 0x67, 0x9D, 0xFD, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x9C, 0xF0, 0xF3, 0xF3, +0x85, 0xE8, 0x0D, 0x27, 0x38, 0x40, 0x80, 0x00, 0x0B, 0x38, 0xD0, 0xC9, 0x1A, 0x27, 0x1E, 0xF8, +0x8C, 0xD9, 0x62, 0xB7, 0x18, 0xEC, 0x97, 0xEB, 0xA4, 0x7F, 0x57, 0x67, 0x99, 0xFD, 0x10, 0x1F, +0xE2, 0x43, 0x7C, 0x9C, 0x5C, 0x96, 0xCF, 0x80, 0x5C, 0x73, 0x82, 0x03, 0x04, 0x08, 0xB0, 0x58, +0xE3, 0x9C, 0x5B, 0x0F, 0x0F, 0x2E, 0x40, 0x3F, 0x6A, 0xB0, 0x3F, 0x10, 0x1E, 0xA5, 0x1B, 0x47, +0x66, 0x3F, 0x8A, 0x81, 0x7F, 0xE9, 0x86, 0xF4, 0xEC, 0xAE, 0xED, 0x53, 0x0E, 0xA8, 0xC4, 0x87, +0xF8, 0x10, 0x1F, 0xA7, 0xD7, 0x9E, 0x60, 0x7B, 0x6D, 0x00, 0x01, 0x02, 0xCC, 0x59, 0x3D, 0xB4, +0xB6, 0x4E, 0x36, 0xF0, 0x29, 0xAF, 0xFD, 0x08, 0x83, 0x03, 0xFE, 0xF2, 0xBE, 0xBB, 0xFD, 0xBB, +0x13, 0x1F, 0xE2, 0x43, 0x7C, 0x9C, 0xD3, 0xEF, 0x41, 0xBB, 0xE1, 0x14, 0x07, 0x08, 0x10, 0x60, +0xD1, 0x6C, 0x86, 0x76, 0xF3, 0xD8, 0x81, 0xCF, 0x71, 0x6B, 0x3F, 0xC2, 0xC8, 0xEC, 0x47, 0x1C, +0xDC, 0xFD, 0x2A, 0xCD, 0x7E, 0xEC, 0x9D, 0x62, 0x50, 0x25, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0xBB, +0xEE, 0x4E, 0x58, 0x75, 0xA7, 0x39, 0x40, 0x80, 0x00, 0x8B, 0xE4, 0x5A, 0x68, 0xEF, 0x9F, 0x68, +0x3C, 0x58, 0x5E, 0xFB, 0x51, 0xDE, 0xF9, 0x2A, 0x0E, 0x0F, 0xA0, 0xD2, 0x5F, 0xB7, 0xC5, 0x87, +0xF8, 0x10, 0x1F, 0xE7, 0xF6, 0x7B, 0x90, 0xE5, 0x0B, 0xD1, 0x05, 0x08, 0x20, 0x40, 0x80, 0x05, +0x72, 0xB0, 0xB5, 0x39, 0xD1, 0x10, 0xF0, 0xB8, 0xB5, 0x1F, 0x43, 0xD7, 0xFF, 0xE8, 0x7F, 0x60, +0xDA, 0xF9, 0xEA, 0xA4, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x29, 0xDE, 0xB7, 0x9D, 0xB0, +0x00, 0x01, 0x02, 0x2C, 0xDC, 0xE0, 0xA7, 0x35, 0xF9, 0x85, 0xCA, 0x86, 0x77, 0xBE, 0x2A, 0x6D, +0xC1, 0x3B, 0xF6, 0x02, 0x85, 0x2F, 0x89, 0x0F, 0xF1, 0x21, 0x3E, 0xCE, 0x2D, 0x3E, 0x92, 0xF6, +0x76, 0xFA, 0x37, 0x6E, 0x27, 0x2C, 0x40, 0x80, 0x00, 0x0B, 0xA4, 0xBD, 0xB7, 0x71, 0xC7, 0x61, +0xE0, 0xB1, 0xB3, 0x1F, 0x71, 0xF4, 0x63, 0x92, 0xF4, 0x1F, 0x5E, 0x0F, 0x4E, 0x30, 0xB8, 0x12, +0x1F, 0xE2, 0x43, 0x7C, 0xCC, 0xE8, 0xEB, 0xB4, 0xEB, 0x4E, 0x74, 0x80, 0x00, 0x01, 0x16, 0x63, +0xFC, 0x73, 0xEB, 0xE1, 0xC6, 0xC4, 0xEB, 0x3F, 0xC6, 0xCE, 0x7E, 0xC4, 0xEE, 0xE0, 0x7F, 0xE0, +0xB6, 0x70, 0xF2, 0xB5, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x19, 0x7E, 0xAD, 0x13, 0xCC, 0x72, +0x02, 0x08, 0x10, 0x60, 0xC6, 0x06, 0xAF, 0x01, 0x32, 0x6E, 0x28, 0x78, 0xE4, 0xEC, 0x47, 0x1C, +0x8A, 0x93, 0x92, 0x93, 0xCC, 0x7E, 0x88, 0x0F, 0xF1, 0x21, 0x3E, 0x66, 0x2B, 0xDB, 0xD9, 0x88, +0x1F, 0x7F, 0xFB, 0x86, 0xD3, 0x1D, 0x20, 0x40, 0x80, 0xC5, 0x08, 0x90, 0x83, 0xAD, 0x89, 0xC6, +0x86, 0xFD, 0xEB, 0x7E, 0xA4, 0xB1, 0x53, 0x16, 0x07, 0x03, 0x20, 0x84, 0xD3, 0xCD, 0x7E, 0x88, +0x0F, 0xF1, 0x21, 0x3E, 0xE6, 0x10, 0x20, 0xF9, 0x42, 0x74, 0xB3, 0x20, 0x80, 0x00, 0x01, 0x16, +0x40, 0x7B, 0xF7, 0xFA, 0x91, 0xC3, 0xC1, 0xF2, 0xAE, 0x56, 0xA5, 0xC0, 0xC8, 0x9F, 0x76, 0x35, +0x34, 0x66, 0x1C, 0xB8, 0xEE, 0x47, 0xDA, 0xF5, 0x6A, 0x92, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, +0xC7, 0x9C, 0xBE, 0xAE, 0x00, 0x01, 0x04, 0x08, 0xB0, 0x30, 0x01, 0xD2, 0xAC, 0x1F, 0xF9, 0xBE, +0x5A, 0x29, 0x3C, 0x42, 0xED, 0xF0, 0x69, 0x57, 0x31, 0x8C, 0xAE, 0x07, 0x29, 0x8F, 0x25, 0x77, +0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x0B, 0x13, 0x1F, 0xF9, 0xBF, 0xF3, 0xED, 0xF4, 0xDA, 0x4E, 0x58, +0xC0, 0x99, 0xAD, 0x3B, 0x04, 0xC0, 0x99, 0x65, 0xCD, 0xCD, 0xB1, 0x43, 0xC2, 0x58, 0x1E, 0x1E, +0xD6, 0xFA, 0xBB, 0x5D, 0xC5, 0xE1, 0xEB, 0x7C, 0xC4, 0x31, 0xB3, 0x1F, 0x6D, 0xF1, 0x21, 0x3E, +0xC4, 0xC7, 0xC2, 0xC4, 0x47, 0xFF, 0x31, 0xB4, 0xCD, 0x80, 0x00, 0x67, 0x66, 0x06, 0x04, 0x38, +0xDB, 0x78, 0xE4, 0xD6, 0xC3, 0xF5, 0x23, 0x77, 0xC0, 0xAA, 0x1D, 0x86, 0xC6, 0xF0, 0xCC, 0x47, +0xF1, 0xB4, 0xAC, 0x91, 0x31, 0xD5, 0x24, 0xB3, 0x1F, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x73, 0xFA, +0x8F, 0x0D, 0x7B, 0x02, 0x04, 0x10, 0x20, 0xC0, 0xB9, 0xEB, 0x04, 0xC8, 0xEE, 0xF8, 0x61, 0x61, +0x79, 0x06, 0xA4, 0x34, 0xF3, 0x71, 0xB8, 0x36, 0xA4, 0x76, 0x18, 0x06, 0xC5, 0xE7, 0xEC, 0xDE, +0x61, 0xF6, 0x43, 0x7C, 0x88, 0x0F, 0xF1, 0x71, 0x8E, 0x01, 0xB2, 0x6F, 0x17, 0x2C, 0x40, 0x80, +0x00, 0xE7, 0xAE, 0x11, 0xDA, 0xCD, 0xB1, 0xF1, 0x31, 0xB2, 0x08, 0x7D, 0x78, 0xF6, 0xA3, 0x3C, +0xB6, 0xCA, 0x77, 0xC5, 0x0A, 0xC7, 0xEF, 0x7C, 0x25, 0x3E, 0xC4, 0x87, 0xF8, 0x38, 0xE7, 0xC7, +0xD3, 0x0C, 0xF1, 0xE3, 0x6F, 0x6F, 0x38, 0xED, 0x01, 0x02, 0x04, 0x38, 0x4F, 0xD7, 0x8E, 0xBA, +0x06, 0xC8, 0xE0, 0x95, 0xCE, 0x0F, 0x83, 0x23, 0x96, 0x66, 0x3F, 0x06, 0xC6, 0x58, 0xBB, 0xF1, +0xE8, 0xB1, 0xA5, 0xF8, 0x10, 0x1F, 0xE2, 0xE3, 0xFC, 0x65, 0x7B, 0xE9, 0x75, 0xDD, 0x69, 0x0F, +0x10, 0x20, 0xC0, 0xF9, 0x69, 0xBE, 0x50, 0x1F, 0x19, 0x1A, 0xC6, 0xC3, 0xA7, 0x5D, 0x0D, 0x2C, +0x30, 0xEF, 0x7F, 0x40, 0x6D, 0x74, 0xFD, 0x47, 0x9A, 0xFD, 0x38, 0x6A, 0xED, 0x87, 0xF8, 0x10, +0x1F, 0xE2, 0x63, 0x11, 0x1E, 0x94, 0x00, 0x01, 0x04, 0x08, 0xB0, 0x00, 0xB2, 0xD6, 0xE6, 0x51, +0x03, 0xA8, 0xE1, 0x2D, 0x76, 0x8B, 0x20, 0x08, 0x71, 0xCC, 0x38, 0xEB, 0xA8, 0xD9, 0x0F, 0xF1, +0x21, 0x3E, 0xC4, 0xC7, 0x62, 0xC4, 0x47, 0xFE, 0x47, 0xBB, 0xF3, 0xD2, 0xBA, 0xE1, 0xC4, 0x07, +0x08, 0x10, 0xE0, 0x7C, 0x86, 0x24, 0xB7, 0x1E, 0xDE, 0xE8, 0x0C, 0x46, 0x36, 0x06, 0x86, 0x87, +0xBD, 0x8B, 0x0C, 0x0E, 0xAC, 0xF7, 0x88, 0x83, 0xE3, 0xC7, 0x89, 0x67, 0x3F, 0xC4, 0x87, 0xF8, +0x10, 0x1F, 0x8B, 0x13, 0x1F, 0xFD, 0x7F, 0xAF, 0x7B, 0x75, 0x67, 0x3F, 0x40, 0x80, 0x00, 0xE7, +0x65, 0x33, 0xB4, 0x76, 0x46, 0x07, 0x50, 0xBD, 0x0B, 0x0C, 0x8E, 0xDC, 0x7C, 0xD4, 0xEC, 0xC7, +0xD6, 0x98, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xE2, 0xC5, 0x47, 0x7E, 0x53, 0x4B, 0x80, +0x00, 0x02, 0x04, 0x38, 0x37, 0xF5, 0xB4, 0x00, 0x3D, 0x96, 0x06, 0x50, 0x83, 0x33, 0x21, 0xA1, +0xB4, 0xEF, 0xEE, 0xE1, 0x70, 0x66, 0xE0, 0xA2, 0x83, 0x69, 0xCB, 0xDD, 0xBD, 0x28, 0x3E, 0xC4, +0x87, 0xF8, 0xA8, 0x42, 0x7C, 0xE4, 0x37, 0x1F, 0xD8, 0x09, 0x0B, 0x10, 0x20, 0xC0, 0xB9, 0x8D, +0x4F, 0xEA, 0xA1, 0xB5, 0x5B, 0x1A, 0xFD, 0x87, 0xFE, 0xEC, 0x47, 0x1C, 0x09, 0x8E, 0xDA, 0xE8, +0x70, 0x26, 0xDD, 0xB0, 0x2D, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x4C, 0x7C, 0xE4, 0xFF, 0xD1, 0x60, +0x3B, 0xBD, 0x76, 0x3D, 0x10, 0x40, 0x80, 0x00, 0xE7, 0xA0, 0xBD, 0x77, 0x63, 0x60, 0xB7, 0xAB, +0xF2, 0xD8, 0x65, 0xCC, 0xDA, 0x8F, 0x81, 0xEB, 0x81, 0x24, 0x69, 0xED, 0xC7, 0x5E, 0x14, 0x1F, +0xE2, 0x43, 0x7C, 0x54, 0x25, 0x3E, 0xF2, 0x7F, 0xB7, 0xF9, 0xB6, 0xDB, 0xAE, 0x88, 0x0E, 0x08, +0x10, 0xE0, 0x1C, 0x64, 0xCD, 0xFA, 0x60, 0x1D, 0x0C, 0xCE, 0x7E, 0x1C, 0x6E, 0xC1, 0x5B, 0x1B, +0x7F, 0x81, 0xC2, 0x2D, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0xA5, 0xE2, 0x23, 0xFF, 0x90, 0x83, 0xF4, +0xFA, 0xBA, 0x13, 0x20, 0x20, 0x40, 0x80, 0xF9, 0x0F, 0x55, 0x0E, 0xB6, 0xEB, 0x03, 0x51, 0x51, +0x8C, 0x5F, 0xE2, 0x98, 0xB1, 0xD5, 0xF0, 0x3A, 0xF3, 0x34, 0x86, 0xD9, 0x8F, 0xE2, 0x43, 0x7C, +0x88, 0x8F, 0x2A, 0xC5, 0x47, 0xA1, 0x7D, 0xBB, 0xEE, 0x0C, 0x08, 0x08, 0x10, 0x60, 0xBE, 0x43, +0x95, 0x8F, 0x3E, 0xB4, 0x19, 0xB2, 0xFD, 0xA1, 0xC0, 0x98, 0x60, 0xF6, 0xA3, 0x78, 0x5A, 0xD6, +0xB6, 0xF8, 0x10, 0x1F, 0xE2, 0xA3, 0x92, 0xF1, 0x91, 0x7F, 0x78, 0xDB, 0x53, 0xB0, 0x00, 0x01, +0x02, 0xCC, 0x7D, 0x0C, 0x95, 0xEF, 0x80, 0x75, 0xEA, 0xD9, 0x8F, 0x83, 0x28, 0x3E, 0xC4, 0x87, +0xF8, 0xA8, 0x62, 0x7C, 0xE4, 0x9F, 0x92, 0xEF, 0x84, 0x25, 0x42, 0x00, 0x01, 0x02, 0xCC, 0xD5, +0xE6, 0xE1, 0x0E, 0x58, 0x27, 0x9D, 0xFD, 0xC8, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x55, 0x8D, 0x8F, +0xA4, 0x9D, 0xFF, 0xDB, 0xAF, 0x3B, 0x0D, 0x02, 0x02, 0x04, 0x98, 0x9F, 0xEC, 0xE0, 0x5A, 0xC8, +0xDA, 0xA7, 0x98, 0xFD, 0xE8, 0xFC, 0xAD, 0x29, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x6C, 0x7C, 0xE4, +0x9F, 0x7A, 0xD0, 0xFD, 0x8F, 0x10, 0x00, 0x02, 0x04, 0x98, 0x9B, 0x56, 0x79, 0x01, 0x7A, 0x6D, +0x30, 0x38, 0xCA, 0xA1, 0x50, 0xBA, 0x2D, 0x1F, 0xEF, 0xBC, 0x94, 0x89, 0x0F, 0xF1, 0x21, 0x3E, +0xAA, 0x1C, 0x1F, 0xF9, 0x7F, 0x80, 0xD8, 0x4B, 0x57, 0x44, 0xB7, 0x13, 0x16, 0x20, 0x40, 0x80, +0x79, 0x8E, 0x5F, 0xB2, 0xC6, 0xE8, 0x53, 0xAE, 0x86, 0xAE, 0xF3, 0x11, 0x86, 0xAE, 0x7A, 0xBE, +0x9B, 0x75, 0xAF, 0x7C, 0x2E, 0x3E, 0xC4, 0x87, 0xF8, 0xA8, 0x6E, 0x7C, 0xF4, 0x23, 0x64, 0xDF, +0xC5, 0x08, 0x01, 0x01, 0x02, 0xCC, 0x47, 0xF6, 0x7B, 0x0F, 0xD5, 0x43, 0xBB, 0x39, 0x76, 0xED, +0xC7, 0xC8, 0x6D, 0xFD, 0x4F, 0xEA, 0xDC, 0xB2, 0x13, 0xC5, 0x87, 0xF8, 0x10, 0x1F, 0xCB, 0x10, +0x1F, 0xDD, 0x7F, 0xD4, 0x0D, 0x67, 0x43, 0x40, 0x80, 0x00, 0xF3, 0x1A, 0xC3, 0xD4, 0x63, 0xEB, +0x76, 0x3F, 0x16, 0xC2, 0xB8, 0xD9, 0x8F, 0xD2, 0xAC, 0x48, 0xFE, 0x2A, 0x5D, 0xF1, 0xBC, 0x2D, +0x3E, 0xC4, 0x87, 0xF8, 0x58, 0x8E, 0xF8, 0x08, 0xF9, 0xD3, 0xB0, 0xE2, 0xC7, 0xDF, 0x5E, 0x77, +0x42, 0x04, 0x04, 0x08, 0x30, 0x8F, 0x61, 0xCC, 0x66, 0x9A, 0x01, 0x19, 0x9E, 0xF9, 0x28, 0x16, +0x9C, 0x0F, 0x8C, 0xBD, 0xD2, 0x5F, 0xD2, 0xB2, 0x8F, 0xA1, 0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, +0x47, 0xA8, 0xF6, 0xF1, 0xB7, 0x13, 0x16, 0x20, 0x40, 0x80, 0x39, 0xBA, 0x16, 0xDB, 0x07, 0x03, +0x33, 0x1F, 0xFD, 0xD9, 0x8F, 0x58, 0x2B, 0x05, 0x44, 0x6F, 0xD0, 0xB3, 0x3B, 0x38, 0xFB, 0x21, +0x3E, 0xC4, 0x87, 0xF8, 0x08, 0xD5, 0x3F, 0xFE, 0xDD, 0x9D, 0xB0, 0x1A, 0x4E, 0x87, 0x80, 0x00, +0x01, 0x66, 0xEF, 0x60, 0x6B, 0x33, 0x94, 0x9F, 0x72, 0x55, 0x9E, 0xFD, 0x08, 0xA5, 0xA7, 0x5D, +0xA5, 0x3F, 0x87, 0x66, 0x3F, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x61, 0x39, 0x8E, 0x7F, 0xDA, 0x09, +0x2B, 0x84, 0x6B, 0x4E, 0x88, 0x80, 0x00, 0x01, 0x66, 0x2F, 0x3B, 0xA8, 0x17, 0x63, 0x9B, 0x7E, +0x6B, 0x14, 0x31, 0xD1, 0x1B, 0x88, 0x1D, 0xEE, 0x7C, 0x75, 0x78, 0x21, 0x10, 0xF1, 0x21, 0x3E, +0xC4, 0x47, 0x58, 0xAE, 0xE3, 0x9F, 0xED, 0xD7, 0x9D, 0x10, 0x01, 0x01, 0x02, 0xCC, 0x5E, 0x7B, +0xBF, 0x1E, 0xCB, 0xD7, 0xF6, 0x08, 0xDD, 0x98, 0x18, 0x78, 0xDA, 0x55, 0x3E, 0x38, 0x09, 0xFD, +0xD9, 0x0F, 0xF1, 0x21, 0x3E, 0xC4, 0x47, 0x58, 0xBE, 0xE3, 0x1F, 0x5B, 0x0D, 0x27, 0x44, 0x40, +0x80, 0x00, 0xB3, 0x6D, 0x8F, 0xDF, 0x79, 0xB8, 0x11, 0xD3, 0x16, 0xBC, 0x61, 0x30, 0x1C, 0x42, +0xE9, 0x69, 0x57, 0xFD, 0xF1, 0xD8, 0x76, 0x1C, 0xB9, 0x3A, 0xBA, 0xF8, 0x10, 0x1F, 0xE2, 0x63, +0x89, 0x8E, 0x7F, 0xB6, 0x93, 0x76, 0xC2, 0x72, 0x3D, 0x10, 0x40, 0x80, 0x00, 0x33, 0x95, 0x5F, +0x03, 0x64, 0x70, 0xF6, 0x23, 0xE6, 0x33, 0x1F, 0x03, 0x63, 0xB1, 0xB4, 0xE8, 0x7C, 0x37, 0x8A, +0x0F, 0xF1, 0x21, 0x3E, 0x96, 0x35, 0x3E, 0xF2, 0x00, 0xC9, 0xFF, 0x63, 0xC4, 0xA6, 0xD3, 0x22, +0x20, 0x40, 0x80, 0x59, 0x8E, 0x6F, 0xEA, 0xF1, 0x60, 0xAB, 0x14, 0x0F, 0x61, 0xE4, 0xCA, 0xE7, +0xB9, 0x6D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0x52, 0xC7, 0x47, 0xFE, 0x25, 0x0F, 0x04, 0x08, 0x20, +0x40, 0x80, 0x19, 0x8F, 0x37, 0xDA, 0xBB, 0xD7, 0x43, 0x39, 0x3A, 0xCA, 0x17, 0x21, 0x2C, 0xB4, +0x3B, 0x7F, 0xDF, 0x8D, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x65, 0x3F, 0xFE, 0xED, 0xED, 0xF4, 0xDA, +0x4E, 0x58, 0x80, 0x00, 0x01, 0x66, 0xA8, 0xD5, 0xAC, 0x1F, 0xEE, 0x7C, 0x15, 0x07, 0xC6, 0x3F, +0xFD, 0xB8, 0xD8, 0x16, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xCC, 0xF1, 0xCF, 0xF6, 0xCD, 0x80, 0x00, +0x02, 0x04, 0x98, 0xE1, 0x50, 0x27, 0x6B, 0x6E, 0x1E, 0x5E, 0x00, 0xA4, 0x56, 0xDA, 0x82, 0xB7, +0xF7, 0xFE, 0xF4, 0x8C, 0x8C, 0xDD, 0x28, 0x3E, 0xC4, 0x87, 0xF8, 0x58, 0x95, 0xE3, 0x1F, 0x5B, +0x02, 0x04, 0x10, 0x20, 0xC0, 0x6C, 0xB4, 0xFE, 0xC5, 0xC3, 0xF5, 0xD0, 0xDE, 0x3F, 0x5C, 0x70, +0x1E, 0xC7, 0x8C, 0x83, 0xB6, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x2B, 0x75, 0xFC, 0xB3, 0xBD, 0x0D, +0x3B, 0x61, 0x01, 0x02, 0x04, 0x98, 0xD1, 0x70, 0x27, 0xD6, 0xE3, 0xC1, 0xCE, 0xC0, 0xF0, 0x27, +0x96, 0x87, 0x42, 0x69, 0x43, 0x9C, 0x66, 0x14, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xD2, 0xF1, 0xB7, +0x10, 0x1D, 0x10, 0x20, 0xC0, 0x0C, 0xC7, 0x3C, 0x8D, 0x98, 0x15, 0x5B, 0xF0, 0x76, 0x03, 0xA3, +0x56, 0x7E, 0xF7, 0xB6, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0xE5, 0x8E, 0x7F, 0xB6, 0x97, 0x5E, 0xD7, +0x9D, 0x20, 0x01, 0x01, 0x02, 0xCC, 0x62, 0xD8, 0x73, 0x2D, 0x5D, 0x03, 0x24, 0x14, 0x31, 0x11, +0x4A, 0x5B, 0xF1, 0xA6, 0x9B, 0xF7, 0xA3, 0xF8, 0x10, 0x1F, 0xE2, 0x63, 0xD5, 0x8E, 0xBF, 0x00, +0x01, 0x04, 0x08, 0x30, 0x33, 0xFB, 0x2F, 0xD4, 0x87, 0xAF, 0xED, 0xD1, 0x7F, 0x0A, 0xD6, 0xED, +0x4C, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0x8A, 0xC7, 0x3F, 0xA6, 0x7D, 0xB7, 0x5B, 0x37, 0x9C, 0x20, +0x01, 0x01, 0x02, 0x4C, 0x5F, 0x36, 0xBA, 0xDB, 0x4D, 0x2D, 0x8D, 0x89, 0xD2, 0x15, 0xCF, 0x0F, +0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x2B, 0x7B, 0xFC, 0xB3, 0xBD, 0xBA, 0x13, 0x24, 0x20, 0x40, 0x80, +0xA9, 0x6A, 0x7E, 0xE4, 0xA1, 0x8D, 0x4E, 0x80, 0x6C, 0xE4, 0x4F, 0xBF, 0x0A, 0x83, 0x8B, 0xCF, +0xB3, 0xDB, 0x41, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0xCA, 0xC7, 0x3F, 0xDB, 0x17, 0x20, 0x80, 0x00, +0x01, 0xA6, 0xAC, 0x16, 0x36, 0x63, 0x6B, 0xA7, 0x5F, 0x1A, 0xB5, 0x62, 0x4C, 0xB4, 0x1B, 0xBA, +0x57, 0x3E, 0x17, 0x1F, 0xE2, 0x43, 0x7C, 0xAC, 0xEE, 0xF1, 0x8F, 0x07, 0x21, 0x7E, 0xFC, 0xED, +0x0D, 0x27, 0x4A, 0x40, 0x80, 0x00, 0xD3, 0x54, 0x4F, 0x0B, 0xD0, 0x63, 0xAF, 0x46, 0x8A, 0x3F, +0xB3, 0xDB, 0x51, 0x7C, 0x88, 0x0F, 0xF1, 0xB1, 0xEA, 0xC7, 0xDF, 0x42, 0x74, 0x40, 0x80, 0x00, +0x33, 0x18, 0x9F, 0xD5, 0x63, 0x6B, 0xB7, 0x3B, 0x01, 0xD2, 0x1B, 0x18, 0xC5, 0xAD, 0xCE, 0x5B, +0x2D, 0xF1, 0x21, 0x3E, 0xC4, 0xC7, 0xCA, 0x1F, 0xFF, 0xAC, 0x29, 0x40, 0x00, 0x01, 0x02, 0x4C, +0x59, 0x7B, 0xAF, 0xBF, 0xCB, 0x4D, 0xAD, 0xB7, 0xDB, 0x55, 0x9A, 0xFD, 0x10, 0x1F, 0xE2, 0x43, +0x7C, 0x38, 0xFE, 0xF9, 0xC5, 0x08, 0xED, 0x84, 0x05, 0x08, 0x10, 0x60, 0xBA, 0x01, 0xD2, 0xAC, +0x97, 0xC7, 0x6C, 0xD9, 0x56, 0xE7, 0xCF, 0xAC, 0x37, 0x56, 0x12, 0x1F, 0xE2, 0x43, 0x7C, 0x38, +0xFE, 0xD9, 0xEE, 0x86, 0x13, 0x25, 0x30, 0x89, 0x75, 0x87, 0x00, 0x98, 0x48, 0x6B, 0xA7, 0x17, +0x20, 0xBD, 0xD9, 0x8F, 0xAD, 0xD8, 0x9F, 0xF9, 0x08, 0xE2, 0x43, 0x7C, 0x88, 0x0F, 0xC7, 0x3F, +0xB6, 0x37, 0x9D, 0x28, 0x81, 0x49, 0x98, 0x01, 0x01, 0xEE, 0xA8, 0xF9, 0xDB, 0x0F, 0x6D, 0x86, +0x6C, 0xBF, 0xD3, 0x1E, 0xB5, 0xFC, 0xEF, 0xF9, 0xEC, 0x47, 0xBB, 0x74, 0x15, 0x74, 0xF1, 0x21, +0x3E, 0xC4, 0x87, 0xE3, 0xDF, 0xDD, 0x09, 0x4B, 0x84, 0x00, 0x02, 0x04, 0x98, 0x8A, 0x7C, 0x07, +0xAC, 0x7C, 0x8C, 0x94, 0xC2, 0xE3, 0x76, 0x31, 0x58, 0xAA, 0x85, 0xDA, 0xC5, 0xF5, 0x70, 0xF5, +0xAD, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x95, 0x3F, 0xFE, 0xED, 0xED, 0xF4, 0xBD, 0xD4, 0x9D, 0x2E, +0x01, 0x01, 0x02, 0x4C, 0xC3, 0x66, 0x68, 0xEF, 0x75, 0xC7, 0x18, 0x2F, 0xC5, 0x7C, 0xED, 0x47, +0xFE, 0xD4, 0xAB, 0xF5, 0x4E, 0x7C, 0xA4, 0x99, 0x8F, 0x97, 0x8B, 0x0F, 0xF1, 0x21, 0x3E, 0x56, +0xFE, 0xF8, 0x77, 0x77, 0xC2, 0x32, 0x03, 0x02, 0xDC, 0x91, 0x35, 0x20, 0xC0, 0x1D, 0xD5, 0xB2, +0x83, 0x6B, 0x31, 0x3D, 0xE7, 0xAA, 0xF3, 0xBF, 0x6C, 0xBB, 0x3B, 0x64, 0x4A, 0x33, 0x1F, 0xF7, +0xBC, 0x59, 0x7C, 0x88, 0x0F, 0xF1, 0xE1, 0xF8, 0xF7, 0xBE, 0x97, 0x98, 0x07, 0xC8, 0x75, 0x67, +0x4C, 0x40, 0x80, 0x00, 0x67, 0xD7, 0xDA, 0xAE, 0xA7, 0xB5, 0xE7, 0x69, 0xF6, 0x23, 0x8D, 0x33, +0x52, 0x7C, 0xDC, 0xFD, 0x26, 0xF1, 0x21, 0x3E, 0xC4, 0x87, 0xE3, 0x3F, 0xF4, 0xBD, 0xB4, 0x8B, +0xCD, 0x2A, 0x00, 0x04, 0x08, 0x70, 0x06, 0xB5, 0xD0, 0x6E, 0x84, 0x56, 0xCC, 0x17, 0x9F, 0x87, +0x4B, 0x9D, 0xF8, 0x78, 0xE3, 0xEB, 0xC4, 0x87, 0xF8, 0x10, 0x1F, 0x8E, 0xFF, 0x98, 0xEF, 0xC5, +0x4E, 0x58, 0xC0, 0x9D, 0x59, 0x03, 0x02, 0x1C, 0xAB, 0xFD, 0x3B, 0xAF, 0xAB, 0xA7, 0xA7, 0x56, +0xB4, 0xBE, 0x22, 0x3E, 0xC4, 0x87, 0xF8, 0x70, 0xFC, 0xEF, 0xF0, 0xBD, 0x64, 0x7B, 0x21, 0x7E, +0xFC, 0xAF, 0xD4, 0x9D, 0x39, 0x01, 0x01, 0x02, 0x9C, 0x45, 0x3D, 0xBB, 0x7D, 0x3B, 0xC4, 0xAC, +0x13, 0x1F, 0x6F, 0x10, 0x1F, 0xE2, 0x43, 0x7C, 0x38, 0xFE, 0xC7, 0x7C, 0x2F, 0xED, 0xDD, 0xFC, +0x9C, 0xE1, 0xB4, 0x09, 0x08, 0x10, 0xE0, 0xD4, 0x0E, 0xF6, 0xB3, 0xCD, 0xD6, 0xED, 0x2C, 0xDC, +0xD5, 0x89, 0x8F, 0x35, 0xF1, 0x21, 0x3E, 0xC4, 0x87, 0xE3, 0x7F, 0xCC, 0xF7, 0x12, 0xDB, 0x07, +0x61, 0xFF, 0xF6, 0x41, 0xC3, 0x99, 0x13, 0x38, 0x8E, 0x35, 0x20, 0xC0, 0xB1, 0x6E, 0x7F, 0x39, +0xBB, 0xB6, 0x7E, 0xED, 0x7A, 0xB8, 0x78, 0xE5, 0x6A, 0x58, 0x13, 0x1F, 0xE2, 0x43, 0x7C, 0x88, +0x8F, 0x23, 0xEC, 0xBE, 0x90, 0x85, 0xBD, 0x17, 0xB7, 0x42, 0xBC, 0xBC, 0x77, 0xCD, 0x99, 0x13, +0x10, 0x20, 0xC0, 0xA9, 0xAD, 0x7D, 0xE3, 0xD7, 0xBD, 0x3A, 0x76, 0xCE, 0x14, 0xB7, 0xFF, 0xF4, +0xD9, 0xB0, 0xBE, 0xF6, 0x5C, 0xB8, 0xE7, 0x81, 0x0B, 0x61, 0xED, 0xD4, 0x67, 0x0E, 0xF1, 0x21, +0x3E, 0xC4, 0xC7, 0xB2, 0xC5, 0xC7, 0xFE, 0x56, 0x0C, 0xBB, 0x5F, 0xCD, 0x42, 0xD6, 0xEA, 0xBD, +0x3F, 0xDB, 0xDD, 0x70, 0xE6, 0x04, 0x8E, 0x1D, 0x5B, 0x38, 0x04, 0xC0, 0x71, 0xFE, 0xCD, 0xAD, +0x7B, 0xDF, 0x71, 0xB0, 0xBD, 0xFB, 0xFE, 0xB8, 0xF7, 0xEC, 0x56, 0x6B, 0x2F, 0x86, 0x17, 0xFF, +0xB4, 0x15, 0xB6, 0x9F, 0xCB, 0xF2, 0x8B, 0x11, 0x8A, 0x0F, 0xF1, 0x21, 0x3E, 0x56, 0x37, 0x3E, +0xD2, 0xF9, 0xE0, 0xF6, 0x17, 0xDB, 0x61, 0xFB, 0xD9, 0x76, 0x11, 0x1F, 0x2F, 0x74, 0x5E, 0xDE, +0xF3, 0xE4, 0x2F, 0xFC, 0x7F, 0x3F, 0xE4, 0xCC, 0x09, 0x1C, 0xA7, 0xE6, 0x10, 0x00, 0x93, 0xF8, +0xF8, 0x7B, 0x36, 0x5F, 0xF9, 0x75, 0xAF, 0x6A, 0xBF, 0xFF, 0xC2, 0xC5, 0xF8, 0x8E, 0xFC, 0xE4, +0xB1, 0x16, 0xC2, 0x95, 0x7B, 0xD7, 0xC2, 0x95, 0x8D, 0x49, 0xFE, 0x3B, 0x86, 0xF8, 0x10, 0x1F, +0xE2, 0x63, 0x59, 0xE2, 0x23, 0x6B, 0x85, 0xB0, 0xF3, 0x95, 0x76, 0x68, 0xEE, 0xC4, 0xF2, 0x6D, +0xFF, 0x70, 0x6D, 0x3D, 0xFC, 0xD2, 0xD7, 0xFC, 0xC8, 0xA7, 0x5E, 0x70, 0xB6, 0x04, 0x04, 0x08, +0x30, 0x55, 0x9F, 0xFC, 0x6F, 0xAF, 0x5F, 0x7F, 0xF9, 0x9F, 0x6B, 0x7F, 0xB0, 0x33, 0xD8, 0xF8, +0xEE, 0xF4, 0xF7, 0xB5, 0xF5, 0x5A, 0xB8, 0xAB, 0x13, 0x21, 0x97, 0xAE, 0xD6, 0xC4, 0x87, 0xF8, +0x10, 0x1F, 0x4B, 0x1C, 0x1F, 0x69, 0xD6, 0x33, 0x3D, 0xD5, 0x6A, 0xEF, 0xA5, 0xC3, 0xE9, 0xCF, +0xAC, 0x1D, 0x7E, 0x77, 0xED, 0x42, 0xF8, 0x9B, 0x9D, 0xF0, 0xF8, 0x8C, 0xB3, 0x23, 0x20, 0x40, +0x80, 0x99, 0xFA, 0xDC, 0x07, 0xFF, 0xE2, 0x0F, 0xDE, 0x73, 0x7F, 0xF6, 0xAB, 0xB5, 0xB5, 0xF0, +0x8D, 0xE9, 0xEF, 0xEB, 0x57, 0xBA, 0x21, 0x92, 0xFE, 0x14, 0x1F, 0xE2, 0x43, 0x7C, 0x2C, 0x57, +0x7C, 0x74, 0x17, 0x98, 0x0F, 0x3C, 0xF5, 0xF2, 0x0F, 0x3B, 0x2F, 0x3F, 0xD3, 0x09, 0x8F, 0x8F, +0x39, 0x1B, 0x02, 0x02, 0x04, 0x98, 0x9B, 0xFF, 0xE3, 0xCD, 0xDF, 0xB5, 0xFE, 0x9D, 0x3F, 0xBA, +0xF7, 0x5F, 0x5C, 0xBE, 0x1A, 0xDF, 0xDB, 0x09, 0x91, 0xAB, 0xE9, 0xB6, 0x8B, 0x77, 0xD7, 0xC2, +0xDD, 0xF7, 0xAF, 0x9D, 0x61, 0xA1, 0xBA, 0xF8, 0x30, 0xF8, 0x15, 0x1F, 0x8B, 0xF2, 0xBD, 0x0C, +0x2F, 0x30, 0xEF, 0x04, 0xC8, 0x9F, 0x76, 0xFE, 0xAD, 0xFF, 0xED, 0x4E, 0x78, 0x7C, 0xD8, 0x19, +0x10, 0x10, 0x20, 0xC0, 0xB9, 0xF9, 0xDC, 0x07, 0xFF, 0xE2, 0x03, 0x97, 0xEE, 0x8A, 0x7F, 0xEB, +0xE2, 0x5D, 0xF1, 0xC7, 0xBB, 0x21, 0x12, 0xF3, 0xD9, 0x90, 0xCB, 0xF7, 0xAE, 0xE5, 0x6B, 0x45, +0xC4, 0x87, 0xC1, 0xAF, 0xF8, 0xA8, 0x56, 0x7C, 0xA4, 0x05, 0xE6, 0x29, 0x3C, 0x0E, 0xF6, 0xFA, +0xDF, 0x57, 0x5A, 0xDB, 0xF1, 0xF7, 0x3B, 0x2F, 0x1F, 0xB0, 0xCE, 0x03, 0x10, 0x20, 0xC0, 0x22, +0x85, 0xC8, 0x83, 0x57, 0x5E, 0x96, 0xFD, 0xF2, 0xFA, 0xE5, 0xF8, 0xF6, 0xFC, 0x04, 0xD3, 0x89, +0x8F, 0x22, 0x44, 0xC4, 0x87, 0xC1, 0xAF, 0xF8, 0x58, 0x7C, 0x69, 0xA6, 0x63, 0x78, 0x81, 0x79, +0xC7, 0x87, 0x5F, 0xFC, 0x42, 0xFB, 0xE7, 0xFF, 0xFC, 0xCF, 0x7E, 0xFA, 0x39, 0x67, 0x39, 0x40, +0x80, 0x00, 0x0B, 0xE9, 0x8B, 0xBF, 0xF5, 0xDA, 0xB7, 0xAE, 0x5F, 0x8C, 0xEF, 0xBD, 0x70, 0x29, +0x7C, 0x67, 0xFA, 0x7B, 0x5A, 0xA8, 0x9E, 0x9E, 0x96, 0x95, 0x9E, 0x9E, 0x25, 0x3E, 0x0C, 0x7E, +0xC5, 0xC7, 0x02, 0x7E, 0x17, 0xF9, 0x02, 0xF3, 0xF6, 0xC0, 0x02, 0xF3, 0x8E, 0x8F, 0xED, 0xBD, +0x98, 0xFD, 0xC4, 0x2B, 0xFF, 0xFA, 0x9F, 0x7C, 0xCA, 0x59, 0x0D, 0x10, 0x20, 0xC0, 0xC2, 0xFB, +0x17, 0x6F, 0xFC, 0x8E, 0xF5, 0xEF, 0xFA, 0xB1, 0xE6, 0x8F, 0x5C, 0xBE, 0x27, 0xBE, 0xEF, 0xF8, +0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0xF3, 0x94, 0xA2, 0x23, 0xC5, 0x47, 0xB1, 0xC0, 0x3C, +0xB6, 0xC3, 0xBF, 0xAD, 0x75, 0x77, 0xB6, 0xFA, 0x98, 0x33, 0x19, 0x20, 0x40, 0x80, 0x2A, 0x86, +0xC8, 0x95, 0xEF, 0x7D, 0xD7, 0xFE, 0xE3, 0x6B, 0xEB, 0xE1, 0xA7, 0x8B, 0x85, 0xEA, 0x97, 0xAE, +0xAE, 0xE5, 0x21, 0x72, 0xC7, 0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x88, 0x8F, 0x99, 0x49, 0x4F, 0xB3, +0x4A, 0x4F, 0xB7, 0xB2, 0xC0, 0x1C, 0x10, 0x20, 0xC0, 0x52, 0xFA, 0xF7, 0x7F, 0xEF, 0xDB, 0xBF, +0xFE, 0xBE, 0xFF, 0x20, 0xFE, 0xF2, 0xC5, 0x2B, 0xF1, 0xC7, 0xF3, 0x93, 0x4F, 0xEF, 0x42, 0x86, +0x47, 0x2E, 0x54, 0x17, 0x1F, 0xE2, 0x43, 0x7C, 0xCC, 0xC4, 0xE0, 0x02, 0xF3, 0xFE, 0x15, 0xCC, +0x2D, 0x30, 0x07, 0x04, 0x08, 0xB0, 0x9C, 0xFE, 0xE4, 0x03, 0xDF, 0xFE, 0x9A, 0xAB, 0x5F, 0x97, +0x7D, 0x60, 0xFD, 0x52, 0x78, 0x53, 0x11, 0x22, 0x77, 0xDF, 0x7F, 0x61, 0xF0, 0x42, 0x86, 0xE2, +0x43, 0x7C, 0x88, 0x8F, 0xA9, 0x4B, 0x57, 0x30, 0x4F, 0xE1, 0xB1, 0xBF, 0x95, 0x95, 0xBF, 0x17, +0x0B, 0xCC, 0x01, 0x01, 0x02, 0xAC, 0x86, 0x2F, 0xFC, 0xC6, 0x6B, 0x7F, 0xE0, 0xF2, 0xD5, 0xF8, +0xFE, 0xB5, 0xF5, 0xF0, 0x60, 0xFA, 0x7B, 0x5A, 0xA8, 0x7E, 0xCF, 0x03, 0x6B, 0x61, 0xFD, 0x72, +0xD5, 0x06, 0x8E, 0xE2, 0x43, 0x7C, 0x2C, 0xF8, 0xA1, 0xCC, 0x8A, 0x75, 0x1E, 0xE5, 0x05, 0xE6, +0xD1, 0x02, 0x73, 0x40, 0x80, 0x00, 0xAB, 0x27, 0x2D, 0x54, 0xFF, 0xEE, 0x77, 0x36, 0xDF, 0x75, +0xE1, 0x62, 0x7C, 0xF7, 0xDA, 0x85, 0xF0, 0xF5, 0xB1, 0x33, 0xE8, 0xBC, 0x78, 0xA5, 0xBB, 0x63, +0xD6, 0x85, 0x4B, 0xD3, 0x3C, 0x3D, 0x89, 0x0F, 0xF1, 0xB1, 0x9A, 0xC7, 0xBF, 0x08, 0x8F, 0xD1, +0x05, 0xE6, 0x9F, 0xFC, 0x98, 0x33, 0x10, 0x20, 0x40, 0x80, 0x95, 0xF5, 0xB9, 0x7F, 0xF4, 0xDA, +0x07, 0x2E, 0x5D, 0x8D, 0xBF, 0xB0, 0x7E, 0x29, 0xFE, 0x64, 0xB1, 0x50, 0xFD, 0xF2, 0xD5, 0xB5, +0x3C, 0x44, 0xCE, 0x7E, 0x21, 0x43, 0xF1, 0x21, 0x3E, 0x56, 0xEF, 0xF8, 0x77, 0x17, 0x98, 0x8F, +0xBB, 0x82, 0xF9, 0x27, 0x3F, 0xEC, 0x8C, 0x03, 0x08, 0x10, 0x80, 0x9E, 0x7F, 0xF3, 0x5F, 0xBF, +0xF6, 0x95, 0xAF, 0x78, 0x30, 0x7B, 0xFF, 0x85, 0x8B, 0xE1, 0x1D, 0xF9, 0x09, 0xAA, 0xB7, 0x50, +0xFD, 0xCA, 0xA9, 0xAF, 0xA8, 0x2E, 0x3E, 0xC4, 0xC7, 0x6A, 0x1D, 0xFF, 0xE3, 0xAF, 0x60, 0xFE, +0x49, 0x0B, 0xCC, 0x01, 0x01, 0x02, 0x30, 0xCE, 0x67, 0x7F, 0xED, 0x35, 0xAF, 0xBB, 0xFB, 0xE5, +0xF9, 0xFA, 0x90, 0xEF, 0x4E, 0x7F, 0x4F, 0xEB, 0x43, 0xF2, 0x2B, 0xAA, 0x5F, 0x3D, 0xC9, 0x29, +0x4B, 0x7C, 0x88, 0x8F, 0xD5, 0x39, 0xFE, 0xA3, 0x0B, 0xCC, 0x73, 0xBD, 0x05, 0xE6, 0x7F, 0x62, +0x81, 0x39, 0x20, 0x40, 0x00, 0x26, 0x0A, 0x91, 0x7F, 0xF4, 0x9A, 0x1F, 0xBC, 0xE7, 0xFE, 0xF8, +0xAB, 0xC5, 0x85, 0x0C, 0xD3, 0xBA, 0x90, 0x7B, 0xEE, 0x9F, 0xE4, 0x42, 0x86, 0xE2, 0x43, 0x7C, +0xAC, 0xC6, 0xF1, 0x1F, 0xBF, 0xC0, 0xBC, 0xB8, 0x82, 0xF9, 0x1F, 0x5B, 0x60, 0x0E, 0x08, 0x10, +0x80, 0x93, 0xFA, 0x48, 0xE3, 0xFA, 0xFA, 0x77, 0xBF, 0xB3, 0xF5, 0x0B, 0x17, 0xAF, 0xC4, 0x5F, +0x2C, 0xD6, 0x87, 0xA4, 0x85, 0xEA, 0xF7, 0x3C, 0x70, 0xE1, 0x88, 0x0B, 0x19, 0x8A, 0x0F, 0xF1, +0xB1, 0x1A, 0xC7, 0xDF, 0x02, 0x73, 0x40, 0x80, 0x00, 0xCC, 0xD0, 0x67, 0x7F, 0xED, 0x35, 0x0F, +0x5C, 0xBC, 0x3B, 0xFC, 0xAD, 0xCB, 0xF7, 0xC4, 0xC7, 0x8A, 0xDB, 0xD2, 0xDA, 0x90, 0xF4, 0xD4, +0xAC, 0xC3, 0xF5, 0x21, 0xE2, 0x43, 0x7C, 0x2C, 0xFF, 0xF1, 0xB7, 0xC0, 0x1C, 0x10, 0x20, 0x00, +0xF3, 0x0D, 0x91, 0x07, 0xAF, 0xDC, 0x1B, 0x7F, 0x79, 0xFD, 0x72, 0x78, 0x7B, 0x7E, 0x12, 0x5B, +0x2B, 0x42, 0x64, 0x56, 0xA7, 0x33, 0xF1, 0x21, 0x3E, 0x16, 0x83, 0x05, 0xE6, 0x80, 0x00, 0x01, +0x38, 0x47, 0xCF, 0x7C, 0xF8, 0x35, 0x6F, 0x5D, 0xBF, 0x18, 0xDF, 0x7B, 0xE1, 0x52, 0xF8, 0xCE, +0x34, 0x70, 0x3C, 0xDD, 0x42, 0x75, 0xF1, 0x21, 0x3E, 0x16, 0xFF, 0xF8, 0x5B, 0x60, 0x0E, 0x08, +0x10, 0x80, 0x05, 0x91, 0xD6, 0x87, 0x7C, 0xCF, 0xBB, 0x0E, 0xDE, 0xB5, 0x7E, 0x29, 0xFE, 0x52, +0xB1, 0x50, 0x3D, 0xAD, 0x0F, 0x49, 0x21, 0x72, 0xE7, 0x85, 0xEA, 0xE2, 0x43, 0x7C, 0x2C, 0xF6, +0xF1, 0x3F, 0x6A, 0x81, 0x79, 0x27, 0x3C, 0x7E, 0xAA, 0x13, 0x1E, 0x7F, 0xE4, 0x0C, 0x00, 0x08, +0x10, 0x80, 0x73, 0xF2, 0xCF, 0x1F, 0x7A, 0xCD, 0x95, 0x87, 0x7E, 0x2A, 0x7B, 0x7C, 0x6D, 0x3D, +0xFC, 0x74, 0xF9, 0x42, 0x86, 0x29, 0x44, 0xC6, 0x2F, 0x54, 0x17, 0x1F, 0xE2, 0x63, 0xB1, 0x8F, +0xFF, 0xB8, 0x05, 0xE6, 0xED, 0x83, 0xF8, 0x33, 0xAF, 0xF8, 0x6B, 0x9F, 0xFA, 0x97, 0xFE, 0xC5, +0x03, 0x02, 0x04, 0x60, 0x41, 0xFC, 0xBB, 0xF7, 0x7D, 0xDB, 0x2B, 0xEE, 0x7B, 0x65, 0x7C, 0xEF, +0xC5, 0x2B, 0xE1, 0xC7, 0x8B, 0xDB, 0x52, 0x84, 0x9C, 0xEC, 0x42, 0x86, 0xE2, 0x43, 0x7C, 0x9C, +0x9F, 0xE1, 0x05, 0xE6, 0x1D, 0x2F, 0x74, 0xC2, 0xE3, 0xE7, 0xBF, 0xEE, 0xC7, 0x3E, 0xF5, 0x1B, +0xFE, 0x85, 0x03, 0x02, 0x04, 0x60, 0x41, 0xFD, 0xF1, 0x7F, 0xFF, 0x6D, 0xAF, 0xB9, 0xF7, 0x1B, +0xE2, 0x6F, 0x14, 0x17, 0x32, 0x4C, 0xF1, 0x51, 0x84, 0x88, 0xF8, 0x10, 0x1F, 0x8B, 0x78, 0xFC, +0x87, 0x17, 0x98, 0xC7, 0x2C, 0x6C, 0x75, 0x7E, 0x6F, 0xFF, 0xBB, 0x60, 0x81, 0x39, 0x20, 0x40, +0x00, 0xAA, 0xE3, 0xF3, 0xFF, 0xE3, 0xB7, 0xFE, 0xC0, 0x95, 0x97, 0x85, 0x74, 0x45, 0xF5, 0x07, +0xD3, 0xDF, 0xD3, 0x42, 0xF5, 0xBB, 0xEF, 0x5F, 0x0B, 0x97, 0xEE, 0xAE, 0x89, 0x0F, 0xF1, 0xB1, +0x10, 0x8E, 0x5A, 0x60, 0xDE, 0x79, 0x79, 0x4F, 0x27, 0x3C, 0x3E, 0xE3, 0x5F, 0x31, 0x20, 0x40, +0x00, 0x2A, 0xE6, 0x9F, 0x3F, 0xF4, 0x9A, 0xF5, 0xEF, 0xFD, 0x1B, 0xD9, 0xBB, 0xD6, 0x2F, 0x85, +0xF7, 0x97, 0x2F, 0x64, 0x98, 0x42, 0x24, 0x5D, 0x59, 0x5D, 0x7C, 0x88, 0x8F, 0x73, 0x79, 0x14, +0x47, 0x2C, 0x30, 0x6F, 0x6E, 0xC7, 0x9F, 0xFF, 0x86, 0xFF, 0xEC, 0x53, 0xFF, 0xAF, 0x7F, 0xB9, +0x80, 0x00, 0x01, 0xA8, 0xB8, 0xCF, 0xFE, 0xDA, 0xB7, 0x3E, 0x70, 0xF9, 0x6A, 0xF8, 0x85, 0xF5, +0xCB, 0xE1, 0x27, 0x0F, 0x17, 0xAA, 0xD7, 0xCE, 0xB0, 0x50, 0x5D, 0x7C, 0x88, 0x8F, 0xD3, 0x19, +0x5E, 0x60, 0xDE, 0xF1, 0x99, 0xD6, 0x5E, 0xFC, 0x09, 0x0B, 0xCC, 0x01, 0x01, 0x02, 0xB0, 0x9C, +0x21, 0xF2, 0xEA, 0xBB, 0x36, 0xC2, 0xBB, 0x2F, 0x5C, 0x0C, 0xEF, 0x48, 0x03, 0xD2, 0xE2, 0x42, +0x86, 0x27, 0x5B, 0xA8, 0x2E, 0x3E, 0xC4, 0xC7, 0xC9, 0x59, 0x60, 0x0E, 0x20, 0x40, 0x80, 0x95, +0x0E, 0x91, 0x07, 0x5F, 0x77, 0xF7, 0xCB, 0xF3, 0xF5, 0x21, 0xFD, 0x85, 0xEA, 0x77, 0xDF, 0x7F, +0x61, 0xCA, 0x17, 0x32, 0x14, 0x1F, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0x50, 0xF2, 0x85, +0xFF, 0xE9, 0xC1, 0xBF, 0x7A, 0xF9, 0x6A, 0x78, 0x5F, 0x71, 0x21, 0xC3, 0xB4, 0x50, 0xFD, 0xEA, +0x03, 0xD3, 0xB8, 0x90, 0xA1, 0xF8, 0x58, 0xF5, 0xF8, 0xB0, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x8C, +0xF5, 0xCF, 0xBE, 0xE7, 0xD5, 0xEB, 0xAF, 0xFB, 0xC9, 0xDA, 0x2F, 0x5C, 0xBC, 0x12, 0x7E, 0xB1, +0xBC, 0x50, 0xFD, 0x9E, 0x07, 0x2E, 0x9C, 0x71, 0x7D, 0x88, 0xF8, 0x58, 0xC5, 0xF8, 0xB0, 0xC0, +0x1C, 0x40, 0x80, 0x00, 0x4C, 0xE4, 0xB3, 0xFF, 0xF0, 0xC1, 0x07, 0x2E, 0xDF, 0x1B, 0xFE, 0x6E, +0xF9, 0x42, 0x86, 0xE9, 0x8A, 0xEA, 0x69, 0xC7, 0xAC, 0x93, 0xAF, 0x0F, 0x11, 0x1F, 0xAB, 0x18, +0x1F, 0xFB, 0x5B, 0x69, 0x9D, 0x47, 0xDB, 0x02, 0x73, 0x00, 0x01, 0x02, 0x70, 0xA2, 0x10, 0x79, +0xF5, 0x5D, 0x2F, 0x0F, 0x1F, 0xB8, 0x70, 0x31, 0x7C, 0x5F, 0x7E, 0xA2, 0x3C, 0xF1, 0x42, 0x75, +0xF1, 0xB1, 0x6A, 0xF1, 0x91, 0xD6, 0x79, 0x6C, 0x3D, 0x6B, 0x81, 0x39, 0x80, 0x00, 0x01, 0x38, +0x83, 0x67, 0x3E, 0xF4, 0xE0, 0x5B, 0x2F, 0x5E, 0x09, 0xEF, 0x2E, 0x16, 0xAA, 0xA7, 0xF5, 0x21, +0x69, 0xDB, 0xDE, 0xE3, 0x17, 0xAA, 0x8B, 0x8F, 0x55, 0x8A, 0x8F, 0x76, 0xB3, 0xBB, 0xB3, 0x95, +0x05, 0xE6, 0x00, 0x02, 0x04, 0x60, 0x2A, 0xD2, 0xFA, 0x90, 0x87, 0xFE, 0xF3, 0x5A, 0xBA, 0x90, +0xE1, 0x2F, 0x15, 0x0B, 0xD5, 0xD3, 0xFA, 0x90, 0x14, 0x22, 0xA3, 0x0B, 0xD5, 0xC5, 0xC7, 0xAA, +0xC4, 0x87, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x60, 0xA6, 0xD2, 0xFA, 0x90, 0xBB, 0x36, 0xC2, 0xCF, +0xAD, 0xAD, 0x87, 0x9F, 0x2E, 0x16, 0xAA, 0x5F, 0xBA, 0xBB, 0x96, 0x6F, 0xDD, 0xDB, 0x5D, 0xA8, +0x2E, 0x3E, 0x56, 0x21, 0x3E, 0x8A, 0x05, 0xE6, 0x7B, 0x2F, 0x0E, 0x5C, 0x48, 0xD0, 0x02, 0x73, +0x00, 0x01, 0x02, 0x30, 0x1B, 0x7F, 0xF4, 0x77, 0x1F, 0x7C, 0xC5, 0xFD, 0xD7, 0xF2, 0xF5, 0x21, +0xEF, 0x28, 0x6E, 0xBB, 0xF2, 0xB2, 0x5A, 0xB8, 0xEB, 0xE5, 0xB3, 0xBE, 0x90, 0xA1, 0xF8, 0x38, +0xEF, 0xF8, 0x18, 0xB7, 0xC0, 0xBC, 0x13, 0x22, 0x3F, 0xF5, 0xCA, 0xBF, 0xFE, 0xC7, 0x4F, 0xFA, +0x97, 0x01, 0x20, 0x40, 0x00, 0x66, 0x2A, 0x2D, 0x54, 0xBF, 0xFB, 0xE5, 0xE1, 0xC3, 0x6B, 0xEB, +0xB1, 0x7F, 0x21, 0xC3, 0x2B, 0xF7, 0xAD, 0xE5, 0x4F, 0xCD, 0x12, 0x1F, 0xCB, 0x15, 0x1F, 0xE3, +0x16, 0x98, 0x37, 0x77, 0xE2, 0x7F, 0xF5, 0x0D, 0xEF, 0xFC, 0xD4, 0x07, 0xFD, 0x4B, 0x00, 0x10, +0x20, 0x00, 0x73, 0xF5, 0xF9, 0x5F, 0x7F, 0xF5, 0x0F, 0xDC, 0x75, 0x5F, 0xF8, 0x60, 0xF9, 0x42, +0x86, 0x77, 0x7F, 0xCD, 0x5A, 0xFE, 0xF4, 0x2C, 0xF1, 0x51, 0xED, 0xF8, 0x18, 0xB7, 0xC0, 0xBC, +0xF3, 0xF2, 0xF7, 0xD7, 0xD6, 0xC3, 0xFB, 0x2D, 0x30, 0x07, 0x10, 0x20, 0x00, 0xE7, 0xE6, 0x7F, +0xFB, 0xAE, 0x6F, 0x59, 0x7F, 0xF8, 0x6F, 0x5E, 0x48, 0x0B, 0xD5, 0xDF, 0x5F, 0xBE, 0x90, 0x61, +0x7A, 0x5A, 0xD6, 0xF4, 0xAE, 0xA8, 0x2E, 0x3E, 0xE6, 0x15, 0x1F, 0xE3, 0x16, 0x98, 0x67, 0xED, +0xF0, 0x4F, 0xD6, 0x2E, 0x84, 0x5F, 0xB2, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x2C, 0x8C, 0xCF, 0xFE, +0xEA, 0xAB, 0x1E, 0xB8, 0xB2, 0x51, 0xFB, 0xB9, 0x4E, 0x88, 0xFC, 0x62, 0x71, 0x5B, 0xBA, 0x90, +0x61, 0x0A, 0x91, 0xB3, 0x5D, 0x51, 0x5D, 0x7C, 0xCC, 0x23, 0x3E, 0x2C, 0x30, 0x07, 0x10, 0x20, +0x00, 0x55, 0x0D, 0x91, 0x57, 0xDF, 0xF5, 0xF2, 0xDA, 0xBB, 0xCB, 0x0B, 0xD5, 0x53, 0x84, 0x4C, +0x7E, 0x21, 0x43, 0xF1, 0x31, 0xEF, 0xF8, 0xB0, 0xC0, 0x1C, 0x40, 0x80, 0x00, 0x54, 0xDE, 0x33, +0xBF, 0xF9, 0xEA, 0x81, 0x0B, 0x19, 0xA6, 0xF8, 0x28, 0x42, 0x44, 0x7C, 0x2C, 0x46, 0x7C, 0x58, +0x60, 0x0E, 0x20, 0x40, 0x00, 0x96, 0xCE, 0x17, 0x7E, 0xE3, 0xD5, 0x7F, 0xF5, 0xF2, 0xD5, 0xF0, +0xBE, 0xF2, 0x42, 0xF5, 0xAB, 0x5F, 0xBB, 0xC0, 0xEB, 0x43, 0x56, 0x20, 0x3E, 0x2C, 0x30, 0x07, +0x10, 0x20, 0x00, 0x4B, 0x2D, 0x2D, 0x54, 0x6F, 0xFC, 0xEC, 0x85, 0xC7, 0x6B, 0xB5, 0xF0, 0x58, +0x79, 0xA1, 0x7A, 0xDA, 0x31, 0xEB, 0xC2, 0xA5, 0x05, 0x3A, 0x15, 0x2F, 0x79, 0x7C, 0x58, 0x60, +0x0E, 0x20, 0x40, 0x00, 0x56, 0x4A, 0x5A, 0xA8, 0x7E, 0xF9, 0xDE, 0xDA, 0xAF, 0x5C, 0xBC, 0x12, +0x7E, 0xA2, 0xB8, 0x6D, 0x61, 0x16, 0xAA, 0x2F, 0x71, 0x7C, 0x1C, 0xB5, 0xC0, 0xBC, 0xF3, 0xF2, +0x9E, 0x4E, 0x78, 0x7C, 0xCC, 0x6F, 0x26, 0x80, 0x00, 0x01, 0x58, 0xF6, 0x10, 0x49, 0x0B, 0xD5, +0xD3, 0x15, 0xD5, 0xBF, 0x2F, 0x3F, 0x19, 0xF7, 0x2E, 0x64, 0x78, 0x6E, 0x0B, 0xD5, 0x97, 0x38, +0x3E, 0xC6, 0x2D, 0x30, 0x6F, 0xED, 0xC7, 0x9F, 0x7F, 0xC5, 0x7F, 0xFA, 0xA9, 0x7F, 0xE6, 0x37, +0x11, 0x40, 0x80, 0x00, 0xAC, 0x94, 0xB4, 0x50, 0xFD, 0xD2, 0xDD, 0xF9, 0xF5, 0x43, 0x5E, 0x9B, +0xFE, 0x9E, 0xD6, 0x87, 0xA4, 0xD9, 0x90, 0xCB, 0x57, 0xE7, 0x78, 0x7A, 0x5E, 0xD2, 0xF8, 0x18, +0xB7, 0xC0, 0xBC, 0x7D, 0x10, 0xFE, 0xCE, 0xD7, 0xFD, 0xD8, 0x27, 0xDF, 0xEF, 0x37, 0x0F, 0x40, +0x80, 0x00, 0xAC, 0xAC, 0x7F, 0xFA, 0x1D, 0xF5, 0xF5, 0xD7, 0x3F, 0x76, 0x39, 0x5D, 0xC8, 0xF0, +0x97, 0x8A, 0x85, 0xEA, 0x69, 0x5D, 0xC8, 0x3D, 0x5F, 0x33, 0x87, 0x85, 0xEA, 0x4B, 0x18, 0x1F, +0xC3, 0x0B, 0xCC, 0x93, 0xAC, 0x15, 0xFE, 0x8E, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x80, 0x92, 0xE2, +0x42, 0x86, 0x17, 0xD6, 0xC3, 0x4F, 0x97, 0x17, 0xAA, 0xDF, 0xF3, 0xB5, 0x17, 0x66, 0xB3, 0x3E, +0x64, 0xC9, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x29, 0x3C, 0xF3, 0x9B, 0xAF, 0xFA, +0xE6, 0xF5, 0xCB, 0xB5, 0xF7, 0x96, 0x2F, 0x64, 0x98, 0x16, 0xAA, 0xA7, 0x1D, 0xB3, 0xA6, 0xB6, +0x3E, 0x64, 0x89, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x14, 0x7C, 0xE9, 0xB7, 0x5E, +0xF5, 0x9D, 0x17, 0x2E, 0xD6, 0xFE, 0x87, 0xB5, 0xF5, 0xF0, 0x3D, 0xF9, 0x09, 0xBB, 0xB7, 0x50, +0xFD, 0xAE, 0x8D, 0x33, 0x56, 0xC8, 0x12, 0xC5, 0x87, 0x05, 0xE6, 0x00, 0x02, 0x04, 0x80, 0x29, +0xFB, 0xFC, 0xAF, 0xBF, 0xEA, 0x07, 0xEE, 0xBA, 0xAF, 0xF6, 0xC1, 0xF2, 0x85, 0x0C, 0x4F, 0xBD, +0x50, 0x7D, 0x49, 0xE2, 0xC3, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x0C, 0xA5, 0x85, 0xEA, 0x8F, +0xFC, 0x97, 0x97, 0x1F, 0xED, 0x44, 0xC8, 0xBB, 0xCB, 0xEB, 0x43, 0x52, 0x88, 0x4C, 0xBC, 0x50, +0x7D, 0x09, 0xE2, 0x63, 0xDC, 0x02, 0xF3, 0x4E, 0x8C, 0xFC, 0x37, 0x9D, 0x63, 0xF0, 0x3E, 0x0B, +0xCC, 0x01, 0x04, 0x08, 0x00, 0x53, 0x56, 0x2C, 0x54, 0x5F, 0xBF, 0x14, 0x7E, 0xB1, 0xB8, 0xED, +0xD2, 0xDD, 0xE9, 0x8A, 0xEA, 0x77, 0x58, 0xA8, 0x5E, 0xF1, 0xF8, 0x48, 0x4F, 0xB1, 0x4A, 0xE1, +0x51, 0x5E, 0x60, 0xDE, 0x6A, 0xC6, 0xFF, 0x75, 0xFD, 0x52, 0xED, 0x17, 0x2D, 0x30, 0x07, 0x10, +0x20, 0x00, 0xCC, 0x3E, 0x44, 0x5E, 0x7D, 0xCF, 0x03, 0xB5, 0x74, 0xFD, 0x90, 0xBF, 0x5C, 0xDC, +0x96, 0x2E, 0x62, 0x98, 0x66, 0x44, 0x46, 0x16, 0xAA, 0x57, 0x38, 0x3E, 0xC6, 0x2D, 0x30, 0xCF, +0xDA, 0xE1, 0x5F, 0xAF, 0x5D, 0x08, 0x7F, 0xDB, 0x02, 0x73, 0x00, 0x01, 0x02, 0xC0, 0x9C, 0x3D, +0xF3, 0x9B, 0xAF, 0x7A, 0xEB, 0xC5, 0x2B, 0xB5, 0xC7, 0xCB, 0x0B, 0xD5, 0x53, 0x84, 0xA4, 0x18, +0xA9, 0x7A, 0x7C, 0xA4, 0x05, 0xE6, 0x69, 0x5B, 0xDD, 0xD2, 0x3A, 0x0F, 0x0B, 0xCC, 0x01, 0x04, +0x08, 0x00, 0x8B, 0xE0, 0x4B, 0xFF, 0xF8, 0xD5, 0x7F, 0xA3, 0x7C, 0x21, 0xC3, 0xF4, 0x74, 0xAC, +0xBB, 0xEF, 0x5F, 0xCB, 0x9F, 0x9E, 0x55, 0xB5, 0xF8, 0x48, 0x0B, 0xCC, 0xB7, 0xBF, 0x92, 0xE5, +0xEB, 0x3D, 0x7A, 0xF2, 0x05, 0xE6, 0xFF, 0xF2, 0x57, 0x6E, 0x7F, 0xE0, 0x3F, 0xF9, 0xC4, 0x97, +0x5A, 0x7E, 0xDA, 0x00, 0x02, 0x04, 0x80, 0x05, 0x90, 0x2F, 0x54, 0xFF, 0xF9, 0xCB, 0x8F, 0xD7, +0x6A, 0xE1, 0xB1, 0xDA, 0x5A, 0x3C, 0x5C, 0xA8, 0xBE, 0x31, 0x87, 0x2B, 0xAA, 0x4F, 0x21, 0x3E, +0xD2, 0x85, 0x04, 0xB7, 0x9F, 0x6D, 0x5B, 0x60, 0x0E, 0x20, 0x40, 0x00, 0xA8, 0x92, 0xCF, 0xFC, +0x83, 0x6F, 0x79, 0xE0, 0xEE, 0xFB, 0xD7, 0xFE, 0xC1, 0xF0, 0x85, 0x0C, 0x53, 0x88, 0xCC, 0xE4, +0x8A, 0xEA, 0x67, 0x8C, 0x0F, 0x0B, 0xCC, 0x01, 0x04, 0x08, 0x00, 0xCB, 0x11, 0x22, 0xAF, 0xBE, +0xFA, 0xB5, 0x6B, 0xBF, 0x51, 0x5B, 0x0B, 0x0F, 0xE5, 0x27, 0xFC, 0xB5, 0xEE, 0x42, 0xF5, 0xF4, +0x32, 0xB5, 0x2B, 0xAA, 0x9F, 0x21, 0x3E, 0x2C, 0x30, 0x07, 0x10, 0x20, 0x00, 0x2C, 0xA1, 0x67, +0x7E, 0xF3, 0x2F, 0xBC, 0xF5, 0xD2, 0xDD, 0xF9, 0x8E, 0x59, 0xAF, 0x2D, 0x42, 0xE4, 0xEE, 0xFB, +0x2F, 0x9C, 0xEE, 0x42, 0x86, 0x53, 0x8A, 0x0F, 0x0B, 0xCC, 0x01, 0x04, 0x08, 0x00, 0x4B, 0xEC, +0x7F, 0xB9, 0xFE, 0x8D, 0xEB, 0x8D, 0x9F, 0xBD, 0xEB, 0x5D, 0x17, 0xEF, 0xAA, 0xBD, 0xAF, 0xF3, +0xD7, 0x8D, 0x74, 0x5B, 0xBA, 0xA2, 0xFA, 0xD5, 0x07, 0x66, 0xB9, 0x3E, 0x64, 0x34, 0x3E, 0x2C, +0x30, 0x07, 0x40, 0x80, 0x00, 0xAC, 0x90, 0xB4, 0x3E, 0xE4, 0xAE, 0x97, 0xD7, 0x7E, 0xEE, 0xC2, +0x7A, 0xED, 0xA7, 0xCB, 0x57, 0x54, 0x4F, 0x3B, 0x66, 0x5D, 0xB8, 0x34, 0xCD, 0xFF, 0x4B, 0x18, +0x8C, 0x0F, 0x0B, 0xCC, 0x01, 0x10, 0x20, 0x00, 0x2B, 0xEC, 0x99, 0xDF, 0xFC, 0x0B, 0xDF, 0xBC, +0x7E, 0xB9, 0xF6, 0xDE, 0xE1, 0x85, 0xEA, 0x57, 0xEE, 0xAD, 0x4D, 0x21, 0x44, 0xE2, 0x40, 0x78, +0xA4, 0xA7, 0x5A, 0x59, 0x60, 0x0E, 0x80, 0x00, 0x01, 0x20, 0x7C, 0xE5, 0x9F, 0xBE, 0xAA, 0xD1, +0x89, 0x84, 0x5F, 0x29, 0x2E, 0x64, 0x98, 0xA4, 0x00, 0x49, 0x0B, 0xD5, 0xD3, 0xCC, 0xC8, 0xC9, +0x77, 0xCD, 0x8A, 0xF9, 0x82, 0xF2, 0xE6, 0x4E, 0x0C, 0xCD, 0xDB, 0xD9, 0xC0, 0x8C, 0x47, 0x6C, +0x87, 0x7F, 0x5D, 0xB3, 0xC0, 0x1C, 0x40, 0x80, 0x38, 0x04, 0x00, 0xFC, 0xD9, 0x3F, 0x79, 0xD5, +0x5F, 0xB9, 0x70, 0x31, 0xBC, 0xBF, 0xF3, 0x66, 0xBD, 0x7C, 0x7B, 0x8A, 0x91, 0x22, 0x44, 0xD6, +0x3B, 0x6F, 0xA7, 0x05, 0xEC, 0xE5, 0x19, 0x92, 0xB4, 0x96, 0x23, 0x05, 0x47, 0xAB, 0xF3, 0x67, +0x5A, 0x50, 0x7E, 0xB0, 0x1B, 0xCB, 0xEB, 0x3B, 0x0A, 0x9F, 0xE9, 0xBC, 0xBC, 0xA7, 0x13, 0x1E, +0x1F, 0x76, 0xA4, 0x01, 0x10, 0x20, 0x00, 0xF4, 0xA5, 0x1D, 0xB3, 0xD6, 0x2F, 0xD7, 0x1E, 0xED, +0xC4, 0xC8, 0xF7, 0x9F, 0xE8, 0x13, 0xE3, 0xD8, 0xDD, 0xAE, 0x7E, 0x7B, 0xEF, 0xA5, 0xF8, 0xEB, +0xAF, 0xFC, 0x89, 0x4F, 0x3D, 0xE9, 0xC8, 0x02, 0x20, 0x40, 0x00, 0x38, 0x52, 0x5A, 0xAC, 0x7E, +0xF9, 0x65, 0xB5, 0x37, 0x5E, 0x58, 0xAF, 0x35, 0x6A, 0x6B, 0xA1, 0xB1, 0xB6, 0x1E, 0x1E, 0xBC, +0x53, 0x7C, 0xC4, 0x2C, 0xFC, 0xDB, 0xCE, 0x9B, 0x7F, 0xB8, 0x76, 0x21, 0xFC, 0x5F, 0x2F, 0x7D, +0xB1, 0xFD, 0xBB, 0xDF, 0xF4, 0xD8, 0x9F, 0x3C, 0xE7, 0x48, 0x02, 0x20, 0x40, 0x00, 0x38, 0xB1, +0xB4, 0x8D, 0xEF, 0x77, 0xFF, 0xF8, 0x95, 0x6F, 0x7E, 0xD9, 0x2B, 0xD6, 0xBE, 0x61, 0xF8, 0x7D, +0x9D, 0xD8, 0xF8, 0xE2, 0x37, 0x3D, 0xFA, 0xC7, 0xFF, 0xCE, 0x51, 0x02, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xC7, +0xFF, 0x2F, 0xC0, 0x00, 0x23, 0xF2, 0x4C, 0x01, 0x21, 0x26, 0xDF, 0x39, 0x00, 0x00, 0x00, 0x00, +0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; diff --git a/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino b/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino new file mode 100755 index 000000000..e9ff771e4 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Set_flag/Set_flag.ino @@ -0,0 +1,70 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +#include +#include "ESP32_MailClient.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + +//The Email Reading data object contains config and data that received +IMAPData imapData; + +void readEmail(); + +unsigned long lastTime = 0; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + Serial.println(); + + imapData.setLogin("imap.gmail.com", 993, "YOUR_EMAIL_ACCOUNT@gmail.com", "YOUR_EMAIL_PASSWORD"); + imapData.setFolder("INBOX"); + imapData.setDebug(true); + + //Set \Seen and \Answered to flags for message with UID 100 + MailClient.setFlag(imapData, 100, "\\Seen \\Answered"); + + //Add \Seen and \Answered to flags for message with UID 100 + //MailClient.addFlag(imapData, 100, "\\Seen \\Answered"); + + //Remove \Seen and \Answered from flags for message with UID 100 + //MailClient.removeFlag(imapData, 100, "\\Seen \\Answered"); +} + + + +void loop() +{ + +} + diff --git a/libesp32/ESP32-Mail-Client/examples/Time/Time.ino b/libesp32/ESP32-Mail-Client/examples/Time/Time.ino new file mode 100755 index 000000000..4726064d3 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/examples/Time/Time.ino @@ -0,0 +1,136 @@ +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +#include +#include "ESP32_MailClient.h" + +#define WIFI_SSID "YOUR_WIFI_SSID" +#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + Serial.println(); + + //Set Clock + //GMT offset (3 Hrs), Daylight offset (0 Hrs) + MailClient.Time.setClock(3, 0); + + Serial.println("Number of Days This Year (since January): " + String(MailClient.Time.getNumberOfDayThisYear())); + Serial.println("Day of Week Number: " + String(MailClient.Time.getDayOfWeek())); + Serial.println("Day of Week String: : " + String(MailClient.Time.getDayOfWeekString())); + Serial.println("Total seconds today: : " + String(MailClient.Time.getCurrentSecond())); + Serial.println(); +} + +void loop() +{ + + if (!MailClient.Time.clockReady) + return; + + //Print out current date and time + int d = MailClient.Time.getDay(); + int m = MailClient.Time.getMonth(); + int y = MailClient.Time.getYear(); + int hr = MailClient.Time.getHour(); + int min = MailClient.Time.getMin(); + int sec = MailClient.Time.getSec(); + Serial.print("Current Time (GMT+3): "); + Serial.print(d); + Serial.print("/"); + Serial.print(m); + Serial.print("/"); + Serial.print(y); + Serial.print(" "); + Serial.print(hr); + Serial.print(":"); + Serial.print(min); + Serial.print(":"); + Serial.println(sec); + + uint32_t todayFromMidnightTimestamp = MailClient.Time.getTimestamp(y, m, d, 0, 0, 0); + uint32_t currentTimestamp = MailClient.Time.getUnixTime(); + uint32_t totalSecondsFromMidnight = currentTimestamp - todayFromMidnightTimestamp; + + //Assumed we countdown until 15:00:00 everyday + uint8_t targetSec = 0; + uint8_t targetMin = 0; + uint8_t targetHr = 15; + uint32_t targetSecondsFromMidnight = targetHr * 60 * 60 + targetMin * 60 + targetSec; + + if (targetSecondsFromMidnight >= totalSecondsFromMidnight) + { + uint32_t diffSeconds = targetSecondsFromMidnight - totalSecondsFromMidnight; + int remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec; + MailClient.Time.getTimeFromSec(diffSeconds, remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec); + Serial.print("Everyday countdown until 15:00:00 is "); + Serial.print(remainHr); + Serial.print(" Hr, "); + Serial.print(remainMin); + Serial.print(" Min and "); + Serial.print(remainSec); + Serial.println(" Sec to go."); + } + else + { + Serial.println("Everyday countdown until 15:00:00 was passed."); + } + + //Assumed we countdown until 18/12/2019 8:30:45 + uint32_t targetTimestamp = MailClient.Time.getTimestamp(2019, 12, 18, 8, 30, 45); + if (targetTimestamp >= currentTimestamp) + { + uint32_t diffSeconds = targetTimestamp - currentTimestamp; + int remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec; + MailClient.Time.getTimeFromSec(diffSeconds, remainYrs, remainMonths, remainDays, remainHr, remainMin, remainSec); + Serial.print("One time countdown until 18/12/2019 8:30:45 is "); + Serial.print(remainYrs); + Serial.print(" Years, "); + Serial.print(remainMonths); + Serial.print(" Months, "); + Serial.print(remainDays); + Serial.print(" Days, "); + Serial.print(remainHr); + Serial.print(" Hr, "); + Serial.print(remainMin); + Serial.print(" Min and "); + Serial.print(remainSec); + Serial.println(" Sec to go."); + } + else + { + Serial.println("One time countdown until 18/12/2019 8:30:45 was finished."); + } + Serial.println(); + + delay(1000); +} + diff --git a/libesp32/ESP32-Mail-Client/keywords.txt b/libesp32/ESP32-Mail-Client/keywords.txt new file mode 100755 index 000000000..81b7ee9b2 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/keywords.txt @@ -0,0 +1,166 @@ +####################################### +# Syntax Coloring Map ESP32-Mail-Client +####################################### + +####################################### +# Classes (KEYWORD1) +####################################### + +IMAPData KEYWORD1 +SMTPData KEYWORD1 +attachmentData KEYWORD1 +SendStatus KEYWORD1 +messageBodyData KEYWORD1 +DownloadProgress KEYWORD1 +MessageData KEYWORD1 + +TIME KEYWORD1 + + +################################## +# Methods and Functions (KEYWORD2) +################################## + +sendMail KEYWORD2 +readMail KEYWORD2 +smtpErrorReason KEYWORD2 +imapErrorReason KEYWORD2 +sdBegin KEYWORD2 +setFlag KEYWORD2 +addFlag KEYWORD2 +removeFlag KEYWORD2 + + +setClock KEYWORD2 +getUnixTime KEYWORD2 +getTimestamp KEYWORD2 +getYear KEYWORD2 +getMonth KEYWORD2 +getDay KEYWORD2 +getDayOfWeek KEYWORD2 +getDayOfWeekString KEYWORD2 +getHour KEYWORD2 +getMin KEYWORD2 +getSec KEYWORD2 +getNumberOfDayThisYear KEYWORD2 +getTotalDays KEYWORD2 +dayofweek KEYWORD2 +getCurrentSecond KEYWORD2 +getCurrentTimestamp KEYWORD2 +getTimeFromSec KEYWORD2 + +######################################### +# Methods for IMAP Data object (KEYWORD2) +######################################### + +setLogin KEYWORD2 +setSTARTTLS KEYWORD2 +setDebug KEYWORD2 +setFolder KEYWORD2 +setMessageBufferSize KEYWORD2 +setAttachmentSizeLimit KEYWORD2 +setSearchCriteria KEYWORD2 +setSaveFilePath KEYWORD2 +setFechUID KEYWORD2 +setDownloadAttachment KEYWORD2 +setHTMLMessage KEYWORD2 +setTextMessage KEYWORD2 +setSearchLimit KEYWORD2 +setRecentSort KEYWORD2 +setReadCallback KEYWORD2 +setDownloadReport KEYWORD2 +isHeaderOnly KEYWORD2 +getFrom KEYWORD2 +getFromCharset KEYWORD2 +getTo KEYWORD2 +getToCharset KEYWORD2 +getCC KEYWORD2 +getCCCharset KEYWORD2 +getSubject KEYWORD2 +getSubjectCharset KEYWORD2 +getHTMLMessage KEYWORD2 +getTextMessage KEYWORD2 +getHTMLMessgaeCharset KEYWORD2 +getTextMessgaeCharset KEYWORD2 +getDate KEYWORD2 +getUID KEYWORD2 +getNumber KEYWORD2 +getMessageID KEYWORD2 +getAcceptLanguage KEYWORD2 +getContentLanguage KEYWORD2 +isFetchMessageFailed KEYWORD2 +getFetchMessageFailedReason KEYWORD2 +isDownloadAttachmentFailed KEYWORD2 +getDownloadAttachmentFailedReason KEYWORD2 +isDownloadMessageFailed KEYWORD2 +getDownloadMessageFailedReason KEYWORD2 +saveHTMLMessage KEYWORD2 +saveTextMessage KEYWORD2 +getFolderCount KEYWORD2 +getFolder KEYWORD2 +getFlagCount KEYWORD2 +getFlag KEYWORD2 +totalMessages KEYWORD2 +searchCount KEYWORD2 +availableMessages KEYWORD2 +getAttachmentCount KEYWORD2 +getAttachmentFileName KEYWORD2 +getAttachmentName KEYWORD2 +getAttachmentFileSize KEYWORD2 +getAttachmentCreationDate KEYWORD2 +getAttachmentType KEYWORD2 +empty KEYWORD2 +clearMessageData KEYWORD2 + +######################################### +# Methods for SMTP Data object (KEYWORD2) +######################################### + +setSender KEYWORD2 +getFromName KEYWORD2 +getSenderEmail KEYWORD2 +setPriority KEYWORD2 +getPriority KEYWORD2 +addRecipient KEYWORD2 +removeRecipient KEYWORD2 +clearRecipient KEYWORD2 +getRecipient KEYWORD2 +recipientCount KEYWORD2 +setSubject KEYWORD2 +getSubject KEYWORD2 +setMessage KEYWORD2 +getMessage KEYWORD2 +htmlFormat KEYWORD2 +addCC KEYWORD2 +removeCC KEYWORD2 +clearCC KEYWORD2 +getCC KEYWORD2 +ccCount KEYWORD2 +addBCC KEYWORD2 +removeBCC KEYWORD2 +clearBCC KEYWORD2 +getBCC KEYWORD2 +bccCount KEYWORD2 +addAttachData KEYWORD2 +removeAttachData KEYWORD2 +attachDataCount KEYWORD2 +addAttachFile KEYWORD2 +removeAttachFile KEYWORD2 +clearAttachData KEYWORD2 +clearAttachFile KEYWORD2 +clearAttachment KEYWORD2 +attachFileCount KEYWORD2 +setSendCallback KEYWORD2 + + +############################################################ +# Functions for ReadStatus and SendStatus classes (KEYWORD2) +############################################################ + +SendStatus KEYWORD2 +info KEYWORD2 +success KEYWORD2 +ReadStatus KEYWORD2 +status KEYWORD2 + +clockReady KEYWORD3 \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/library.properties b/libesp32/ESP32-Mail-Client/library.properties new file mode 100755 index 000000000..6ff993e8b --- /dev/null +++ b/libesp32/ESP32-Mail-Client/library.properties @@ -0,0 +1,17 @@ +name=ESP32 Mail Client + +version=2.1.4 + +author=Mobizt + +maintainer=Mobizt + +sentence=Mail Client Arduino Library for ESP32 + +paragraph=This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + +category=Communication + +url=https://github.com/mobizt/ESP32-Mail-Client + +architectures=esp32 diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg new file mode 100755 index 000000000..d4c62c865 Binary files /dev/null and b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.jpg differ diff --git a/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png new file mode 100755 index 000000000..459238782 Binary files /dev/null and b/libesp32/ESP32-Mail-Client/media/images/esp32-mail.png differ diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp new file mode 100755 index 000000000..39df83f7e --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.cpp @@ -0,0 +1,200 @@ +/* + * Customized version of ESP32 HTTPClient Library. + * Allow custom header and payload with STARTTLS support + * + * v 1.0.0 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP32MailHTTPClient_CPP +#define ESP32MailHTTPClient_CPP + +#ifdef ESP32 + +#include "ESP32MailHTTPClient.h" + +class TransportTraits +{ +public: + virtual ~TransportTraits() {} + + virtual std::unique_ptr create() + { + return std::unique_ptr(new WiFiClient()); + } + + virtual bool + verify(WiFiClient &client, const char *host, bool starttls, DebugMsgCallback cb) + { + return true; + } +}; + +class TLSTraits : public TransportTraits +{ +public: + TLSTraits(const char *CAcert, const char *clicert = nullptr, const char *clikey = nullptr) : _cacert(CAcert), _clicert(clicert), _clikey(clikey) {} + + std::unique_ptr create() override + { + return std::unique_ptr(new WiFiClientSecureESP32()); + } + + bool verify(WiFiClient &client, const char *host, bool starttls, DebugMsgCallback cb) override + { + WiFiClientSecureESP32 &wcs = static_cast(client); + wcs.setCACert(_cacert); + wcs.setCertificate(_clicert); + wcs.setPrivateKey(_clikey); + wcs.setSTARTTLS(starttls); + wcs.setDebugCB(cb); + return true; + } + +protected: + const char *_cacert; + const char *_clicert; + const char *_clikey; +}; + +ESP32MailHTTPClient::ESP32MailHTTPClient() {} + +ESP32MailHTTPClient::~ESP32MailHTTPClient() +{ + if (_client) + _client->stop(); +} + +bool ESP32MailHTTPClient::begin(const char *host, uint16_t port, const char *uri, const char *CAcert) +{ + transportTraits.reset(nullptr); + + _host = host; + _port = port; + _uri = uri; + transportTraits = TransportTraitsPtr(new TLSTraits(CAcert)); + return true; +} + +bool ESP32MailHTTPClient::connected() +{ + if (_client) + return ((_client->available() > 0) || _client->connected()); + return false; +} + +bool ESP32MailHTTPClient::sendHeader(const char *header) +{ + if (!connected()) + return false; + return (_client->write(header, strlen(header)) == strlen(header)); +} + +int ESP32MailHTTPClient::sendRequest(const char *header, const char *payload) +{ + size_t size = strlen(payload); + if (strlen(header) > 0) + { + if (!connect()) + return HTTPC_ERROR_CONNECTION_REFUSED; + if (!sendHeader(header)) + return HTTPC_ERROR_SEND_HEADER_FAILED; + } + if (size > 0) + if (_client->write(&payload[0], size) != size) + return HTTPC_ERROR_SEND_PAYLOAD_FAILED; + + return 0; +} + +WiFiClient *ESP32MailHTTPClient::getStreamPtr(void) +{ + if (connected()) + return _client.get(); + return nullptr; +} + +bool ESP32MailHTTPClient::connect(void) +{ + if (connected()) + { + while (_client->available() > 0) + _client->read(); + return true; + } + + if (!transportTraits) + return false; + + _client = transportTraits->create(); + + if (!transportTraits->verify(*_client, _host.c_str(), false, _debugCallback)) + { + _client->stop(); + return false; + } + + if (!_client->connect(_host.c_str(), _port)) + return false; + + return connected(); +} + +bool ESP32MailHTTPClient::connect(bool starttls) +{ + if (connected()) + { + while (_client->available() > 0) + _client->read(); + return true; + } + + if (!transportTraits) + return false; + + _client = transportTraits->create(); + + if (!transportTraits->verify(*_client, _host.c_str(), starttls, _debugCallback)) + { + _client->stop(); + return false; + } + + if (!_client->connect(_host.c_str(), _port)) + return false; + + return connected(); +} + +void ESP32MailHTTPClient::setDebugCallback(DebugMsgCallback cb) +{ + _debugCallback = std::move(cb); +} + +#endif //ESP32 + +#endif //ESP32MailHTTPClient_CPP diff --git a/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h new file mode 100755 index 000000000..5df2c8d2d --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32MailHTTPClient.h @@ -0,0 +1,107 @@ +/* + * Customized version of ESP32 HTTPClient Library. + * Allow custom header and payload with STARTTLS support + * + * v 1.0.0 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP32MailHTTPClient_H +#define ESP32MailHTTPClient_H + +#ifdef ESP32 + +#include +#include + +#include +#include "WiFiClientSecureESP32.h" + +class ESP32MailHTTPClient : public HTTPClient +{ +public: + ESP32MailHTTPClient(); + ~ESP32MailHTTPClient(); + + /** + * Initialization of new http connection. + * \param host - Host name without protocols. + * \param port - Server's port. + * \param uri - The URI of resource. + * \param CAcert - The Base64 encode root certificate string + * \return True as default. + * If no certificate string provided, use (const char*)NULL to CAcert param + */ + bool begin(const char *host, uint16_t port, const char *uri, const char *CAcert); + + /** + * Check the http connection status. + * \return True if connected. + */ + bool connected(); + + /** + * Establish http connection if header provided and send it, send payload if provided. + * \param header - The header string (constant chars array). + * \param payload - The payload string (constant chars array), optional. + * \return http status code, Return zero if new http connection and header and/or payload sent + * with no error or no header and payload provided. If obly payload provided, no new http connection was established. + */ + int sendRequest(const char *header, const char *payload); + + /** + * Send extra header without making new http connection (if sendRequest has been called) + * \param header - The header string (constant chars array). + * \return True if header sending success. + * Need to call sendRequest with header first. + */ + bool sendHeader(const char *header); + + /** + * Get the WiFi client pointer. + * \return WiFi client pointer. + */ + WiFiClient *getStreamPtr(void); + + uint16_t tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; + bool connect(void); + bool connect(bool starttls); + void setDebugCallback(DebugMsgCallback cb); + +protected: + TransportTraitsPtr transportTraits; + std::unique_ptr _client; + DebugMsgCallback _debugCallback = NULL; + + std::string _host = ""; + std::string _uri = ""; + uint16_t _port = 0; +}; + +#endif //ESP32 + +#endif //ESP32MailHTTPClient_H diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp new file mode 100755 index 000000000..8e37b566b --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.cpp @@ -0,0 +1,191 @@ +/* + * ESP32 Internet Time Helper Arduino Library v 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning 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 ESP32TimeHelper_CPP +#define ESP32TimeHelper_CPP + +#ifdef ESP32 + +#include "ESP32TimeHelper.h" + +ESP32TimeHelper::ESP32TimeHelper() +{ +} +uint32_t ESP32TimeHelper::getUnixTime() +{ + uint32_t utime = (msec_time_diff + millis()) / 1000; + return utime; +} + +time_t ESP32TimeHelper::getTimestamp(int year, int mon, int date, int hour, int mins, int sec) +{ + struct tm timeinfo; + timeinfo.tm_year = year - 1900; + timeinfo.tm_mon = mon - 1; + timeinfo.tm_mday = date; + timeinfo.tm_hour = hour; + timeinfo.tm_min = mins; + timeinfo.tm_sec = sec; + time_t ts = mktime(&timeinfo); + return ts; +} + +bool ESP32TimeHelper::setClock(float gmtOffset, float daylightOffset) +{ + TZ = gmtOffset; + DST_MN = daylightOffset; + configTime((TZ)*3600, (DST_MN)*60, "pool.ntp.org", "time.nist.gov", NULL); + + now = time(nullptr); + int cnt = 0; + while (now < 8 * 3600 * 2 && cnt < 20) + { + delay(50); + now = time(nullptr); + cnt++; + } + + uint64_t tmp = now; + tmp = tmp * 1000; + msec_time_diff = tmp - millis(); + + getLocalTime(&timeinfo); + + clockReady = now > 8 * 3600 * 2; + return clockReady; +} + +int ESP32TimeHelper::getYear() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_year + 1900; +} +int ESP32TimeHelper::getMonth() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_mon + 1; +} +int ESP32TimeHelper::getDay() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_mday; +} + +int ESP32TimeHelper::getDayOfWeek() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_wday; +} +String ESP32TimeHelper::getDayOfWeekString() +{ + getLocalTime(&timeinfo); + return dow[timeinfo.tm_wday]; +} + +int ESP32TimeHelper::getHour() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_hour; +} + +int ESP32TimeHelper::getMin() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_min; +} +int ESP32TimeHelper::getSec() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_sec; +} +int ESP32TimeHelper::getNumberOfDayThisYear() +{ + getLocalTime(&timeinfo); + return timeinfo.tm_yday + 1; +} + +int ESP32TimeHelper::totalDays(int y, int m, int d) +{ + static char daytab[2][13] = + { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; + int daystotal = d; + for (int year = 1; year <= y; year++) + { + int max_month = (year < y ? 12 : m - 1); + int leap = (year % 4 == 0); + if (year % 100 == 0 && year % 400 != 0) + leap = 0; + for (int month = 1; month <= max_month; month++) + { + daystotal += daytab[leap][month]; + } + } + return daystotal; +} +int ESP32TimeHelper::getTotalDays(int year, int month, int day) +{ + return totalDays(year, month, day) - totalDays(1970, 1, 1); +} + +int ESP32TimeHelper::dayofWeek(int year, int month, int day) /* 1 <= m <= 12, y > 1752 (in the U.K.) */ +{ + static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + year -= month < 3; + return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7; +} + +int ESP32TimeHelper::getCurrentSecond() +{ + return (timeinfo.tm_hour * 3600) + (timeinfo.tm_min * 60) + timeinfo.tm_sec; +} +uint64_t ESP32TimeHelper::getCurrentTimestamp() +{ + return now; +} +void ESP32TimeHelper::getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec) +{ + int _yrs = secCount / (365 * 24 * 3600); + secCount = secCount - _yrs * (365 * 24 * 3600); + yrs = _yrs; + int _months = secCount / (30* 24 * 3600); + secCount = secCount - _months * (30 * 24 * 3600); + months = _months; + int _days = secCount / (24 * 3600); + secCount = secCount - _days * (24 * 3600); + days = _days; + int _hr = secCount / 3600; + secCount = secCount - _hr * 3600; + hr = _hr; + int _min = secCount / 60; + secCount = secCount - _min * 60; + min = _min; + sec = secCount; +} + +#endif //ESP32 + +#endif //ESP32TimeHelper_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h new file mode 100755 index 000000000..e4feff648 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32TimeHelper.h @@ -0,0 +1,73 @@ +/* + * ESP32 Internet Time Helper Arduino Library v 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning 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 ESP32TimeHelper_H +#define ESP32TimeHelper_H + +#ifdef ESP32 + + +#include +#include +#include + +class ESP32TimeHelper +{ +public: + ESP32TimeHelper(); + bool clockReady = false; + bool setClock(float gmtOffset, float daylightOffset); + uint32_t getUnixTime(); + time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); + int getYear(); + int getMonth(); + int getDay(); + int getDayOfWeek(); + String getDayOfWeekString(); + int getHour(); + int getMin(); + int getSec(); + int getNumberOfDayThisYear(); + int getTotalDays(int year, int month, int day); + int dayofWeek(int year, int month, int day); + int getCurrentSecond(); + uint64_t getCurrentTimestamp(); + void getTimeFromSec(int secCount, int &yrs, int &months, int &days, int &hr, int &min, int &sec); + +private: + time_t now; + uint64_t msec_time_diff = 0; + struct tm timeinfo; + float TZ = 0.0; + float DST_MN = 0.0; + + bool setClock(); + int totalDays(int y, int m, int d); + const char *dow[20] = {"sunday", "monday", "tuesday", "wednesday", "thurseday", "friday", "saturday"}; +}; + +#endif //ESP32 + +#endif //ESP32TimeHelper_H diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp new file mode 100755 index 000000000..85cb0663e --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.cpp @@ -0,0 +1,4875 @@ +/* + *Mail Client Arduino Library for ESP32, version 2.1.4 + * + * April 12, 2020 + * + * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + * + * The library supports all ESP32 MCU based modules. + * + * The MIT License (MIT) + * Copyright (c) 2019 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 ESP32_MailClient_CPP +#define ESP32_MailClient_CPP + +#ifdef ESP32 + +#include "ESP32_MailClient.h" + +struct ESP32_MailClient::IMAP_COMMAND_TYPE +{ + static const uint8_t LOGIN = 0; + static const uint8_t LIST = 1; + static const uint8_t SELECT = 2; + static const uint8_t EXAMINE = 3; + static const uint8_t STATUS = 4; + static const uint8_t SEARCH = 5; + static const uint8_t FETCH_BODY_HEADER = 6; + static const uint8_t FETCH_BODY_MIME = 7; + static const uint8_t FETCH_BODY_TEXT = 8; + static const uint8_t FETCH_BODY_ATTACHMENT = 9; + static const uint8_t LOGOUT = 10; +}; + +struct ESP32_MailClient::IMAP_HEADER_TYPE +{ + static const uint8_t FROM = 1; + static const uint8_t TO = 2; + static const uint8_t CC = 3; + static const uint8_t SUBJECT = 4; + static const uint8_t DATE = 5; + static const uint8_t MSG_ID = 6; + static const uint8_t CONT_LANG = 7; + static const uint8_t ACCEPT_LANG = 8; +}; + + + +bool ESP32_MailClient::readMail(IMAPData &imapData) +{ + + std::string buf; + std::string command = "$"; + + size_t mailIndex = 0; + int messageDataIndex = 0; + int partID = 1; + int _partID = 1; + bool res = false; + bool _res = false; + bool starttls = imapData._starttls; + bool connected = false; + + int bufSize = 50; + + char *_val = new char[bufSize]; + char *_part = new char[bufSize]; + + unsigned long dataTime = 0; + + int count = 0; + + imapData._net->setDebugCallback(NULL); + + if (imapData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_225); + ESP32MailDebug(imapData._host.c_str()); + ESP32MailDebug(String(imapData._port).c_str()); + } + + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_50; + imapData._cbData._status = ESP32_MAIL_STR_51; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + imapData._net->setDebugCallback(ESP32MailDebug); + + if (imapData._rootCA.size() > 0) + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)imapData._rootCA.front()); + else + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + while (!imapData._net->connected() && count < 10) + { + + count++; + + if (!imapData._net->connect(starttls)) + { + + _imapStatus = IMAP_STATUS_SERVER_CONNECT_FAILED; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + else + { + break; + } + } + + if (!imapData._net->connect(starttls)) + { + goto out; + } + + connected = true; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_54; + imapData._cbData._status = ESP32_MAIL_STR_55; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_228); + + //Don't expect handshake from some servers + dataTime = millis(); + + while (imapData._net->connected() && !imapData._net->getStreamPtr()->available() && millis() - 500 < dataTime) + delay(0); + + if (imapData._net->connected() && imapData._net->getStreamPtr()->available()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData.clearMessageData(); + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_56; + imapData._cbData._status = ESP32_MAIL_STR_57; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_229); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_130); + imapData._net->getStreamPtr()->print(imapData._loginEmail.c_str()); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_131); + imapData._net->getStreamPtr()->println(imapData._loginPassword.c_str()); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LOGIN)) + { + _imapStatus = IMAP_STATUS_LOGIN_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._fetchUID.length() > 0) + imapData._headerOnly = false; + else + imapData._headerOnly = true; + + if (imapData._headerOnly) + { + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_58; + imapData._cbData._status = ESP32_MAIL_STR_59; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_230); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_133); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LIST)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + imapData._cbData.empty(); + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_60; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._folders.size(); i++) + { + imapData._cbData._info = imapData._folders[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_61 + imapData._currentFolder + ESP32_MAIL_STR_97; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_231); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_135); + imapData._net->getStreamPtr()->print(imapData._currentFolder.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_136); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::EXAMINE)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._headerOnly) + { + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_62 + imapData._nextUID; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_63; + memset(_val, 0, bufSize); + itoa(imapData._totalMessage, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_64; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._flag.size(); i++) + { + imapData._cbData._info = imapData._flag[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_65; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_66; + imapData._cbData._status = ESP32_MAIL_STR_67; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + imapData._msgNum.clear(); + imapData._uidSearch = false; + imapData._msgID.clear(); + imapData._contentLanguage.clear(); + imapData._acceptLanguage.clear(); + imapData._attachmentCount.clear(); + imapData._totalAttachFileSize.clear(); + imapData._downloadedByte.clear(); + imapData._error.clear(); + imapData._errorMsg.clear(); + imapData._searchCount = 0; + + if (imapData._headerOnly) + { + + if (imapData._searchCriteria != "") + { + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_232); + + if (imapData._searchCriteria.find(ESP32_MAIL_STR_137) != std::string::npos) + { + imapData._uidSearch = true; + command += ESP32_MAIL_STR_138; + } + command += ESP32_MAIL_STR_139; + + for (size_t i = 0; i < imapData._searchCriteria.length(); i++) + { + if (imapData._searchCriteria[i] != ' ' && imapData._searchCriteria[i] != '\r' && imapData._searchCriteria[i] != '\n' && imapData._searchCriteria[i] != '$') + buf.append(1, imapData._searchCriteria[i]); + + if (imapData._searchCriteria[i] == ' ') + { + if ((imapData._uidSearch && buf == ESP32_MAIL_STR_140) || (imapData._unseen && buf.find(ESP32_MAIL_STR_224) != std::string::npos)) + buf.clear(); + + if (buf != ESP32_MAIL_STR_141 && buf != "") + { + command += ESP32_MAIL_STR_131; + command += buf; + } + + buf.clear(); + } + } + + if (imapData._unseen && imapData._searchCriteria.find(ESP32_MAIL_STR_223) == std::string::npos) + command += ESP32_MAIL_STR_223; + + if (buf.length() > 0) + { + command += ESP32_MAIL_STR_131; + command += buf; + } + + imapData._net->getStreamPtr()->println(command.c_str()); + + std::string().swap(command); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::SEARCH, 1)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_68; + memset(_val, 0, bufSize); + itoa(imapData._emailNumMax, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + if (imapData._msgNum.size() > 0) + { + + imapData._cbData._info = ESP32_MAIL_STR_69; + memset(_val, 0, bufSize); + itoa(imapData._searchCount, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_70; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + imapData._cbData._info = ESP32_MAIL_STR_71; + memset(_val, 0, bufSize); + itoa(imapData._msgNum.size(), _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_70; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + else + { + imapData._cbData._info = ESP32_MAIL_STR_72; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + } + else + { + + imapData._msgNum.push_back(imapData._totalMessage); + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_73; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + } + else + { + + imapData._msgNum.push_back(atoi(imapData._fetchUID.c_str())); + } + + for (int i = 0; i < imapData._messageDataInfo.size(); i++) + imapData._messageDataInfo[i].clear(); + + imapData._messageDataInfo.clear(); + + for (int i = 0; i < imapData._msgNum.size(); i++) + { + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_233); + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_74; + memset(_val, 0, bufSize); + itoa(i + 1, _val, 10); + imapData._cbData._info += _val; + + imapData._cbData._status = ""; + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._cbData._info += ESP32_MAIL_STR_75; + else + imapData._cbData._info += ESP32_MAIL_STR_76; + + memset(_val, 0, bufSize); + itoa(imapData._msgNum[i], _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ESP32_MAIL_STR_77; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._date.push_back(std::string()); + imapData._subject.push_back(std::string()); + imapData._subject_charset.push_back(std::string()); + imapData._from.push_back(std::string()); + imapData._from_charset.push_back(std::string()); + imapData._to.push_back(std::string()); + imapData._to_charset.push_back(std::string()); + imapData._cc.push_back(std::string()); + imapData._attachmentCount.push_back(0); + imapData._totalAttachFileSize.push_back(0); + imapData._downloadedByte.push_back(0); + imapData._messageDataCount.push_back(0); + imapData._error.push_back(false); + imapData._errorMsg.push_back(std::string()); + imapData._cc_charset.push_back(std::string()); + imapData._msgID.push_back(std::string()); + imapData._acceptLanguage.push_back(std::string()); + imapData._contentLanguage.push_back(std::string()); + + std::vector d = std::vector(); + + imapData._messageDataInfo.push_back(d); + + std::vector().swap(d); + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_144); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_HEADER, 0, mailIndex)) + { + if (imapData._headerOnly) + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + else + _imapStatus = IMAP_STATUS_BAD_COMMAND; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (!imapData._headerOnly) + { + + messageDataIndex = 0; + partID = 1; + _partID = 1; + res = false; + _res = false; + + do + { + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(partID); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_148); + + memset(_part, 0, bufSize); + memset(_val, 0, bufSize); + itoa(partID, _val, 10); + strcpy(_part, _val); + res = waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_MIME, 0, mailIndex, messageDataIndex, _part); + if (res) + { + + if (imapData._messageDataInfo[mailIndex].size() < messageDataIndex + 1) + { + messageBodyData b; + imapData._messageDataInfo[mailIndex].push_back(b); + b.empty(); + imapData._messageDataCount[mailIndex] = imapData._messageDataInfo[mailIndex].size(); + } + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == "") + continue; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType.find(ESP32_MAIL_STR_149) != std::string::npos) + { + do + { + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(partID); + imapData._net->getStreamPtr()->print("."); + imapData._net->getStreamPtr()->print(_partID); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_148); + + memset(_part, 0, bufSize); + memset(_val, 0, bufSize); + itoa(partID, _val, 10); + strcpy(_part, _val); + strcat(_part, "."); + memset(_val, 0, bufSize); + itoa(_partID, _val, 10); + strcat(_part, _val); + _res = waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_MIME, 0, mailIndex, messageDataIndex, _part); + + if (_res) + { + messageDataIndex++; + _partID++; + } + + } while (_res); + } + else + { + messageDataIndex++; + } + partID++; + } + + } while (res); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg || imapData._downloadAttachment) + { + + if (!_sdOk) + { + if (imapData._storageType == MailClientStorageType::SD) + { + _sdOk = sdTest(); + if (_sdOk) + if (!SD.exists(imapData._savePath.c_str())) + createDirs(imapData._savePath); + } + else if (imapData._storageType == MailClientStorageType::SPIFFS) + _sdOk = SPIFFS.begin(true); + } + } + + if (imapData._messageDataInfo[mailIndex].size() > 0) + { + if (imapData._attachmentCount[mailIndex] > 0 && imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_78; + memset(_val, 0, bufSize); + itoa(imapData._attachmentCount[mailIndex], _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_79; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (int j = 0; j < imapData._messageDataInfo[mailIndex].size(); j++) + { + if (imapData._messageDataInfo[mailIndex][j]._disposition == ESP32_MAIL_STR_153) + { + imapData._cbData._info = imapData._messageDataInfo[mailIndex][j]._filename; + imapData._cbData._info += ESP32_MAIL_STR_83; + memset(_val, 0, bufSize); + itoa(imapData._messageDataInfo[mailIndex][j]._size, _val, 10); + imapData._cbData._info += _val; + imapData._cbData._info += ESP32_MAIL_STR_82; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + if (imapData._downloadAttachment && _sdOk) + { + imapData._cbData._info = ESP32_MAIL_STR_80; + imapData._cbData._status = ESP32_MAIL_STR_81; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + } + + for (int j = 0; j < imapData._messageDataInfo[mailIndex].size(); j++) + { + + if (imapData._messageDataInfo[mailIndex][j]._disposition == "") + { + + if (!imapData._textFormat && imapData._messageDataInfo[mailIndex][j]._contentType != ESP32_MAIL_STR_154) + continue; + + if (!imapData._htmlFormat && imapData._messageDataInfo[mailIndex][j]._contentType != ESP32_MAIL_STR_155) + continue; + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(imapData._messageDataInfo[mailIndex][j]._part.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_156); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_TEXT, imapData._message_buffer_size, mailIndex, j)) + { + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + } + else if (imapData._messageDataInfo[mailIndex][j]._disposition == ESP32_MAIL_STR_153 && _sdOk) + { + + if (imapData._downloadAttachment) + { + if (imapData._messageDataInfo[mailIndex][j]._size <= imapData._attacement_max_size) + { + + if (_sdOk) + { + + if (j < imapData._messageDataInfo[mailIndex].size() - 1) + if (imapData._messageDataInfo[mailIndex][j + 1]._size > imapData._attacement_max_size) + imapData._downloadedByte[mailIndex] += imapData._messageDataInfo[mailIndex][j + 1]._size; + + if (imapData._uidSearch || imapData._fetchUID.length() > 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_142); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_143); + + imapData._net->getStreamPtr()->print(imapData._msgNum[i]); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_147); + imapData._net->getStreamPtr()->print(imapData._messageDataInfo[mailIndex][j]._part.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_156); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT, imapData._message_buffer_size, mailIndex, j)) + { + _imapStatus = IMAP_STATUS_IMAP_RESPONSE_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + + delay(0); + } + } + else + { + if (j == imapData._messageDataInfo[mailIndex].size() - 1) + imapData._downloadedByte[mailIndex] += imapData._messageDataInfo[mailIndex][j]._size; + } + } + } + } + } + + if (imapData._storageType == MailClientStorageType::SD) + { + if (_sdOk) + SD.end(); + } + else if (imapData._storageType == MailClientStorageType::SPIFFS) + { + if (_sdOk) + SPIFFS.end(); + } + + _sdOk = false; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_84; + memset(_val, 0, bufSize); + itoa(ESP.getFreeHeap(), _val, 10); + imapData._cbData._info += _val; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + mailIndex++; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_85; + imapData._cbData._status = ESP32_MAIL_STR_86; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_234); + + if (imapData._net->connected()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_146); + + if (!waitIMAPResponse(imapData, 0)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_98; + imapData._cbData._status = ESP32_MAIL_STR_96; + imapData._cbData._success = true; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_235); + + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->stop(); + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(command); + std::string().swap(buf); + + return true; + +out: + + if (connected) + { + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + imapData._net->getStreamPtr()->stop(); + } + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(command); + std::string().swap(buf); + + return false; +} + +bool ESP32_MailClient::setFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 0); +} + +bool ESP32_MailClient::addFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 1); +} + +bool ESP32_MailClient::removeFlag(IMAPData &imapData, int msgUID, const String &flag) +{ + return _setFlag(imapData, msgUID, flag, 2); +} + +bool ESP32_MailClient::_setFlag(IMAPData &imapData, int msgUID, const String &flag, uint8_t action) +{ + + std::string buf; + + bool starttls = imapData._starttls; + bool connected = false; + + int bufSize = 50; + + char *_val = new char[bufSize]; + char *_part = new char[bufSize]; + + unsigned long dataTime = 0; + + int count = 0; + + imapData._net->setDebugCallback(NULL); + + if (imapData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_225); + ESP32MailDebug(imapData._host.c_str()); + ESP32MailDebug(String(imapData._port).c_str()); + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_50; + imapData._cbData._status = ESP32_MAIL_STR_51; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + imapData._net->setDebugCallback(ESP32MailDebug); + + if (imapData._rootCA.size() > 0) + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)imapData._rootCA.front()); + else + imapData._net->begin(imapData._host.c_str(), imapData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + while (!imapData._net->connected() && count < 10) + { + + count++; + + if (!imapData._net->connect(starttls)) + { + + _imapStatus = IMAP_STATUS_SERVER_CONNECT_FAILED; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + } + else + { + break; + } + } + + if (!imapData._net->connect(starttls)) + { + goto out; + } + + connected = true; + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_54; + imapData._cbData._status = ESP32_MAIL_STR_55; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_228); + + //Don't expect handshake from some servers + dataTime = millis(); + + while (imapData._net->connected() && !imapData._net->getStreamPtr()->available() && millis() - 500 < dataTime) + delay(0); + + if (imapData._net->connected() && imapData._net->getStreamPtr()->available()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData.clearMessageData(); + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_56; + imapData._cbData._status = ESP32_MAIL_STR_57; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_229); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_130); + imapData._net->getStreamPtr()->print(imapData._loginEmail.c_str()); + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_131); + imapData._net->getStreamPtr()->println(imapData._loginPassword.c_str()); + + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LOGIN)) + { + _imapStatus = IMAP_STATUS_LOGIN_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_58; + imapData._cbData._status = ESP32_MAIL_STR_59; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_230); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_133); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::LIST)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + imapData._cbData.empty(); + } + + if (imapData._readCallback) + { + + imapData._cbData._info = ESP32_MAIL_STR_60; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + + for (size_t i = 0; i < imapData._folders.size(); i++) + { + imapData._cbData._info = imapData._folders[i]; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + imapData._cbData._info = ESP32_MAIL_STR_61 + imapData._currentFolder + ESP32_MAIL_STR_97; + imapData._cbData._status = ""; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_248); + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_247); + imapData._net->getStreamPtr()->print(imapData._currentFolder.c_str()); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_136); + if (!waitIMAPResponse(imapData, IMAP_COMMAND_TYPE::EXAMINE)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._debug) + { + if (action == 0) + ESP32MailDebugInfo(ESP32_MAIL_STR_253); + else if (action == 1) + ESP32MailDebugInfo(ESP32_MAIL_STR_254); + else + ESP32MailDebugInfo(ESP32_MAIL_STR_255); + } + + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_249); + imapData._net->getStreamPtr()->print(msgUID); + if (action == 0) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_250); + else if (action == 1) + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_251); + else + imapData._net->getStreamPtr()->print(ESP32_MAIL_STR_252); + imapData._net->getStreamPtr()->print(flag); + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_192); + + if (!getIMAPResponse(imapData)) + { + _imapStatus = IMAP_STATUS_PARSE_FLAG_FAILED; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_85; + imapData._cbData._status = ESP32_MAIL_STR_86; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_234); + + if (imapData._net->connected()) + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->println(ESP32_MAIL_STR_146); + + if (!waitIMAPResponse(imapData, 0)) + { + _imapStatus = IMAP_STATUS_BAD_COMMAND; + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_53 + imapErrorReasonStr(); + imapData._cbData._status = ESP32_MAIL_STR_52; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + if (imapData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(imapErrorReasonStr().c_str(), true); + } + goto out; + } + + if (imapData._readCallback) + { + imapData._cbData._info = ESP32_MAIL_STR_98; + imapData._cbData._status = ESP32_MAIL_STR_96; + imapData._cbData._success = true; + imapData._readCallback(imapData._cbData); + } + + if (imapData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_235); + + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + + imapData._net->getStreamPtr()->stop(); + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + + return true; + +out: + + if (connected) + { + if (imapData._net->connected()) + { + while (imapData._net->getStreamPtr()->available()) + imapData._net->getStreamPtr()->read(); + imapData._net->getStreamPtr()->stop(); + } + } + + imapData._cbData.empty(); + delete[] _val; + delete[] _part; + std::string().swap(buf); + return false; +} + + +bool ESP32_MailClient::smtpClientAvailable(SMTPData &smtpData, bool available) +{ + + if (!smtpData._net->getStreamPtr()) + return false; + + if (available) + return smtpData._net->getStreamPtr()->connected() && smtpData._net->getStreamPtr()->available(); + else + return smtpData._net->getStreamPtr()->connected() && !smtpData._net->getStreamPtr()->available(); +} + +bool ESP32_MailClient::imapClientAvailable(IMAPData &imapData, bool available) +{ + + if (!imapData._net->getStreamPtr()) + return false; + + if (available) + return imapData._net->getStreamPtr()->connected() && imapData._net->getStreamPtr()->available(); + else + return imapData._net->getStreamPtr()->connected() && !imapData._net->getStreamPtr()->available(); +} + +void ESP32_MailClient::createDirs(std::string dirs) +{ + std::string dir = ""; + int count = 0; + for (int i = 0; i < dirs.length(); i++) + { + dir.append(1, dirs[i]); + count++; + if (dirs[i] == '/') + { + if (dir.length() > 0) + SD.mkdir(dir.substr(0, dir.length() - 1).c_str()); + count = 0; + } + } + if (count > 0) + SD.mkdir(dir.c_str()); + std::string().swap(dir); +} + +bool ESP32_MailClient::sdTest() +{ + + if (_sdConfigSet) + sdBegin(_sck, _miso, _mosi, _ss); + else + sdBegin(); + + File file = SD.open(ESP32_MAIL_STR_204, FILE_WRITE); + if (!file) + return false; + + if (!file.write(32)) + return false; + file.close(); + + file = SD.open(ESP32_MAIL_STR_204); + if (!file) + return false; + + while (file.available()) + { + if (file.read() != 32) + return false; + } + file.close(); + + SD.remove(ESP32_MAIL_STR_204); + + return true; +} + +bool ESP32_MailClient::sendMail(SMTPData &smtpData) +{ + + _smtpStatus = 0; + std::string buf; + std::string buf2; + int bufSize = 50; + bool starttls = smtpData._starttls; + bool connected = false; + char *_val = new char[bufSize]; + int res = 0; + + smtpData._net->setDebugCallback(NULL); + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_120; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + { + ESP32MailDebugInfo(ESP32_MAIL_STR_236); + ESP32MailDebug(smtpData._host.c_str()); + ESP32MailDebug(String(smtpData._port).c_str()); + } + + + if (smtpData._debug) + smtpData._net->setDebugCallback(ESP32MailDebug); + + if (smtpData._rootCA.size() > 0) + smtpData._net->begin(smtpData._host.c_str(), smtpData._port, ESP32_MAIL_STR_202, (const char *)smtpData._rootCA.front()); + else + smtpData._net->begin(smtpData._host.c_str(), smtpData._port, ESP32_MAIL_STR_202, (const char *)NULL); + + if (smtpData._port == 587) + starttls = true; + + if (!smtpData._net->connect(starttls)) + { + _smtpStatus = SMTP_STATUS_SERVER_CONNECT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_238); + + connected = true; + + if (!starttls) + { + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_121; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (waitSMTPResponse(smtpData) != 220) + { + _smtpStatus = SMTP_STATUS_SMTP_RESPONSE_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_122; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_239); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_5); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_IDENTIFICATION_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_123; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_240); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_4); + + if (waitSMTPResponse(smtpData) != 334) + { + _smtpStatus = SMTP_STATUS_AUTHEN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_124; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_241); + + smtpData._net->getStreamPtr()->println(base64_encode_string((const unsigned char *)smtpData._loginEmail.c_str(), smtpData._loginEmail.length()).c_str()); + + if (waitSMTPResponse(smtpData) != 334) + { + _smtpStatus = SMTP_STATUS_USER_LOGIN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + smtpData._net->getStreamPtr()->println(base64_encode_string((const unsigned char *)smtpData._loginPassword.c_str(), smtpData._loginPassword.length()).c_str()); + + if (waitSMTPResponse(smtpData) != 235) + { + _smtpStatus = SMTP_STATUS_PASSWORD_LOGIN_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_125; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_242); + + if (smtpData._priority > 0 && smtpData._priority <= 5) + { + memset(_val, 0, bufSize); + itoa(smtpData._priority, _val, 10); + + buf2 += ESP32_MAIL_STR_17; + buf2 += _val; + buf2 += ESP32_MAIL_STR_34; + + if (smtpData._priority == 1) + { + buf2 += ESP32_MAIL_STR_18; + buf2 += ESP32_MAIL_STR_21; + } + else if (smtpData._priority == 3) + { + buf2 += ESP32_MAIL_STR_19; + buf2 += ESP32_MAIL_STR_22; + } + else if (smtpData._priority == 5) + { + buf2 += ESP32_MAIL_STR_20; + buf2 += ESP32_MAIL_STR_23; + } + } + + buf2 += ESP32_MAIL_STR_10; + + if (smtpData._fromName.length() > 0) + buf2 += smtpData._fromName; + + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._senderEmail; + buf2 += ESP32_MAIL_STR_15; + buf2 += ESP32_MAIL_STR_34; + + buf += ESP32_MAIL_STR_8; + buf += ESP32_MAIL_STR_14; + buf += smtpData._senderEmail; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_SENDER_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + for (uint8_t i = 0; i < smtpData._recipient.size(); i++) + { + if (i == 0) + { + buf2 += ESP32_MAIL_STR_11; + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._recipient[i]; + buf2 += ESP32_MAIL_STR_15; + } + else + { + buf2 += ESP32_MAIL_STR_13; + buf2 += smtpData._recipient[i]; + buf2 += ESP32_MAIL_STR_15; + } + + if (i == smtpData._recipient.size() - 1) + buf2 += ESP32_MAIL_STR_34; + + buf.clear(); + + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._recipient[i]; + buf += ESP32_MAIL_STR_15; + + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + for (uint8_t i = 0; i < smtpData._cc.size(); i++) + { + + if (i == 0) + { + buf2 += ESP32_MAIL_STR_12; + buf2 += ESP32_MAIL_STR_14; + buf2 += smtpData._cc[i]; + buf2 += ESP32_MAIL_STR_15; + } + else + { + buf2 += ESP32_MAIL_STR_13; + buf2 += smtpData._cc[i]; + buf2 += ESP32_MAIL_STR_15; + } + + if (i == smtpData.ccCount() - 1) + buf2 += ESP32_MAIL_STR_34; + + buf.clear(); + + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._cc[i]; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + for (uint8_t i = 0; i < smtpData._bcc.size(); i++) + { + buf.clear(); + buf += ESP32_MAIL_STR_9; + buf += ESP32_MAIL_STR_14; + buf += smtpData._bcc[i]; + buf += ESP32_MAIL_STR_15; + smtpData._net->getStreamPtr()->println(buf.c_str()); + + if (waitSMTPResponse(smtpData) != 250) + { + _smtpStatus = SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_126; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_243); + + smtpData._net->getStreamPtr()->println(ESP32_MAIL_STR_16); + + if (waitSMTPResponse(smtpData) != 354) + { + _smtpStatus = SMTP_STATUS_SEND_BODY_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + smtpData._net->getStreamPtr()->print(buf2.c_str()); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_24); + smtpData._net->getStreamPtr()->println(smtpData._subject.c_str()); + + if (smtpData._customMessageHeader.size() > 0) + for (uint8_t k = 0; k < smtpData._customMessageHeader.size(); k++) + smtpData._net->getStreamPtr()->println(smtpData._customMessageHeader[k].c_str()); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_3); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_1); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_2); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_35); + + buf.clear(); + + set_message_header(buf, smtpData._message, smtpData._htmlFormat); + + smtpData._net->getStreamPtr()->print(buf.c_str()); + + if (smtpData._attach._index > 0) + { + smtpData._cbData._info = ESP32_MAIL_STR_127; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_244); + } + + for (uint8_t i = 0; i < smtpData._attach._index; i++) + { + if (smtpData._attach._type[i] == 0) + { + + smtpData._cbData._info = smtpData._attach._filename[i]; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + if (smtpData._debug) + ESP32MailDebug(smtpData._attach._filename[i].c_str()); + + buf.clear(); + set_attachment_header(i, buf, smtpData._attach); + smtpData._net->getStreamPtr()->print(buf.c_str()); + send_base64_encode_mime_data(smtpData._net->getStreamPtr(), smtpData._attach._buf[i].front(), smtpData._attach._size[i]); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_34); + } + else + { + + if (!_sdOk) + { + if (smtpData._storageType == MailClientStorageType::SD) + _sdOk = sdTest(); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + _sdOk = SPIFFS.begin(true); + } + + if (!_sdOk) + continue; + + bool file_existed = false; + if (smtpData._storageType == MailClientStorageType::SD) + file_existed = SD.exists(smtpData._attach._filename[i].c_str()); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + file_existed = SPIFFS.exists(smtpData._attach._filename[i].c_str()); + + if (file_existed) + { + smtpData._cbData._info = smtpData._attach._filename[i]; + smtpData._cbData._success = false; + if (smtpData._sendCallback) + smtpData._sendCallback(smtpData._cbData); + + if (smtpData._debug) + ESP32MailDebug(smtpData._attach._filename[i].c_str()); + + buf.clear(); + set_attachment_header(i, buf, smtpData._attach); + smtpData._net->getStreamPtr()->print(buf.c_str()); + + File file; + if (smtpData._storageType == MailClientStorageType::SD) + file = SD.open(smtpData._attach._filename[i].c_str(), FILE_READ); + else if (smtpData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(smtpData._attach._filename[i].c_str(), FILE_READ); + + send_base64_encode_mime_file(smtpData._net->getStreamPtr(), file); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_34); + } + } + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_245); + + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_33); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_2); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_33); + smtpData._net->getStreamPtr()->print(ESP32_MAIL_STR_37); + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_128; + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + + res = waitSMTPResponse(smtpData); + + if (res != 250 && res != -1000) + { + _smtpStatus = SMTP_STATUS_SEND_BODY_FAILED; + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_53 + smtpErrorReasonStr(); + smtpData._cbData._success = false; + smtpData._sendCallback(smtpData._cbData); + } + if (smtpData._debug) + { + ESP32MailDebugError(); + ESP32MailDebugLine(smtpErrorReasonStr().c_str(), true); + } + goto failed; + } + + if (smtpData._sendCallback) + { + smtpData._cbData._info = ESP32_MAIL_STR_129; + smtpData._cbData._success = true; + smtpData._sendCallback(smtpData._cbData); + } + + if (smtpData._debug) + ESP32MailDebugInfo(ESP32_MAIL_STR_246); + + if (smtpData._net->connected()) + smtpData._net->getStreamPtr()->stop(); + + smtpData._cbData.empty(); + + std::string().swap(buf); + std::string().swap(buf2); + delete[] _val; + + return true; + +failed: + + if (connected) + { + if (smtpData._net->connected()) + smtpData._net->getStreamPtr()->stop(); + } + + smtpData._cbData.empty(); + std::string().swap(buf); + std::string().swap(buf2); + delete[] _val; + return false; +} + +String ESP32_MailClient::smtpErrorReason() +{ + return smtpErrorReasonStr().c_str(); +} + +std::string ESP32_MailClient::smtpErrorReasonStr() +{ + std::string res = ""; + switch (_smtpStatus) + { + case SMTP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case SMTP_STATUS_SMTP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_39; + break; + case SMTP_STATUS_IDENTIFICATION_FAILED: + res = ESP32_MAIL_STR_41; + break; + case SMTP_STATUS_AUTHEN_NOT_SUPPORT: + res = ESP32_MAIL_STR_42; + break; + case SMTP_STATUS_AUTHEN_FAILED: + res = ESP32_MAIL_STR_43; + break; + case SMTP_STATUS_USER_LOGIN_FAILED: + res = ESP32_MAIL_STR_44; + break; + case SMTP_STATUS_PASSWORD_LOGIN_FAILED: + res = ESP32_MAIL_STR_47; + break; + case SMTP_STATUS_SEND_HEADER_SENDER_FAILED: + res = ESP32_MAIL_STR_48; + break; + case SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED: + res = ESP32_MAIL_STR_222; + break; + case SMTP_STATUS_SEND_BODY_FAILED: + res = ESP32_MAIL_STR_49; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res; +} + +String ESP32_MailClient::imapErrorReason() +{ + std::string res = ""; + switch (_imapStatus) + { + case IMAP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case IMAP_STATUS_IMAP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_40; + break; + case IMAP_STATUS_LOGIN_FAILED: + res = ESP32_MAIL_STR_45; + break; + case IMAP_STATUS_BAD_COMMAND: + res = ESP32_MAIL_STR_46; + break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res.c_str(); +} + +std::string ESP32_MailClient::imapErrorReasonStr() +{ + std::string res = ""; + + switch (_imapStatus) + { + case IMAP_STATUS_SERVER_CONNECT_FAILED: + res = ESP32_MAIL_STR_38; + break; + case IMAP_STATUS_IMAP_RESPONSE_FAILED: + res = ESP32_MAIL_STR_40; + break; + case IMAP_STATUS_LOGIN_FAILED: + res = ESP32_MAIL_STR_45; + break; + case IMAP_STATUS_BAD_COMMAND: + res = ESP32_MAIL_STR_46; + break; + case IMAP_STATUS_PARSE_FLAG_FAILED: + res = ESP32_MAIL_STR_256; + break; + case MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL: + res = ESP32_MAIL_STR_221; + break; + default: + res = ""; + } + return res; +} + +void ESP32_MailClient::ESP32MailDebugError() +{ + size_t dbgInfoLen = strlen_P(ESP32_MAIL_STR_227) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, ESP32_MAIL_STR_227); + ESP32MailDebugLine(dbgInfo, false); + delete[] dbgInfo; +} + +void ESP32_MailClient::ESP32MailDebugInfo(PGM_P info) +{ + size_t dbgInfoLen = strlen_P(info) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, info); + ESP32MailDebug(dbgInfo); + delete[] dbgInfo; +} + +bool ESP32_MailClient::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss) +{ + _sck = sck; + _miso = miso; + _mosi = mosi; + _ss = ss; + _sdConfigSet = true; + SPI.begin(_sck, _miso, _mosi, _ss); + return SD.begin(_ss, SPI); +} + +bool ESP32_MailClient::sdBegin(void) +{ + _sdConfigSet = false; + return SD.begin(); +} + +void ESP32_MailClient::set_message_header(string &header, string &message, bool htmlFormat) +{ + header += ESP32_MAIL_STR_33; + header += ESP32_MAIL_STR_2; + header += ESP32_MAIL_STR_34; + if (!htmlFormat) + header += ESP32_MAIL_STR_27; + else + header += ESP32_MAIL_STR_28; + + header += ESP32_MAIL_STR_29; + header += ESP32_MAIL_STR_34; + + header += message; + header += ESP32_MAIL_STR_34; + header += ESP32_MAIL_STR_34; +} + +void ESP32_MailClient::set_attachment_header(uint8_t index, std::string &header, attachmentData &attach) +{ + + header += ESP32_MAIL_STR_33; + header += ESP32_MAIL_STR_2; + header += ESP32_MAIL_STR_34; + + header += ESP32_MAIL_STR_25; + + if (attach._mime_type[index].length() == 0) + header += ESP32_MAIL_STR_32; + else + header += attach._mime_type[index]; + + header += ESP32_MAIL_STR_26; + + std::string filename(attach._filename[index]); + + size_t found = filename.find_last_of("/\\"); + + if (found != std::string::npos) + { + filename.clear(); + filename += attach._filename[index].substr(found + 1); + } + + header += filename; + header += ESP32_MAIL_STR_36; + + header += ESP32_MAIL_STR_30; + header += filename; + header += ESP32_MAIL_STR_36; + + header += ESP32_MAIL_STR_31; + header += ESP32_MAIL_STR_34; + + std::string().swap(filename); +} + +int ESP32_MailClient::waitSMTPResponse(SMTPData &smtpData) +{ + + long dataTime = millis(); + char c = '\0'; + std::string lineBuf = ""; + int lfCount = 0; + size_t p1 = 0; + int resCode = -1000; + + while (smtpClientAvailable(smtpData, false) && millis() - dataTime < smtpData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (smtpClientAvailable(smtpData, true)) + { + while (smtpClientAvailable(smtpData, true)) + { + int r = smtpData._net->getStreamPtr()->read(); + + if (r < 0) + continue; + + c = (char)r; + + lineBuf.append(1, c); + if (c == '\n') + { + dataTime = millis(); + if (lfCount == 0) + { + p1 = lineBuf.find(" "); + if (p1 != std::string::npos) + resCode = atoi(lineBuf.substr(0, p1).c_str()); + } + if (smtpData._debug) + ESP32MailDebug(lineBuf.c_str()); + lineBuf.clear(); + lfCount++; + } + + if (millis() - dataTime > smtpData._net->tcpTimeout + 30000) + break; + } + } + std::string().swap(lineBuf); + return resCode; +} + +bool ESP32_MailClient::getIMAPResponse(IMAPData &imapData) +{ + long dataTime = millis(); + char c = '\0'; + bool success = false; + std::string str = ""; + while (imapClientAvailable(imapData, false) && millis() - dataTime < imapData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (imapClientAvailable(imapData, true)) + { + while (imapClientAvailable(imapData, true)) + { + int r = imapData._net->getStreamPtr()->read(); + if (r < 0) + continue; + c = (char)r; + if (c == '\n') + { + if (imapData._debug) + ESP32MailDebug(str.c_str()); + str.clear(); + } + else + str += c; + + if (str.find(ESP32_MAIL_STR_132) != std::string::npos) + success = true; + } + } + + std::string().swap(str); + return success; +} + +bool ESP32_MailClient::waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType, int maxChar, int mailIndex, int messageDataIndex, std::string part) +{ + + long dataTime = millis(); + + char c = 0; + std::string lineBuf = ""; + std::string msgNumBuf = ""; + std::string filepath = ""; + std::string hpath = ""; + std::string tmp = ""; + std::string msgID = ""; + std::string from = ""; + std::string to = ""; + std::string subject = ""; + std::string date = ""; + std::string cc = ""; + std::string from_charset = ""; + std::string to_charset = ""; + std::string cc_charset = ""; + std::string subject_charset = ""; + std::string acceptLanguage = ""; + std::string contentLanguage = ""; + + int bufSize = 100; + char *dest = new char[bufSize]; + char *buf = new char[bufSize]; + + int readCount = 0; + int lfCount = 0; + int charCount = 0; + size_t p1 = 0; + size_t p2 = 0; + size_t p3 = 0; + size_t payloadLength = 0; + size_t outputLength; + + bool completeResp = false; + bool validResponse = false; + bool downloadReq = false; + size_t currentDownloadByte = 0; + + int max = imapData._emailNumMax; + if (!imapData._recentSort) + max = max - 1; + + uint8_t headerType = 0; + + File file; + int reportState = 0; + int downloadedByte = 0; + + if (imapCommandType == IMAP_COMMAND_TYPE::LIST) + std::vector() + .swap(imapData._folders); + + while (imapClientAvailable(imapData, false) && millis() - dataTime < imapData._net->tcpTimeout) + delay(0); + + dataTime = millis(); + if (imapClientAvailable(imapData, true)) + { + while (imapClientAvailable(imapData, true) || !completeResp) + { + + int r = imapData._net->getStreamPtr()->read(); + + if (r < 0) + continue; + + c = (char)r; + + if (payloadLength > 0 && !completeResp) + charCount++; + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount == 0) + { + delay(0); + if (c == ' ') + { + p3 = msgNumBuf.find(ESP32_MAIL_STR_257); + if (p3 != std::string::npos) + { + validResponse = false; + break; + } + + if (msgNumBuf != ESP32_MAIL_STR_183 && msgNumBuf != ESP32_MAIL_STR_141 && imapData._msgNum.size() <= max) + { + imapData._msgNum.push_back(atoi(msgNumBuf.c_str())); + + if (imapData._msgNum.size() > imapData._emailNumMax && imapData._recentSort) + imapData._msgNum.erase(imapData._msgNum.begin()); + imapData._searchCount++; + } + + msgNumBuf.clear(); + } + else if (c != '\r' && c != '\n') + { + msgNumBuf.append(1, c); + } + } + + if (c != '\r' && c != '\n' && imapCommandType != IMAP_COMMAND_TYPE::SEARCH) + lineBuf.append(1, c); + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT && lfCount > 0) + { + + if (payloadLength > 0 && charCount < payloadLength - 1) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding != ESP32_MAIL_STR_160) + { + if (charCount < maxChar) + imapData._messageDataInfo[mailIndex][messageDataIndex]._text.append(1, c); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + downloadReq = true; + + filepath.clear(); + + filepath = imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + if (!imapData._headerSaved) + hpath = filepath + ESP32_MAIL_STR_203; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155) + { + if (imapData._saveDecodedText) + filepath += ESP32_MAIL_STR_161; + else + filepath += ESP32_MAIL_STR_162; + } + else if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154) + { + if (imapData._saveDecodedHTML) + filepath += ESP32_MAIL_STR_163; + else + filepath += ESP32_MAIL_STR_164; + } + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + if (_sdOk) + file.write(c); + } + } + } + + if (millis() - dataTime > imapData._net->tcpTimeout + (30 * 1000) || (payloadLength > 0 && charCount == payloadLength && completeResp)) + { + + if (charCount < payloadLength || !completeResp) + clientReadAll(imapData._net->getStreamPtr()); + + break; + } + } + + if (c == '\n') + { + dataTime = millis(); + + if (lfCount == 0) + { + if (imapData._debug) + ESP32MailDebug(lineBuf.c_str()); + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT) + { + + p1 = lineBuf.find(ESP32_MAIL_STR_165); + if (p1 != std::string::npos) + validResponse = true; + } + + p1 = lineBuf.find(ESP32_MAIL_STR_166); + if (p1 != std::string::npos) + validResponse = true; + } + + p1 = lineBuf.find(ESP32_MAIL_STR_211); + p2 = lineBuf.find(ESP32_MAIL_STR_158); + p3 = lineBuf.find(ESP32_MAIL_STR_159); + + if (p1 != std::string::npos || p2 != std::string::npos || p3 != std::string::npos) + { + + validResponse = true; + + if (p2 != std::string::npos || p3 != std::string::npos) + validResponse = false; + + if (payloadLength == 0) + { + if (imapCommandType == IMAP_COMMAND_TYPE::LOGIN || + imapCommandType == IMAP_COMMAND_TYPE::LIST || + imapCommandType == IMAP_COMMAND_TYPE::EXAMINE || + imapCommandType == IMAP_COMMAND_TYPE::SEARCH || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME || + imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + + //Cyrus server 3.0 does not comply to rfc3501 as it resonses the CAPABILITY after received LOGIN command with no CAPABILITY command requested. + if (lineBuf.find(ESP32_MAIL_STR_134) == std::string::npos && lineBuf.find(ESP32_MAIL_STR_145) == std::string::npos) + completeResp = true; + + //Some servers e.g. STRATO E-Mail-Server does not reply any error when fetching none existing MIME header part at defined index. + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME) + validResponse = false; + } + } + else + { + + if ((payloadLength > 0 && charCount >= payloadLength) || imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME) + { + completeResp = true; + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount > 0) + { + completeResp = true; + validResponse = true; + } + + tmp = lineBuf; + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_MIME && lfCount > 0) + { + + if (payloadLength > 0 && validResponse) + { + + if (imapData._messageDataInfo[mailIndex].size() < messageDataIndex + 1) + { + messageBodyData b; + imapData._messageDataInfo[mailIndex].push_back(b); + imapData._messageDataCount[mailIndex] = imapData._messageDataInfo[mailIndex].size(); + } + + p1 = tmp.find(ESP32_MAIL_STR_167); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_167)); + if (p2 != std::string::npos) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_167), p2 - p1 - strlen(ESP32_MAIL_STR_167)); + + p1 = tmp.find(ESP32_MAIL_STR_168, p2); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_168)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._charset = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_168), p2 - p1 - strlen(ESP32_MAIL_STR_168)); + } + else if (tmp.find(ESP32_MAIL_STR_169, p2) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_169, p2); + imapData._messageDataInfo[mailIndex][messageDataIndex]._charset = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_169)); + } + + p1 = tmp.find(ESP32_MAIL_STR_170, p2); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_170)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._name = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_170), p2 - p1 - strlen(ESP32_MAIL_STR_170)); + } + else if (tmp.find(ESP32_MAIL_STR_171, p2) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_171, p2); + imapData._messageDataInfo[mailIndex][messageDataIndex]._name = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_171)); + } + } + } + + p1 = tmp.find(ESP32_MAIL_STR_172); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_172)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_172), p2 - p1 - strlen(ESP32_MAIL_STR_172)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_172)); + } + + p1 = tmp.find(ESP32_MAIL_STR_174); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_174)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._descr = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_174), p2 - p1 - strlen(ESP32_MAIL_STR_174)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._descr = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_174)); + } + + p1 = tmp.find(ESP32_MAIL_STR_175); + if (p1 != std::string::npos) + { + + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_175)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_175), p2 - p1 - strlen(ESP32_MAIL_STR_175)); + else + imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_175)); + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition == ESP32_MAIL_STR_153) + imapData._attachmentCount[mailIndex]++; + } + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._disposition != "") + { + + p1 = tmp.find(ESP32_MAIL_STR_176); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_176)); + + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_176), p2 - p1 - strlen(ESP32_MAIL_STR_176)); + } + else if (tmp.find(ESP32_MAIL_STR_177) != std::string::npos) + { + + p1 = tmp.find(ESP32_MAIL_STR_177); + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_177)); + } + + p1 = tmp.find(ESP32_MAIL_STR_178); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(";", p1 + strlen(ESP32_MAIL_STR_178) + 1); + if (p2 != std::string::npos) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = atoi(lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_178), p2 - p1 - strlen(ESP32_MAIL_STR_178)).c_str()); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + else + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = atoi(lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_178)).c_str()); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + } + + p1 = tmp.find(ESP32_MAIL_STR_179); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_179)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._creation_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_179), p2 - p1 - strlen(ESP32_MAIL_STR_179)); + } + else if (tmp.find(ESP32_MAIL_STR_180) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_180); + imapData._messageDataInfo[mailIndex][messageDataIndex]._creation_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_180)); + } + + p1 = tmp.find(ESP32_MAIL_STR_181); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_136, p1 + strlen(ESP32_MAIL_STR_181)); + if (p2 != std::string::npos) + imapData._messageDataInfo[mailIndex][messageDataIndex]._modification_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_181), p2 - p1 - strlen(ESP32_MAIL_STR_181)); + } + else if (tmp.find(ESP32_MAIL_STR_182) != std::string::npos) + { + p1 = tmp.find(ESP32_MAIL_STR_182); + imapData._messageDataInfo[mailIndex][messageDataIndex]._modification_date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_182)); + } + } + + imapData._messageDataInfo[mailIndex][messageDataIndex]._part = part; + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SEARCH && lfCount == 0) + { + + if (msgNumBuf.length() > 0 && msgNumBuf != ESP32_MAIL_STR_183 && msgNumBuf != ESP32_MAIL_STR_141 && imapData._msgNum.size() <= max) + { + imapData._msgNum.push_back(atoi(msgNumBuf.c_str())); + imapData._searchCount++; + + if (imapData._msgNum.size() > imapData._emailNumMax && imapData._recentSort) + imapData._msgNum.erase(imapData._msgNum.begin()); + } + + if (imapData._recentSort) + std::sort(imapData._msgNum.begin(), imapData._msgNum.end(), compFunc); + } + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + + uint8_t _headerType = 0; + + p1 = tmp.find(ESP32_MAIL_STR_184); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::FROM; + _headerType = IMAP_HEADER_TYPE::FROM; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + strlen(ESP32_MAIL_STR_184)); + if (p2 != std::string::npos) + from = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_184), p2 - p1 - strlen(ESP32_MAIL_STR_184)); + else + from = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_184)); + + if (from[0] == '=' && from[1] == '?') + { + p1 = from.find("?", 2); + + if (p1 != std::string::npos) + from_charset = from.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, from.c_str(), bufSize); + from = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_185); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::TO; + _headerType = IMAP_HEADER_TYPE::TO; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + to = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_185), p2 - p1 - strlen(ESP32_MAIL_STR_185)); + else + to = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_185)); + + if (to[0] == '=' && to[1] == '?') + { + p1 = to.find("?", 2); + + if (p1 != std::string::npos) + to_charset = to.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, to.c_str(), bufSize); + to = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_186); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::CC; + _headerType = IMAP_HEADER_TYPE::CC; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + cc = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_186), p2 - p1 - strlen(ESP32_MAIL_STR_186)); + else + cc = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_186)); + + if (cc[0] == '=' && cc[1] == '?') + { + p1 = cc.find("?", 2); + + if (p1 != std::string::npos) + cc_charset = cc.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, cc.c_str(), bufSize); + cc = dest; + } + + p1 = tmp.find(ESP32_MAIL_STR_187); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::SUBJECT; + _headerType = IMAP_HEADER_TYPE::SUBJECT; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + + memset(dest, 0, bufSize); + if (p2 != std::string::npos) + subject = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_187), p2 - p1 - strlen(ESP32_MAIL_STR_187)); + else + subject = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_187)); + + if (subject[0] == '=' && subject[1] == '?') + { + p1 = subject.find("?", 2); + if (p1 != std::string::npos) + subject_charset = subject.substr(2, p1 - 2); + } + + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, subject.c_str(), bufSize); + subject = dest; + } + p1 = tmp.find(ESP32_MAIL_STR_188); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::DATE; + _headerType = IMAP_HEADER_TYPE::DATE; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_188), p2 - p1 - strlen(ESP32_MAIL_STR_188)); + else + date = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_188)); + } + + p1 = tmp.find(ESP32_MAIL_STR_189); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::MSG_ID; + _headerType = IMAP_HEADER_TYPE::MSG_ID; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + msgID = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_189), p2 - p1 - strlen(ESP32_MAIL_STR_189)); + else + msgID = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_189)); + } + + p1 = tmp.find(ESP32_MAIL_STR_190); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::ACCEPT_LANG; + _headerType = IMAP_HEADER_TYPE::ACCEPT_LANG; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + acceptLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_190), p2 - p1 - strlen(ESP32_MAIL_STR_190)); + else + acceptLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_190)); + } + + p1 = tmp.find(ESP32_MAIL_STR_191); + if (p1 != std::string::npos) + { + headerType = IMAP_HEADER_TYPE::CONT_LANG; + _headerType = IMAP_HEADER_TYPE::CONT_LANG; + + p2 = lineBuf.find(ESP32_MAIL_STR_173, p1 + 1); + if (p2 != std::string::npos) + contentLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_191), p2 - p1 - strlen(ESP32_MAIL_STR_191)); + else + contentLanguage = lineBuf.substr(p1 + strlen(ESP32_MAIL_STR_191)); + } + + if (_headerType == 0 && charCount < payloadLength && payloadLength > 0) + { + if (headerType == IMAP_HEADER_TYPE::FROM) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + from += dest; + } + else if (headerType == IMAP_HEADER_TYPE::TO) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + to += dest; + } + else if (headerType == IMAP_HEADER_TYPE::CC) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + cc += dest; + } + else if (headerType == IMAP_HEADER_TYPE::SUBJECT) + { + memset(dest, 0, bufSize); + RFC2047Decoder.rfc2047Decode(dest, lineBuf.c_str(), bufSize); + subject += dest; + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::LIST) + { + p1 = lineBuf.find(ESP32_MAIL_STR_195); + p2 = lineBuf.find(ESP32_MAIL_STR_196); + + if (p1 != std::string::npos && p2 == std::string::npos) + { + p2 = lineBuf.find_last_of(ESP32_MAIL_STR_136); + if (p2 != std::string::npos) + { + p1 = lineBuf.find_last_of(ESP32_MAIL_STR_136, p2 - 1); + if (p1 != std::string::npos) + imapData._folders.push_back(lineBuf.substr(p1 + 1, p2 - p1 - 1)); + } + } + } + + if (imapCommandType == IMAP_COMMAND_TYPE::SELECT || imapCommandType == IMAP_COMMAND_TYPE::EXAMINE) + { + + p1 = lineBuf.find(ESP32_MAIL_STR_197); + if (p1 != std::string::npos) + { + p1 = lineBuf.find(ESP32_MAIL_STR_198); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_192); + if (p2 != std::string::npos) + { + string _tmp; + + _tmp = lineBuf.substr(p1 + 1, p2 - p1 - 1).c_str(); + msgNumBuf.clear(); + + for (size_t i = 0; i < _tmp.length(); i++) + { + if (_tmp[i] != '\\' && _tmp[i] != ' ' && _tmp[i] != '\r' && _tmp[i] != '\n') + msgNumBuf.append(1, _tmp[i]); + + if (_tmp[i] == ' ') + { + imapData._flag.push_back(msgNumBuf); + msgNumBuf.clear(); + } + } + if (msgNumBuf.length() > 0) + { + imapData._flag.push_back(msgNumBuf); + } + + std::string().swap(_tmp); + } + } + } + + p2 = lineBuf.find(ESP32_MAIL_STR_199); + if (p2 != std::string::npos) + imapData._totalMessage = atoi(lineBuf.substr(2, p2 - 2).c_str()); + + p1 = lineBuf.find(ESP32_MAIL_STR_200); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_156, p1 + 10); + if (p2 != std::string::npos) + imapData._nextUID = lineBuf.substr(p1 + 10, p2 - p1 - 10); + } + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT && lfCount > 0 && (charCount < maxChar || imapData._saveHTMLMsg || imapData._saveTextMsg)) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding == ESP32_MAIL_STR_160) + { + + unsigned char *decoded = base64_decode_char((const unsigned char *)lineBuf.c_str(), lineBuf.length(), &outputLength); + + if (decoded) + { + if (charCount < maxChar) + imapData._messageDataInfo[mailIndex][messageDataIndex]._text.append((char *)decoded, outputLength); + + if (imapData._saveHTMLMsg || imapData._saveTextMsg) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + + downloadReq = true; + + filepath.clear(); + filepath += imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + if (!imapData._headerSaved) + hpath = filepath + ESP32_MAIL_STR_203; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155) + { + if (imapData._saveDecodedText) + filepath += ESP32_MAIL_STR_161; + else + filepath += ESP32_MAIL_STR_162; + } + else if (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154) + { + if (imapData._saveDecodedHTML) + filepath += ESP32_MAIL_STR_163; + else + filepath += ESP32_MAIL_STR_164; + } + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + + if (_sdOk) + { + if ((imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_155 && imapData._saveDecodedText) || + (imapData._messageDataInfo[mailIndex][messageDataIndex]._contentType == ESP32_MAIL_STR_154 && imapData._saveDecodedHTML)) + file.write((const uint8_t *)decoded, outputLength); + else + file.write((const uint8_t *)lineBuf.c_str(), lineBuf.length()); + } + } + + delete[] decoded; + } + } + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT && lfCount > 0) + { + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._transfer_encoding == ESP32_MAIL_STR_160) + { + + if (!imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + { + + imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite = true; + + if (_sdOk) + { + + downloadReq = true; + + filepath.clear(); + filepath += imapData._savePath; + filepath += ESP32_MAIL_STR_202; + + char *midx = new char[50]; + memset(midx, 0, 50); + itoa(imapData._msgNum[mailIndex], midx, 10); + + filepath += midx; + + delete[] midx; + + if (imapData._storageType == MailClientStorageType::SD) + if (!SD.exists(filepath.c_str())) + createDirs(filepath); + + filepath += ESP32_MAIL_STR_202; + + filepath += imapData._messageDataInfo[mailIndex][messageDataIndex]._filename; + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(filepath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(filepath.c_str(), FILE_WRITE); + } + else + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_89; + } + } + } + + if (_sdOk) + { + + unsigned char *decoded = base64_decode_char((const unsigned char *)lineBuf.c_str(), lineBuf.length(), &outputLength); + + downloadedByte += outputLength; + + if (downloadedByte > imapData._messageDataInfo[mailIndex][messageDataIndex]._size) + continue; + + if (decoded) + { + file.write((const uint8_t *)decoded, outputLength); + + if (imapData._storageType == MailClientStorageType::SPIFFS) + delayMicroseconds(1); + else + yield(); + + if (imapData._downloadReport) + { + imapData._downloadedByte[mailIndex] += outputLength; + currentDownloadByte += outputLength; + + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._size == 0) + { + if (payloadLength > 36) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = base64DecodeSize(lineBuf, payloadLength - (payloadLength / 36)); + imapData._totalAttachFileSize[mailIndex] += imapData._messageDataInfo[mailIndex][messageDataIndex]._size; + } + } + + int p = 0; + + if (imapData._totalAttachFileSize[mailIndex] > 0) + p = 100 * imapData._downloadedByte[mailIndex] / imapData._totalAttachFileSize[mailIndex]; + + if ((p % 5 == 0) && (p <= 100)) + { + + if (imapData._readCallback && reportState != -1) + { + memset(buf, 0, bufSize); + itoa(p, buf, 10); + + std::string dl = ESP32_MAIL_STR_90 + imapData._messageDataInfo[mailIndex][messageDataIndex]._filename + ESP32_MAIL_STR_91 + buf + ESP32_MAIL_STR_92; + + if (imapData._readCallback) + { + imapData._cbData._info = dl; + imapData._cbData._status = dl; + imapData._cbData._success = false; + imapData._readCallback(imapData._cbData); + } + + std::string().swap(dl); + } + reportState = -1; + } + else + reportState = 0; + } + + delete[] decoded; + } + + if (millis() - dataTime > imapData._net->tcpTimeout + 1000 * 60 * 5) + break; + } + } + } + + if (lfCount == 0) + { + p1 = lineBuf.find_last_of(ESP32_MAIL_STR_193); + if (p1 != std::string::npos) + { + p2 = lineBuf.find(ESP32_MAIL_STR_194, p1 + 1); + if (p2 != std::string::npos) + payloadLength = atoi(lineBuf.substr(p1 + 1, p2 - p1 - 1).c_str()); + } + } + + lineBuf.clear(); + lfCount++; + std::string().swap(tmp); + } + + readCount++; + } + + if (imapData._error.size() > 0 && mailIndex > -1) + { + if (validResponse && !imapData._error[mailIndex]) + { + imapData._errorMsg[mailIndex].clear(); + imapData._errorMsg[mailIndex] = ""; + } + } + + if (millis() - dataTime > imapData._net->tcpTimeout) + { + + if (downloadReq) + { + if (imapData._messageDataCount[mailIndex] == messageDataIndex + 1) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._error = true; + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError.clear(); + imapData._messageDataInfo[mailIndex][messageDataIndex]._downloadError = ESP32_MAIL_STR_93; + } + } + else + { + + if (imapData._error.size() > 0 && mailIndex > -1) + { + imapData._error[mailIndex] = true; + imapData._errorMsg[mailIndex].clear(); + imapData._errorMsg[mailIndex] = ESP32_MAIL_STR_95; + } + } + } + } + + if (validResponse && (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT || imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_TEXT) && messageDataIndex != -1) + { + if (imapData._messageDataInfo[mailIndex][messageDataIndex]._sdFileOpenWrite) + file.close(); + } + + if (validResponse && imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_ATTACHMENT && imapData._messageDataInfo[mailIndex][messageDataIndex]._size != currentDownloadByte) + { + imapData._messageDataInfo[mailIndex][messageDataIndex]._size = currentDownloadByte; + } + + if (hpath != "") + { + + if (imapData._storageType == MailClientStorageType::SD) + file = SD.open(hpath.c_str(), FILE_WRITE); + else if (imapData._storageType == MailClientStorageType::SPIFFS) + file = SPIFFS.open(hpath.c_str(), FILE_WRITE); + + file.print(ESP32_MAIL_STR_99); + file.println(imapData._date[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_100); + if (imapData._uidSearch) + file.println(imapData._msgNum[mailIndex]); + else + file.println(); + + file.print(ESP32_MAIL_STR_101); + file.println(imapData._msgNum[mailIndex]); + + file.print(ESP32_MAIL_STR_102); + file.println(imapData._acceptLanguage[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_103); + file.println(imapData._contentLanguage[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_104); + file.println(imapData._from[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_105); + file.println(imapData._from_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_106); + file.println(imapData._to[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_107); + file.println(imapData._to_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_108); + file.println(imapData._cc[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_109); + file.println(imapData._cc_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_110); + file.println(imapData._subject[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_111); + file.println(imapData._subject_charset[mailIndex].c_str()); + + file.print(ESP32_MAIL_STR_112); + file.println(imapData._messageDataInfo[mailIndex][messageDataIndex]._charset.c_str()); + + if (imapData._attachmentCount[mailIndex] > 0) + { + + file.print(ESP32_MAIL_STR_113); + file.println(imapData._attachmentCount[mailIndex]); + + for (int j = 0; j < imapData._attachmentCount[mailIndex]; j++) + { + file.print(ESP32_MAIL_STR_114); + file.println(j + 1); + + file.print(ESP32_MAIL_STR_115); + file.println(imapData.getAttachmentFileName(mailIndex, j)); + + file.print(ESP32_MAIL_STR_116); + file.println(imapData.getAttachmentName(mailIndex, j)); + + file.print(ESP32_MAIL_STR_117); + file.println(imapData.getAttachmentFileSize(mailIndex, j)); + + file.print(ESP32_MAIL_STR_118); + file.println(imapData.getAttachmentType(mailIndex, j)); + + file.print(ESP32_MAIL_STR_119); + file.println(imapData.getAttachmentCreationDate(mailIndex, j)); + } + } + + file.close(); + imapData._headerSaved = true; + } + + if (imapCommandType == IMAP_COMMAND_TYPE::FETCH_BODY_HEADER) + { + if (from != "") + { + imapData._msgID[mailIndex] = msgID; + imapData._from[mailIndex] = from; + imapData._to[mailIndex] = to; + imapData._cc[mailIndex] = cc; + imapData._subject[mailIndex] = subject; + imapData._date[mailIndex] = date; + imapData._from_charset[mailIndex] = from_charset; + imapData._to_charset[mailIndex] = to_charset; + imapData._cc_charset[mailIndex] = cc_charset; + imapData._subject_charset[mailIndex] = subject_charset; + imapData._contentLanguage[mailIndex] = contentLanguage; + imapData._acceptLanguage[mailIndex] = acceptLanguage; + } + } + + delete[] buf; + delete[] dest; + + std::string().swap(lineBuf); + std::string().swap(msgNumBuf); + std::string().swap(filepath); + std::string().swap(hpath); + std::string().swap(tmp); + + std::string().swap(msgID); + std::string().swap(from); + std::string().swap(to); + std::string().swap(subject); + std::string().swap(date); + std::string().swap(cc); + std::string().swap(from_charset); + std::string().swap(to_charset); + std::string().swap(cc_charset); + std::string().swap(subject_charset); + std::string().swap(contentLanguage); + std::string().swap(acceptLanguage); + + return validResponse; +} + +void ESP32_MailClient::clientReadAll(WiFiClient *client) +{ + if (client) + { + if (client->available() > 0) + client->read(); + } +} + +double ESP32_MailClient::base64DecodeSize(std::string lastBase64String, int length) +{ + double result = 0; + int padding = 0; + if (lastBase64String != "") + { + + if (lastBase64String[lastBase64String.length() - 1] == '=' && lastBase64String[lastBase64String.length() - 2] == '=') + padding = 2; + else if (lastBase64String[lastBase64String.length() - 1] == '=') + padding = 1; + } + result = (ceil(length / 4) * 3) - padding; + return result; +} + +unsigned char *ESP32_MailClient::base64_decode_char(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(base64_table) - 1; i++) + dtable[base64_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 NULL; +} + +std::string ESP32_MailClient::base64_encode_string(const unsigned char *src, size_t len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + + size_t olen; + + olen = 4 * ((len + 2) / 3); + + if (olen < len) + return std::string(); + + std::string outStr = ""; + outStr.resize(olen); + out = (unsigned char *)&outStr[0]; + + end = src + len; + in = src; + pos = out; + + while (end - in >= 3) + { + *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[in[2] & 0x3f]; + in += 3; + } + + if (end - in) + { + *pos++ = base64_table[in[0] >> 2]; + if (end - in == 1) + { + *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } + else + { + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + } + + return outStr; +} + +void ESP32_MailClient::send_base64_encode_mime_data(WiFiClient *client, const unsigned char *src, size_t len) +{ + + const unsigned char *end, *in; + + size_t olen; + + olen = 4 * ((len + 2) / 3); + + if (olen < len) + return; + + end = src + len; + in = src; + + size_t chunkSize = 936; + size_t byteAdd = 0; + size_t byteSent = 0; + + int dByte = 0; + unsigned char *buf = new unsigned char[chunkSize]; + memset(buf, 0, chunkSize); + + while (end - in >= 3) + { + buf[byteAdd++] = base64_table[in[0] >> 2]; + buf[byteAdd++] = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + buf[byteAdd++] = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + buf[byteAdd++] = base64_table[in[2] & 0x3f]; + dByte += 4; + if (dByte == 76) + { + if(byteAdd + 1 < chunkSize) + { + buf[byteAdd++] = 0x0d; + buf[byteAdd++] = 0x0a; + } + dByte = 0; + } + if (byteAdd >= chunkSize - 4) + { + byteSent += byteAdd; + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + byteAdd = 0; + } + in += 3; + } + + if (byteAdd > 0) + client->write(buf, byteAdd); + + if (end - in) + { + memset(buf, 0, chunkSize); + byteAdd = 0; + + buf[byteAdd++] = base64_table[in[0] >> 2]; + if (end - in == 1) + { + buf[byteAdd++] = base64_table[(in[0] & 0x03) << 4]; + buf[byteAdd++] = '='; + } + else + { + buf[byteAdd++] = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + buf[byteAdd++] = base64_table[(in[1] & 0x0f) << 2]; + } + buf[byteAdd++] = '='; + + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + } + delete[] buf; +} + +void ESP32_MailClient::send_base64_encode_mime_file(WiFiClient *client, File file) +{ + + if (!file) + return; + + size_t chunkSize = 936; + size_t byteAdd = 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; + + while (file.available()) + { + memset(fbuf, 0, 3); + if (len - fbufIndex >= 3) + { + file.read(fbuf, 3); + buf[byteAdd++] = base64_table[fbuf[0] >> 2]; + buf[byteAdd++] = base64_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdd++] = base64_table[((fbuf[1] & 0x0f) << 2) | (fbuf[2] >> 6)]; + buf[byteAdd++] = base64_table[fbuf[2] & 0x3f]; + dByte += 4; + if (dByte == 76) + { + if(byteAdd + 1 < chunkSize) + { + buf[byteAdd++] = 0x0d; + buf[byteAdd++] = 0x0a; + } + dByte = 0; + } + if (byteAdd >= chunkSize - 4) + { + byteSent += byteAdd; + client->write(buf, byteAdd); + memset(buf, 0, chunkSize); + byteAdd = 0; + } + fbufIndex += 3; + } + else + { + if (len - fbufIndex == 1) + { + fbuf[0] = file.read(); + } + else if (len - fbufIndex == 2) + { + fbuf[0] = file.read(); + fbuf[1] = file.read(); + } + break; + } + } + + file.close(); + if (byteAdd > 0) + client->write(buf, byteAdd); + + if (len - fbufIndex > 0) + { + memset(buf, 0, chunkSize); + byteAdd = 0; + buf[byteAdd++] = base64_table[fbuf[0] >> 2]; + if (len - fbufIndex == 1) + { + buf[byteAdd++] = base64_table[(fbuf[0] & 0x03) << 4]; + buf[byteAdd++] = '='; + } + else + { + buf[byteAdd++] = base64_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdd++] = base64_table[(fbuf[1] & 0x0f) << 2]; + } + buf[byteAdd++] = '='; + client->write(buf, byteAdd); + } + delete[] buf; + delete[] fbuf; +} + +IMAPData::IMAPData() {} +IMAPData::~IMAPData() +{ + empty(); + _net.reset(); + _net.release(); +} + +void IMAPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); + if (strlen(rootCA) > 0) + _rootCA.push_back((char *)rootCA); +} + +void IMAPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword) +{ + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); +} + +void IMAPData::setSTARTTLS(bool starttls) +{ + _starttls = starttls; +} + +void IMAPData::setDebug(bool debug) +{ + _debug = debug; +} + +void IMAPData::setFolder(const String &folderName) +{ + _currentFolder.clear(); + _currentFolder = folderName.c_str(); +} +void IMAPData::setMessageBufferSize(size_t size) +{ + _message_buffer_size = size; +} + +void IMAPData::setAttachmentSizeLimit(size_t size) +{ + _attacement_max_size = size; +} + +void IMAPData::setSearchCriteria(const String &criteria) +{ + _searchCriteria.clear(); + _searchCriteria = criteria.c_str(); +} + +void IMAPData::setSearchUnseenMessage(bool unseenSearch) +{ + _unseen = unseenSearch; +} + +void IMAPData::setSaveFilePath(const String &path) +{ + _savePath.clear(); + if (path.c_str()[0] != '/') + { + _savePath = "/"; + _savePath += path.c_str(); + } + else + _savePath = path.c_str(); +} + +void IMAPData::setFetchUID(const String &fetchUID) +{ + _fetchUID.clear(); + string tmp = fetchUID.c_str(); + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::toupper); + if (tmp.find(ESP32_MAIL_STR_140) != std::string::npos || tmp.find(ESP32_MAIL_STR_212) != std::string::npos || + tmp.find(ESP32_MAIL_STR_213) != std::string::npos || tmp.find(ESP32_MAIL_STR_214) != std::string::npos || tmp.find(ESP32_MAIL_STR_215) != std::string::npos || + tmp.find(ESP32_MAIL_STR_216) != std::string::npos || tmp.find(ESP32_MAIL_STR_217) != std::string::npos || tmp.find(ESP32_MAIL_STR_218) != std::string::npos || + tmp.find(ESP32_MAIL_STR_219) != std::string::npos || tmp.find(ESP32_MAIL_STR_220) != std::string::npos) + _fetchUID = ESP32_MAIL_STR_183; + else + _fetchUID = fetchUID.c_str(); + + std::string().swap(tmp); +} + +void IMAPData::setFileStorageType(uint8_t storageType) +{ + _storageType = storageType; +} + +void IMAPData::setDownloadAttachment(bool download) +{ + _downloadAttachment = download; +} +void IMAPData::setRecentSort(bool recentSort) +{ + _recentSort = recentSort; +} + +void IMAPData::setHTMLMessage(bool htmlFormat) +{ + _htmlFormat = htmlFormat; +} +void IMAPData::setTextMessage(bool textFormat) +{ + _textFormat = textFormat; +} + +void IMAPData::setSearchLimit(uint16_t limit) +{ + if (limit <= MAX_EMAIL_SEARCH_LIMIT) + _emailNumMax = limit; +} + +bool IMAPData::isHeaderOnly() +{ + return _headerOnly; +} + +void IMAPData::saveHTMLMessage(bool download, bool decoded) +{ + _saveDecodedHTML = decoded; + _saveHTMLMsg = download; +} +void IMAPData::saveTextMessage(bool download, bool decoded) +{ + _saveDecodedText = decoded; + _saveTextMsg = download; +} + +void IMAPData::setReadCallback(readStatusCallback readCallback) +{ + _readCallback = std::move(readCallback); +} + +void IMAPData::setDownloadReport(bool report) +{ + _downloadReport = report; +} + +uint16_t IMAPData::getFolderCount() +{ + return _folders.size(); +} +String IMAPData::getFolder(uint16_t folderIndex) +{ + if (folderIndex < _folders.size()) + return _folders[folderIndex].c_str(); + return std::string().c_str(); +} + +uint16_t IMAPData::getFlagCount() +{ + return _flag.size(); +} +String IMAPData::getFlag(uint16_t flagIndex) +{ + if (flagIndex < _flag.size()) + return _flag[flagIndex].c_str(); + return std::string().c_str(); +} + +size_t IMAPData::totalMessages() +{ + return _totalMessage; +} + +size_t IMAPData::searchCount() +{ + return _searchCount; +} + +size_t IMAPData::availableMessages() +{ + return _msgNum.size(); +} + +size_t IMAPData::getAttachmentCount(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _attachmentCount[messageIndex]; + return 0; +} + +String IMAPData::getAttachmentFileName(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._filename.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getAttachmentName(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._name.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +int IMAPData::getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._size; + id++; + } + } + } + else + return 0; + } + else + return 0; + + return 0; +} + +String IMAPData::getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._creation_date.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getAttachmentType(size_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._contentType.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getFrom(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _from[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getFromCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _from_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getTo(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _to[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getToCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _to_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getCC(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _cc[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getCCCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _cc_charset[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getSubject(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _subject[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getSubjectCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _subject_charset[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getHTMLMessage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return getMessage(messageIndex, true); + return std::string().c_str(); +} + +String IMAPData::getTextMessage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return getMessage(messageIndex, false); + return std::string().c_str(); +} + +String IMAPData::getMessage(uint16_t messageIndex, bool htmlFormat) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_155 && !htmlFormat) + return _messageDataInfo[messageIndex][i]._text.c_str(); + else if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_154 && htmlFormat) + return _messageDataInfo[messageIndex][i]._text.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getHTMLMessgaeCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_154) + return _messageDataInfo[messageIndex][i]._charset.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getTextMessgaeCharset(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._contentType == ESP32_MAIL_STR_155) + return _messageDataInfo[messageIndex][i]._charset.c_str(); + } + return std::string().c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +String IMAPData::getDate(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _date[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getUID(uint16_t messageIndex) +{ + char *buf = new char[50]; + memset(buf, 0, 50); + if (_uidSearch) + { + if (messageIndex < _msgNum.size()) + itoa(_msgNum[messageIndex], buf, 10); + } + + String v = buf; + delete[] buf; + return v; +} + +String IMAPData::getNumber(uint16_t messageIndex) +{ + char *buf = new char[50]; + memset(buf, 0, 50); + + if (messageIndex < _msgNum.size()) + { + if (!_uidSearch) + itoa(_msgNum[messageIndex], buf, 10); + else + itoa(_msgNum[messageIndex] + 1, buf, 10); + } + + String v = buf; + delete[] buf; + return v; +} + +String IMAPData::getMessageID(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _msgID[messageIndex].c_str(); + return std::string().c_str(); +} + +String IMAPData::getAcceptLanguage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _acceptLanguage[messageIndex].c_str(); + return std::string().c_str(); +} +String IMAPData::getContentLanguage(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _contentLanguage[messageIndex].c_str(); + return std::string().c_str(); +} + +bool IMAPData::isFetchMessageFailed(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _error[messageIndex]; + return false; +} +String IMAPData::getFetchMessageFailedReason(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + return _errorMsg[messageIndex].c_str(); + return std::string().c_str(); +} + +bool IMAPData::isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._error; + id++; + } + } + } + else + return false; + } + else + return false; + + return false; +} + +String IMAPData::getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + int id = 0; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == ESP32_MAIL_STR_153) + { + if (attachmentIndex == id) + return _messageDataInfo[messageIndex][i]._downloadError.c_str(); + id++; + } + } + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + return std::string().c_str(); +} + +bool IMAPData::isDownloadMessageFailed(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + bool res = false; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == "") + { + res |= _messageDataInfo[messageIndex][i]._error; + } + } + + return res; + } + else + return false; + } + else + return false; + + return false; +} +String IMAPData::getDownloadMessageFailedReason(uint16_t messageIndex) +{ + if (messageIndex < _msgNum.size()) + { + int s = _messageDataInfo[messageIndex].size(); + string res = ""; + if (s > 0) + { + for (int i = 0; i < s; i++) + { + if (_messageDataInfo[messageIndex][i]._disposition == "") + { + if (_messageDataInfo[messageIndex][i]._downloadError != "") + res = _messageDataInfo[messageIndex][i]._downloadError; + } + } + + return res.c_str(); + } + else + return std::string().c_str(); + } + else + return std::string().c_str(); + + return std::string().c_str(); +} + +void IMAPData::empty() +{ + std::string().swap(_host); + std::string().swap(_loginEmail); + std::string().swap(_loginPassword); + std::string().swap(_currentFolder); + std::string().swap(_nextUID); + std::string().swap(_searchCriteria); + std::vector().swap(_date); + std::vector().swap(_subject); + std::vector().swap(_subject_charset); + std::vector().swap(_from); + std::vector().swap(_from_charset); + std::vector().swap(_to); + std::vector().swap(_to_charset); + std::vector().swap(_cc); + std::vector().swap(_cc_charset); + std::vector().swap(_msgNum); + std::vector().swap(_folders); + std::vector().swap(_flag); + std::vector().swap(_msgID); + std::vector().swap(_acceptLanguage); + std::vector().swap(_contentLanguage); + std::vector().swap(_attachmentCount); + std::vector().swap(_totalAttachFileSize); + std::vector().swap(_downloadedByte); + std::vector().swap(_error); + std::vector>().swap(_messageDataInfo); + std::vector().swap(_errorMsg); +} + +void IMAPData::clearMessageData() +{ + std::vector().swap(_date); + std::vector().swap(_subject); + std::vector().swap(_subject_charset); + std::vector().swap(_from); + std::vector().swap(_from_charset); + std::vector().swap(_to); + std::vector().swap(_to_charset); + std::vector().swap(_cc); + std::vector().swap(_cc_charset); + std::vector().swap(_msgNum); + std::vector().swap(_msgID); + std::vector().swap(_contentLanguage); + std::vector().swap(_acceptLanguage); + std::vector().swap(_folders); + std::vector().swap(_flag); + std::vector().swap(_attachmentCount); + std::vector>().swap(_messageDataInfo); + std::vector().swap(_totalAttachFileSize); + std::vector().swap(_downloadedByte); + std::vector().swap(_messageDataCount); + std::vector().swap(_errorMsg); + std::vector().swap(_error); + _searchCount = 0; +} + +messageBodyData::messageBodyData() +{ +} +messageBodyData::~messageBodyData() +{ + empty(); +} + +void messageBodyData::empty() +{ + std::string().swap(_text); + std::string().swap(_filename); + std::string().swap(_savePath); + std::string().swap(_name); + std::string().swap(_disposition); + std::string().swap(_contentType); + std::string().swap(_descr); + std::string().swap(_transfer_encoding); + std::string().swap(_creation_date); + std::string().swap(_modification_date); + std::string().swap(_charset); + std::string().swap(_part); + std::string().swap(_downloadError); +} + +attachmentData::attachmentData() {} +attachmentData::~attachmentData() +{ + + std::vector>().swap(_buf); + std::vector().swap(_filename); + std::vector().swap(_id); + std::vector().swap(_type); + std::vector().swap(_size); + std::vector().swap(_mime_type); +} + +void attachmentData::add(const String &fileName, const String &mimeType, uint8_t *data, size_t size) +{ + _filename.push_back(fileName.c_str()); + _mime_type.push_back(mimeType.c_str()); + + if (size > 0) + { + std::vector d = std::vector(); + d.push_back(data); + _buf.push_back(d); + _size.push_back(size); + _type.push_back(0); + } + else + { + _buf.push_back(std::vector()); + _size.push_back(0); + _type.push_back(1); + } + + _id.push_back(_index); + _index++; +} + +void attachmentData::remove(uint8_t index) +{ + _buf.erase(_buf.begin() + index); + _filename.erase(_filename.begin() + index); + _type.erase(_type.begin() + index); + _size.erase(_size.begin() + index); + _mime_type.erase(_mime_type.begin() + index); + _id.erase(_id.begin() + index); +} + +void attachmentData::free() +{ + std::vector>().swap(_buf); + std::vector().swap(_filename); + std::vector().swap(_id); + std::vector().swap(_type); + std::vector().swap(_size); + std::vector().swap(_mime_type); + _index = 0; +} + +String attachmentData::getFileName(uint8_t index) +{ + return _filename[index].c_str(); +} + +String attachmentData::getMimeType(uint8_t index) +{ + return _mime_type[index].c_str(); +} + +uint8_t *attachmentData::getData(uint8_t index) +{ + uint8_t *ptr = _buf[index].front(); + return ptr; +} + +uint16_t attachmentData::getSize(uint8_t index) +{ + return _size[index]; +} + +uint8_t attachmentData::getCount() +{ + return _index; +} + +uint8_t attachmentData::getType(uint8_t index) +{ + return _type[index]; +} + +SMTPData::SMTPData() {} + +SMTPData::~SMTPData() +{ + empty(); + _net.reset(); + _net.release(); +} + +void SMTPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); + if (strlen(rootCA) > 0) + _rootCA.push_back((char *)rootCA); +} + +void SMTPData::setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword) +{ + + _host.clear(); + _port = port; + _loginEmail.clear(); + _loginPassword.clear(); + + _host = host.c_str(); + _loginEmail = loginEmail.c_str(); + _loginPassword = loginPassword.c_str(); + + _rootCA.clear(); +} + +void SMTPData::setSTARTTLS(bool starttls) +{ + _starttls = starttls; +} + +void SMTPData::setDebug(bool debug) +{ + _debug = debug; +} + +void SMTPData::setSender(const String &fromName, const String &senderEmail) +{ + + _fromName.clear(); + _senderEmail.clear(); + + _fromName += fromName.c_str(); + _senderEmail += senderEmail.c_str(); +} + +String SMTPData::getFromName() +{ + return _fromName.c_str(); +} + +String SMTPData::getSenderEmail() +{ + return _senderEmail.c_str(); +} + +void SMTPData::setPriority(int priority) +{ + _priority = priority; +} +void SMTPData::setPriority(const String &priority) +{ + if (priority == ESP32_MAIL_STR_205 || priority == ESP32_MAIL_STR_206) + _priority = 1; + else if (priority == ESP32_MAIL_STR_207 || priority == ESP32_MAIL_STR_208) + _priority = 3; + else if (priority == ESP32_MAIL_STR_209 || priority == ESP32_MAIL_STR_210) + _priority = 5; +} + +uint8_t SMTPData::getPriority() +{ + return _priority; +} + +void SMTPData::addRecipient(const String &email) +{ + _recipient.insert(_recipient.end(), email.c_str()); +} + +void SMTPData::removeRecipient(const String &email) +{ + for (uint8_t i = 0; i < _recipient.size(); i++) + if (_recipient[i].c_str() == email.c_str()) + _recipient.erase(_recipient.begin() + i); +} + +void SMTPData::removeRecipient(uint8_t index) +{ + _recipient.erase(_recipient.begin() + index); +} + +void SMTPData::clearRecipient() +{ + std::vector().swap(_recipient); +} + +uint8_t SMTPData::recipientCount() +{ + return _recipient.size(); +} + +String SMTPData::getRecipient(uint8_t index) +{ + if (index >= _recipient.size()) + return std::string().c_str(); + return _recipient[index].c_str(); +} + +void SMTPData::setSubject(const String &subject) +{ + _subject = subject.c_str(); +} + +String SMTPData::getSubject() +{ + return _subject.c_str(); +} + +void SMTPData::setMessage(const String &message, bool htmlFormat) +{ + _message.clear(); + _message += message.c_str(); + _htmlFormat = htmlFormat; +} + +void SMTPData::clrMessage(bool htmlFormat) +{ + _message.clear(); + _htmlFormat = htmlFormat; +} + +void SMTPData::addMessage(const String &message) +{ + _message += message.c_str(); +} + +String SMTPData::getMessage() +{ + return _message.c_str(); +} + +bool SMTPData::htmlFormat() +{ + return _htmlFormat; +} +void SMTPData::addCC(const String &email) +{ + _cc.push_back(email.c_str()); +} + +void SMTPData::removeCC(const String &email) +{ + for (uint8_t i = 0; i < _cc.size(); i++) + if (_cc[i].c_str() == email.c_str()) + _cc.erase(_cc.begin() + i); +} + +void SMTPData::removeCC(uint8_t index) +{ + _cc.erase(_cc.begin() + index); +} +void SMTPData::clearCC() +{ + std::vector().swap(_cc); +} + +uint8_t SMTPData::ccCount() +{ + return _cc.size(); +} + +String SMTPData::getCC(uint8_t index) +{ + if (index >= _cc.size()) + return std::string().c_str(); + return _cc[index].c_str(); +} + +void SMTPData::addBCC(const String &email) +{ + _bcc.push_back(email.c_str()); +} + +void SMTPData::removeBCC(const String &email) +{ + for (uint8_t i = 0; i < _bcc.size(); i++) + if (_bcc[i].c_str() == email.c_str()) + _bcc.erase(_bcc.begin() + i); +} + +void SMTPData::removeBCC(uint8_t index) +{ + _bcc.erase(_bcc.begin() + index); +} + +void SMTPData::clearBCC() +{ + std::vector().swap(_bcc); +} + +uint8_t SMTPData::bccCount() +{ + return _bcc.size(); +} + +String SMTPData::getBCC(uint8_t index) +{ + if (index >= _bcc.size()) + return std::string().c_str(); + return _bcc[index].c_str(); +} + +void SMTPData::addAttachData(const String &fileName, const String &mimeType, uint8_t *data, size_t size) +{ + _attach.add(fileName, mimeType, data, size); +} + +void SMTPData::removeAttachData(const String &fileName) +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getFileName(i) == fileName && _attach.getType(i) == 0) + { + _attach.remove(i); + } +} + +void SMTPData::removeAttachData(uint8_t index) +{ + uint8_t id = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + { + if (id == index) + { + _attach.remove(i); + break; + } + id++; + } +} + +uint8_t SMTPData::attachDataCount() +{ + uint8_t count = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + count++; + + return count; +} + +void SMTPData::addAttachFile(const String &filePath, const String &mimeType) +{ + _attach.add(filePath, mimeType, NULL, 0); +} + +void SMTPData::removeAttachFile(const String &filePath) +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getFileName(i) == filePath && _attach.getType(i) == 1) + { + _attach.remove(i); + } +} + +void SMTPData::removeAttachFile(uint8_t index) +{ + uint8_t id = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + { + if (id == index) + { + _attach.remove(i); + break; + } + id++; + } +} + +void SMTPData::setFileStorageType(uint8_t storageType) +{ + _storageType = storageType; +} + +void SMTPData::clearAttachData() +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 0) + _attach.remove(i); +} + +void SMTPData::clearAttachFile() +{ + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + _attach.remove(i); +} + +void SMTPData::clearAttachment() +{ + _attach.free(); +} + +uint8_t SMTPData::attachFileCount() +{ + uint8_t count = 0; + for (uint8_t i = 0; i < _attach.getCount(); i++) + if (_attach.getType(i) == 1) + count++; + + return count; +} + +void SMTPData::addCustomMessageHeader(const String &commmand) +{ + _customMessageHeader.insert(_customMessageHeader.end(), commmand.c_str()); +} + +void SMTPData::removeCustomMessageHeader(const String &commmand) +{ + for (uint8_t i = 0; i < _customMessageHeader.size(); i++) + if (_customMessageHeader[i].c_str() == commmand.c_str()) + _customMessageHeader.erase(_customMessageHeader.begin() + i); +} + +void SMTPData::removeCustomMessageHeader(uint8_t index) +{ + _customMessageHeader.erase(_customMessageHeader.begin() + index); +} + +void SMTPData::clearCustomMessageHeader() +{ + std::vector().swap(_customMessageHeader); +} + +uint8_t SMTPData::CustomMessageHeaderCount() +{ + return _customMessageHeader.size(); +} + +String SMTPData::getCustomMessageHeader(uint8_t index) +{ + if (index >= _customMessageHeader.size()) + return std::string().c_str(); + return _customMessageHeader[index].c_str(); +} + +void SMTPData::empty() +{ + std::string().swap(_host); + std::string().swap(_loginEmail); + std::string().swap(_loginPassword); + std::string().swap(_fromName); + std::string().swap(_senderEmail); + std::string().swap(_subject); + std::string().swap(_message); + clearRecipient(); + clearCustomMessageHeader(); + clearCC(); + clearBCC(); + clearAttachment(); +} + +void SMTPData::setSendCallback(sendStatusCallback sendCallback) +{ + _sendCallback = std::move(sendCallback); +} + +ReadStatus::ReadStatus() +{ +} +ReadStatus::~ReadStatus() +{ + empty(); +} + +String ReadStatus::status() +{ + return _status.c_str(); +} +String ReadStatus::info() +{ + return _info.c_str(); +} + +bool ReadStatus::success() +{ + return _success; +} +void ReadStatus::empty() +{ + std::string().swap(_info); + std::string().swap(_status); +} + +SendStatus::SendStatus() +{ +} + +SendStatus::~SendStatus() +{ + empty(); +} + +String SendStatus::info() +{ + return _info.c_str(); +} +bool SendStatus::success() +{ + return _success; +} +void SendStatus::empty() +{ + std::string().swap(_info); +} + +ESP32_MailClient MailClient = ESP32_MailClient(); + +#endif //ESP32 + +#endif //ESP32_MailClient_CPP diff --git a/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h new file mode 100755 index 000000000..943cd62f7 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ESP32_MailClient.h @@ -0,0 +1,1908 @@ +/* + *Mail Client Arduino Library for ESP32, version 2.1.4 + * + * April 12, 2020 + * + * This library allows ESP32 to send Email with/without attachment and receive Email with/without attachment download through SMTP and IMAP servers. + * + * The library supports all ESP32 MCU based modules. + * + * The MIT License (MIT) + * Copyright (c) 2019 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 ESP32_MailClient_H +#define ESP32_MailClient_H + +#ifdef ESP32 + +#include +#include "WiFiClientSecureESP32.h" +#include +#include +#include +#include +#include +#include +#include +#include "RFC2047.h" +#include "ESP32MailHTTPClient.h" +#include "ESP32TimeHelper.h" + +#define FORMAT_SPIFFS_IF_FAILED true + +static RFC2047 RFC2047Decoder; + +using namespace std; + +#define SMTP_STATUS_SERVER_CONNECT_FAILED 1 +#define SMTP_STATUS_SMTP_RESPONSE_FAILED 2 +#define SMTP_STATUS_IDENTIFICATION_FAILED 3 +#define SMTP_STATUS_AUTHEN_NOT_SUPPORT 4 +#define SMTP_STATUS_AUTHEN_FAILED 5 +#define SMTP_STATUS_USER_LOGIN_FAILED 6 +#define SMTP_STATUS_PASSWORD_LOGIN_FAILED 7 +#define SMTP_STATUS_SEND_HEADER_SENDER_FAILED 8 +#define SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED 9 +#define SMTP_STATUS_SEND_BODY_FAILED 10 + +#define IMAP_STATUS_SERVER_CONNECT_FAILED 1 +#define IMAP_STATUS_IMAP_RESPONSE_FAILED 2 +#define IMAP_STATUS_LOGIN_FAILED 3 +#define IMAP_STATUS_BAD_COMMAND 4 +#define IMAP_STATUS_PARSE_FLAG_FAILED 5 + +#define MAIL_CLIENT_STATUS_WIFI_CONNECT_FAIL 100 + +#define MAX_EMAIL_SEARCH_LIMIT 1000 + +static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +class IMAPData; +class SMTPData; +class attachmentData; +class SendStatus; +class messageBodyData; +class DownloadProgress; +class MessageData; + +struct MailClientStorageType +{ + static const uint8_t SPIFFS = 0; + static const uint8_t SD = 1; +}; + +static const char ESP32_MAIL_STR_1[] PROGMEM = "Content-Type: multipart/mixed; boundary=\""; +static const char ESP32_MAIL_STR_2[] PROGMEM = "{BOUNDARY}"; +static const char ESP32_MAIL_STR_3[] PROGMEM = "Mime-Version: 1.0\r\n"; +static const char ESP32_MAIL_STR_4[] PROGMEM = "AUTH LOGIN"; +static const char ESP32_MAIL_STR_5[] PROGMEM = "HELO dude"; +static const char ESP32_MAIL_STR_6[] PROGMEM = "EHLO dude"; +static const char ESP32_MAIL_STR_7[] PROGMEM = "QUIT"; +static const char ESP32_MAIL_STR_8[] PROGMEM = "MAIL FROM:"; +static const char ESP32_MAIL_STR_9[] PROGMEM = "RCPT TO:"; +static const char ESP32_MAIL_STR_10[] PROGMEM = "From: "; +static const char ESP32_MAIL_STR_11[] PROGMEM = "To: "; +static const char ESP32_MAIL_STR_12[] PROGMEM = "Cc: "; +static const char ESP32_MAIL_STR_13[] PROGMEM = ",<"; +static const char ESP32_MAIL_STR_14[] PROGMEM = "<"; +static const char ESP32_MAIL_STR_15[] PROGMEM = ">"; +static const char ESP32_MAIL_STR_16[] PROGMEM = "DATA"; +static const char ESP32_MAIL_STR_17[] PROGMEM = "X-Priority: "; +static const char ESP32_MAIL_STR_18[] PROGMEM = "X-MSMail-Priority: High\r\n"; +static const char ESP32_MAIL_STR_19[] PROGMEM = "X-MSMail-Priority: Normal\r\n"; +static const char ESP32_MAIL_STR_20[] PROGMEM = "X-MSMail-Priority: Low\r\n"; +static const char ESP32_MAIL_STR_21[] PROGMEM = "Importance: High\r\n"; +static const char ESP32_MAIL_STR_22[] PROGMEM = "Importance: Normal\r\n"; +static const char ESP32_MAIL_STR_23[] PROGMEM = "Importance: Low\r\n"; +static const char ESP32_MAIL_STR_24[] PROGMEM = "Subject: "; +static const char ESP32_MAIL_STR_25[] PROGMEM = "Content-Type: "; +static const char ESP32_MAIL_STR_26[] PROGMEM = "; Name=\""; +static const char ESP32_MAIL_STR_27[] PROGMEM = "Content-type: text/plain; charset=us-ascii\r\n"; +static const char ESP32_MAIL_STR_28[] PROGMEM = "Content-type: text/html; charset=\"UTF-8\"\r\n"; +static const char ESP32_MAIL_STR_29[] PROGMEM = "Content-transfer-encoding: 7bit\r\n"; +static const char ESP32_MAIL_STR_30[] PROGMEM = "Content-Disposition: attachment; filename=\""; +static const char ESP32_MAIL_STR_31[] PROGMEM = "Content-transfer-encoding: base64\r\n"; +static const char ESP32_MAIL_STR_32[] PROGMEM = "application/octet-stream"; +static const char ESP32_MAIL_STR_33[] PROGMEM = "--"; +static const char ESP32_MAIL_STR_34[] PROGMEM = "\r\n"; +static const char ESP32_MAIL_STR_35[] PROGMEM = "\"\r\n\r\n"; +static const char ESP32_MAIL_STR_36[] PROGMEM = "\"\r\n"; +static const char ESP32_MAIL_STR_37[] PROGMEM = "\r\n.\r\n"; +static const char ESP32_MAIL_STR_38[] PROGMEM = "could not connect to server"; +static const char ESP32_MAIL_STR_39[] PROGMEM = "could not handle SMTP server response"; +static const char ESP32_MAIL_STR_40[] PROGMEM = "could not handle IMAP server response"; +static const char ESP32_MAIL_STR_41[] PROGMEM = "identification failed"; +static const char ESP32_MAIL_STR_42[] PROGMEM = "authentication is not support"; +static const char ESP32_MAIL_STR_43[] PROGMEM = "authentication failed"; +static const char ESP32_MAIL_STR_44[] PROGMEM = "login account is not valid"; +static const char ESP32_MAIL_STR_45[] PROGMEM = "could not sigin"; +static const char ESP32_MAIL_STR_46[] PROGMEM = "could not parse command"; +static const char ESP32_MAIL_STR_47[] PROGMEM = "login password is not valid"; +static const char ESP32_MAIL_STR_48[] PROGMEM = "send header failed"; +static const char ESP32_MAIL_STR_49[] PROGMEM = "send body failed"; +static const char ESP32_MAIL_STR_50[] PROGMEM = "Connecting to IMAP server..."; +static const char ESP32_MAIL_STR_51[] PROGMEM = "initialize"; +static const char ESP32_MAIL_STR_52[] PROGMEM = "failed"; +static const char ESP32_MAIL_STR_53[] PROGMEM = "Error, "; +static const char ESP32_MAIL_STR_54[] PROGMEM = "IMAP server connected"; +static const char ESP32_MAIL_STR_55[] PROGMEM = "server_connected"; +static const char ESP32_MAIL_STR_56[] PROGMEM = "Sign in..."; +static const char ESP32_MAIL_STR_57[] PROGMEM = "signin"; +static const char ESP32_MAIL_STR_58[] PROGMEM = "Lising folders..."; +static const char ESP32_MAIL_STR_59[] PROGMEM = "listing"; +static const char ESP32_MAIL_STR_60[] PROGMEM = ":::::::Message folders:::::::"; +static const char ESP32_MAIL_STR_61[] PROGMEM = "Reading "; +static const char ESP32_MAIL_STR_62[] PROGMEM = "Predicted next UID: "; +static const char ESP32_MAIL_STR_63[] PROGMEM = "Total Message: "; +static const char ESP32_MAIL_STR_64[] PROGMEM = "::::::::::::Flags::::::::::::"; +static const char ESP32_MAIL_STR_65[] PROGMEM = "::::::::::Messages:::::::::::"; +static const char ESP32_MAIL_STR_66[] PROGMEM = "Searching messages..."; +static const char ESP32_MAIL_STR_67[] PROGMEM = "searching"; +static const char ESP32_MAIL_STR_68[] PROGMEM = "Search limit:"; +static const char ESP32_MAIL_STR_69[] PROGMEM = "Found "; +static const char ESP32_MAIL_STR_70[] PROGMEM = " messages"; +static const char ESP32_MAIL_STR_71[] PROGMEM = "Show "; +static const char ESP32_MAIL_STR_72[] PROGMEM = "Could not found any Email for defined criteria"; +static const char ESP32_MAIL_STR_73[] PROGMEM = "Search criteria is not set, fetch the recent message"; +static const char ESP32_MAIL_STR_74[] PROGMEM = "Feching message "; +static const char ESP32_MAIL_STR_75[] PROGMEM = ", UID: "; +static const char ESP32_MAIL_STR_76[] PROGMEM = ", Number: "; +static const char ESP32_MAIL_STR_77[] PROGMEM = "fetching"; +static const char ESP32_MAIL_STR_78[] PROGMEM = "Attachment ("; +static const char ESP32_MAIL_STR_79[] PROGMEM = ")"; +static const char ESP32_MAIL_STR_80[] PROGMEM = "Downloading attachments..."; +static const char ESP32_MAIL_STR_81[] PROGMEM = "downloading"; +static const char ESP32_MAIL_STR_82[] PROGMEM = " bytes"; +static const char ESP32_MAIL_STR_83[] PROGMEM = " - "; +static const char ESP32_MAIL_STR_84[] PROGMEM = "Free Heap: "; +static const char ESP32_MAIL_STR_85[] PROGMEM = "Sign out..."; +static const char ESP32_MAIL_STR_86[] PROGMEM = "signout"; +static const char ESP32_MAIL_STR_87[] PROGMEM = "Finished"; +static const char ESP32_MAIL_STR_88[] PROGMEM = "finished"; +static const char ESP32_MAIL_STR_89[] PROGMEM = "SD card mount failed"; +static const char ESP32_MAIL_STR_90[] PROGMEM = "download "; +static const char ESP32_MAIL_STR_91[] PROGMEM = ", "; +static const char ESP32_MAIL_STR_92[] PROGMEM = "%"; +static const char ESP32_MAIL_STR_93[] PROGMEM = "connection timeout"; +static const char ESP32_MAIL_STR_94[] PROGMEM = "WiFi connection lost"; +static const char ESP32_MAIL_STR_95[] PROGMEM = "no server response"; +static const char ESP32_MAIL_STR_96[] PROGMEM = "finished"; +static const char ESP32_MAIL_STR_97[] PROGMEM = " folder..."; +static const char ESP32_MAIL_STR_98[] PROGMEM = "Finished"; +static const char ESP32_MAIL_STR_99[] PROGMEM = "Date: "; +static const char ESP32_MAIL_STR_100[] PROGMEM = "Messsage UID: "; +static const char ESP32_MAIL_STR_101[] PROGMEM = "Messsage ID: "; +static const char ESP32_MAIL_STR_102[] PROGMEM = "Accept Language: "; +static const char ESP32_MAIL_STR_103[] PROGMEM = "Content Language: "; +static const char ESP32_MAIL_STR_104[] PROGMEM = "From: "; +static const char ESP32_MAIL_STR_105[] PROGMEM = "From Charset: "; +static const char ESP32_MAIL_STR_106[] PROGMEM = "To: "; +static const char ESP32_MAIL_STR_107[] PROGMEM = "To Charset: "; +static const char ESP32_MAIL_STR_108[] PROGMEM = "CC: "; +static const char ESP32_MAIL_STR_109[] PROGMEM = "CC Charset: "; +static const char ESP32_MAIL_STR_110[] PROGMEM = "Subject: "; +static const char ESP32_MAIL_STR_111[] PROGMEM = "Subject Charset: "; +static const char ESP32_MAIL_STR_112[] PROGMEM = "Message Charset: "; +static const char ESP32_MAIL_STR_113[] PROGMEM = "Attachment: "; +static const char ESP32_MAIL_STR_114[] PROGMEM = "File Index: "; +static const char ESP32_MAIL_STR_115[] PROGMEM = "Filename: "; +static const char ESP32_MAIL_STR_116[] PROGMEM = "Name: "; +static const char ESP32_MAIL_STR_117[] PROGMEM = "Size: "; +static const char ESP32_MAIL_STR_118[] PROGMEM = "Type: "; +static const char ESP32_MAIL_STR_119[] PROGMEM = "Creation Date: "; +static const char ESP32_MAIL_STR_120[] PROGMEM = "Connecting to SMTP server..."; +static const char ESP32_MAIL_STR_121[] PROGMEM = "SMTP server connected, wait for response..."; +static const char ESP32_MAIL_STR_122[] PROGMEM = "Identification..."; +static const char ESP32_MAIL_STR_123[] PROGMEM = "Authentication..."; +static const char ESP32_MAIL_STR_124[] PROGMEM = "Sign in..."; +static const char ESP32_MAIL_STR_125[] PROGMEM = "Sending Email header..."; +static const char ESP32_MAIL_STR_126[] PROGMEM = "Sending Email body..."; +static const char ESP32_MAIL_STR_127[] PROGMEM = "Sending attachments..."; +static const char ESP32_MAIL_STR_128[] PROGMEM = "Finalize..."; +static const char ESP32_MAIL_STR_129[] PROGMEM = "Finished\r\nEmail sent successfully"; +static const char ESP32_MAIL_STR_130[] PROGMEM = "$ LOGIN "; +static const char ESP32_MAIL_STR_131[] PROGMEM = " "; +static const char ESP32_MAIL_STR_132[] PROGMEM = " OK "; +static const char ESP32_MAIL_STR_133[] PROGMEM = "$ LIST \"\" \"*\""; +static const char ESP32_MAIL_STR_134[] PROGMEM = "CAPABILITY"; +static const char ESP32_MAIL_STR_135[] PROGMEM = "$ EXAMINE \""; +static const char ESP32_MAIL_STR_136[] PROGMEM = "\""; +static const char ESP32_MAIL_STR_137[] PROGMEM = "UID "; +static const char ESP32_MAIL_STR_138[] PROGMEM = " UID"; +static const char ESP32_MAIL_STR_139[] PROGMEM = " SEARCH"; +static const char ESP32_MAIL_STR_140[] PROGMEM = "UID"; +static const char ESP32_MAIL_STR_141[] PROGMEM = "SEARCH"; +static const char ESP32_MAIL_STR_142[] PROGMEM = "$ UID FETCH "; +static const char ESP32_MAIL_STR_143[] PROGMEM = "$ FETCH "; +static const char ESP32_MAIL_STR_144[] PROGMEM = " BODY.PEEK[HEADER.FIELDS (SUBJECT FROM TO DATE Message-ID Accept-Language Content-Language)]"; +static const char ESP32_MAIL_STR_145[] PROGMEM = "IMAP"; +static const char ESP32_MAIL_STR_146[] PROGMEM = "$ LOGOUT"; +static const char ESP32_MAIL_STR_147[] PROGMEM = " BODY.PEEK["; +static const char ESP32_MAIL_STR_148[] PROGMEM = ".MIME]"; +static const char ESP32_MAIL_STR_149[] PROGMEM = "multipart/"; +static const char ESP32_MAIL_STR_150[] PROGMEM = "$ UID FETCH "; +static const char ESP32_MAIL_STR_151[] PROGMEM = " BODY.PEEK["; +static const char ESP32_MAIL_STR_152[] PROGMEM = "."; +static const char ESP32_MAIL_STR_153[] PROGMEM = "attachment"; +static const char ESP32_MAIL_STR_154[] PROGMEM = "text/html"; +static const char ESP32_MAIL_STR_155[] PROGMEM = "text/plain"; +static const char ESP32_MAIL_STR_156[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_157[] PROGMEM = "* ESEARCH"; +static const char ESP32_MAIL_STR_158[] PROGMEM = "$ NO "; +static const char ESP32_MAIL_STR_159[] PROGMEM = "$ BAD "; +static const char ESP32_MAIL_STR_160[] PROGMEM = "base64"; +static const char ESP32_MAIL_STR_161[] PROGMEM = "/decoded_msg.txt"; +static const char ESP32_MAIL_STR_162[] PROGMEM = "/raw_msg.txt"; +static const char ESP32_MAIL_STR_163[] PROGMEM = "/decoded_msg.html"; +static const char ESP32_MAIL_STR_164[] PROGMEM = "/raw_msg.html"; +static const char ESP32_MAIL_STR_165[] PROGMEM = " FETCH "; +static const char ESP32_MAIL_STR_166[] PROGMEM = "* OK "; +static const char ESP32_MAIL_STR_167[] PROGMEM = "content-type: "; +static const char ESP32_MAIL_STR_168[] PROGMEM = "charset=\""; +static const char ESP32_MAIL_STR_169[] PROGMEM = "charset="; +static const char ESP32_MAIL_STR_170[] PROGMEM = "name=\""; +static const char ESP32_MAIL_STR_171[] PROGMEM = "name="; +static const char ESP32_MAIL_STR_172[] PROGMEM = "content-transfer-encoding: "; +static const char ESP32_MAIL_STR_173[] PROGMEM = "\r"; +static const char ESP32_MAIL_STR_174[] PROGMEM = "content-description: "; +static const char ESP32_MAIL_STR_175[] PROGMEM = "content-disposition: "; +static const char ESP32_MAIL_STR_176[] PROGMEM = "filename=\""; +static const char ESP32_MAIL_STR_177[] PROGMEM = "filename="; +static const char ESP32_MAIL_STR_178[] PROGMEM = "size="; +static const char ESP32_MAIL_STR_179[] PROGMEM = "creation-date=\""; +static const char ESP32_MAIL_STR_180[] PROGMEM = "creation-date="; +static const char ESP32_MAIL_STR_181[] PROGMEM = "modification-date=\""; +static const char ESP32_MAIL_STR_182[] PROGMEM = "modification-date="; +static const char ESP32_MAIL_STR_183[] PROGMEM = "*"; +static const char ESP32_MAIL_STR_184[] PROGMEM = "from: "; +static const char ESP32_MAIL_STR_185[] PROGMEM = "to: "; +static const char ESP32_MAIL_STR_186[] PROGMEM = "cc: "; +static const char ESP32_MAIL_STR_187[] PROGMEM = "subject: "; +static const char ESP32_MAIL_STR_188[] PROGMEM = "date: "; +static const char ESP32_MAIL_STR_189[] PROGMEM = "message-id: "; +static const char ESP32_MAIL_STR_190[] PROGMEM = "accept-language: "; +static const char ESP32_MAIL_STR_191[] PROGMEM = "content-language: "; +static const char ESP32_MAIL_STR_192[] PROGMEM = ")"; +static const char ESP32_MAIL_STR_193[] PROGMEM = "{"; +static const char ESP32_MAIL_STR_194[] PROGMEM = "}"; +static const char ESP32_MAIL_STR_195[] PROGMEM = " LIST "; +static const char ESP32_MAIL_STR_196[] PROGMEM = "\\Noselect"; +static const char ESP32_MAIL_STR_197[] PROGMEM = " FLAGS "; +static const char ESP32_MAIL_STR_198[] PROGMEM = "("; +static const char ESP32_MAIL_STR_199[] PROGMEM = " EXISTS"; +static const char ESP32_MAIL_STR_200[] PROGMEM = " [UIDNEXT "; +static const char ESP32_MAIL_STR_201[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_202[] PROGMEM = "/"; +static const char ESP32_MAIL_STR_203[] PROGMEM = "/header.txt"; +static const char ESP32_MAIL_STR_204[] PROGMEM = "/esp.32"; +static const char ESP32_MAIL_STR_205[] PROGMEM = "high"; +static const char ESP32_MAIL_STR_206[] PROGMEM = "High"; +static const char ESP32_MAIL_STR_207[] PROGMEM = "normal"; +static const char ESP32_MAIL_STR_208[] PROGMEM = "Normal"; +static const char ESP32_MAIL_STR_209[] PROGMEM = "low"; +static const char ESP32_MAIL_STR_210[] PROGMEM = "Low"; +static const char ESP32_MAIL_STR_211[] PROGMEM = "$ OK "; +static const char ESP32_MAIL_STR_212[] PROGMEM = "FLAGS"; +static const char ESP32_MAIL_STR_213[] PROGMEM = "BODY"; +static const char ESP32_MAIL_STR_214[] PROGMEM = "PEEK"; +static const char ESP32_MAIL_STR_215[] PROGMEM = "TEXT"; +static const char ESP32_MAIL_STR_216[] PROGMEM = "HEADER"; +static const char ESP32_MAIL_STR_217[] PROGMEM = "FIELDS"; +static const char ESP32_MAIL_STR_218[] PROGMEM = "["; +static const char ESP32_MAIL_STR_219[] PROGMEM = "]"; +static const char ESP32_MAIL_STR_220[] PROGMEM = "MIME"; +static const char ESP32_MAIL_STR_221[] PROGMEM = "connection lost"; +static const char ESP32_MAIL_STR_222[] PROGMEM = "set recipient failed"; +static const char ESP32_MAIL_STR_223[] PROGMEM = " NEW"; +static const char ESP32_MAIL_STR_224[] PROGMEM = "ALL"; + +static const char ESP32_MAIL_STR_225[] PROGMEM = "INFO: connecting to IMAP server..."; +static const char ESP32_MAIL_STR_226[] PROGMEM = "ERROR: could not connect to internet"; +static const char ESP32_MAIL_STR_227[] PROGMEM = "ERROR: "; +static const char ESP32_MAIL_STR_228[] PROGMEM = "INFO: server connected"; +static const char ESP32_MAIL_STR_229[] PROGMEM = "INFO: send imap command LOGIN"; +static const char ESP32_MAIL_STR_230[] PROGMEM = "INFO: send imap command LIST"; +static const char ESP32_MAIL_STR_231[] PROGMEM = "INFO: send imap command EXAMINE"; +static const char ESP32_MAIL_STR_232[] PROGMEM = "INFO: search message"; +static const char ESP32_MAIL_STR_233[] PROGMEM = "INFO: fetch message"; +static const char ESP32_MAIL_STR_234[] PROGMEM = "INFO: send imap command LOGOUT"; +static const char ESP32_MAIL_STR_235[] PROGMEM = "INFO: message fetch completed"; +static const char ESP32_MAIL_STR_236[] PROGMEM = "INFO: connecting to SMTP server..."; +static const char ESP32_MAIL_STR_237[] PROGMEM = "ERROR: could not connect to internet"; +static const char ESP32_MAIL_STR_238[] PROGMEM = "INFO: smtp server connected"; +static const char ESP32_MAIL_STR_239[] PROGMEM = "INFO: send smtp HELO command"; +static const char ESP32_MAIL_STR_240[] PROGMEM = "INFO: send smtp AUTH LOGIN command"; +static const char ESP32_MAIL_STR_241[] PROGMEM = "INFO: log in with username and password"; +static const char ESP32_MAIL_STR_242[] PROGMEM = "INFO: send email header"; +static const char ESP32_MAIL_STR_243[] PROGMEM = "INFO: send email body"; +static const char ESP32_MAIL_STR_244[] PROGMEM = "INFO: send attachment..."; +static const char ESP32_MAIL_STR_245[] PROGMEM = "INFO: finalize..."; +static const char ESP32_MAIL_STR_246[] PROGMEM = "INFO: email sent successfully"; +static const char ESP32_MAIL_STR_247[] PROGMEM = "$ SELECT \""; +static const char ESP32_MAIL_STR_248[] PROGMEM = "INFO: send imap command SELECT"; +static const char ESP32_MAIL_STR_249[] PROGMEM = "$ UID STORE "; +static const char ESP32_MAIL_STR_250[] PROGMEM = " FLAGS ("; +static const char ESP32_MAIL_STR_251[] PROGMEM = " +FLAGS ("; +static const char ESP32_MAIL_STR_252[] PROGMEM = " -FLAGS ("; +static const char ESP32_MAIL_STR_253[] PROGMEM = "INFO: set FLAG"; +static const char ESP32_MAIL_STR_254[] PROGMEM = "INFO: add FLAG"; +static const char ESP32_MAIL_STR_255[] PROGMEM = "INFO: remove FLAG"; +static const char ESP32_MAIL_STR_256[] PROGMEM = "could not parse flag"; +static const char ESP32_MAIL_STR_257[] PROGMEM = "BAD"; + +__attribute__((used)) static bool compFunc(uint32_t i, uint32_t j) +{ + return (i > j); +} + +class ReadStatus +{ +public: + ReadStatus(); + ~ReadStatus(); + String status(); + String info(); + bool success(); + void empty(); + friend IMAPData; + + std::string _status = ""; + std::string _info = ""; + bool _success = false; +}; + +class SendStatus +{ +public: + SendStatus(); + ~SendStatus(); + String info(); + bool success(); + void empty(); + friend SMTPData; + + std::string _info = ""; + bool _success = false; +}; + +typedef void (*readStatusCallback)(ReadStatus); +typedef void (*sendStatusCallback)(SendStatus); + + + +class ESP32_MailClient +{ + +public: + /* + + Sending Email through SMTP server + + @param net - HTTPClientESP32 WiFi client. + + @return Boolean type status indicates the success of operation. + + */ + bool sendMail(SMTPData &smtpData); + + /* + + Reading Email through IMAP server. + + @param imapData - IMAP Data object to hold data and instances. + + @return Boolean type status indicates the success of operation. + + */ + bool readMail(IMAPData &imapData); + + /* + + Set the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool setFlag(IMAPData &imapData, int msgUID, const String &flags); + + + + /* + + Add the argument to the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool addFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Remove the argument from the Flags for message. + + @param imapData - IMAP Data object to hold data and instances. + + @param msgUID - The UID of message. + + @param flags - The flag list. + + + @return Boolean type status indicates the success of operation. + + */ + bool removeFlag(IMAPData &imapData, int msgUID, const String &flags); + + /* + + Get the Email sending error details. + + @return Error details string (String object). + + */ + String smtpErrorReason(); + + /* + + Get the Email reading error details. + + @return Error details string (String object). + + */ + String imapErrorReason(); + + /* + + Init SD card with GPIO pins. + + @param sck - SPI Clock pin. + @param miso - SPI MISO pin. + @param mosi - SPI MOSI pin. + @param ss - SPI Chip/Slave Select pin. + + @return Boolean type status indicates the success of operation. + + */ + bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); + + /* + + Init SD card with default GPIO pins. + + @return Boolean type status indicates the success of operation. + + */ + bool sdBegin(void); + + struct IMAP_COMMAND_TYPE; + struct IMAP_HEADER_TYPE; + + +ESP32TimeHelper Time; + +private: + int _smtpStatus = 0; + int _imapStatus = 0; + bool _sdOk = false; + bool _sdConfigSet = false; + uint8_t _sck, _miso, _mosi, _ss; + unsigned long _lastReconnectMillis = 0; + uint16_t _reconnectTimeout = 10000; + + + std::string smtpErrorReasonStr(); + std::string imapErrorReasonStr(); + void ESP32MailDebugError(); + void ESP32MailDebugInfo(PGM_P info); + void set_message_header(string &header, std::string &message, bool htmlFormat); + void set_attachment_header(uint8_t index, std::string &header, attachmentData &attach); + void clientReadAll(WiFiClient *client); + double base64DecodeSize(std::string lastBase64String, int length); + unsigned char *base64_decode_char(const unsigned char *src, size_t len, size_t *out_len); + std::string base64_encode_string(const unsigned char *src, size_t len); + void send_base64_encode_mime_data(WiFiClient *client, const unsigned char *src, size_t len); + void send_base64_encode_mime_file(WiFiClient *client, File file); + int waitSMTPResponse(SMTPData &smtpData); + bool waitIMAPResponse(IMAPData &imapData, uint8_t imapCommandType = 0, int maxChar = 0, int mailIndex = -1, int messageDataIndex = -1, std ::string part = ""); + bool _setFlag(IMAPData &imapData, int msgUID, const String &flags, uint8_t action); + bool getIMAPResponse(IMAPData &imapData); + void createDirs(std::string dirs); + bool smtpClientAvailable(SMTPData &smtpData, bool available); + bool imapClientAvailable(IMAPData &imapData, bool available); + bool sdTest(); +}; + +class messageBodyData +{ +public: + messageBodyData(); + ~messageBodyData(); + void empty(); + + friend ESP32_MailClient; + friend IMAPData; + +protected: + uint8_t _index = 0; + size_t _size = 0; + std::string _text = ""; + std::string _filename = ""; + std::string _savePath = ""; + std::string _name = ""; + std::string _disposition = ""; + std::string _contentType = ""; + std::string _descr = ""; + std::string _transfer_encoding = ""; + std::string _creation_date = ""; + std::string _modification_date = ""; + std::string _charset = ""; + std::string _part = ""; + std::string _downloadError = ""; + bool _sdFileOpenWrite = false; + bool _error = false; +}; + +class attachmentData +{ +public: + attachmentData(); + ~attachmentData(); + + friend ESP32_MailClient; + friend SMTPData; + +protected: + uint8_t _index = 0; + std::vector> _buf = std::vector>(); + std::vector _filename = std::vector(); + std::vector _id = std::vector(); + std::vector _type = std::vector(); + std::vector _size = std::vector(); + std::vector _mime_type = std::vector(); + + void add(const String &fileName, const String &mimeType, uint8_t *data, size_t size); + void remove(uint8_t index); + void free(); + String getFileName(uint8_t index); + String getMimeType(uint8_t index); + uint8_t *getData(uint8_t index); + uint16_t getSize(uint8_t index); + uint8_t getCount(); + uint8_t getType(uint8_t index); +}; + +class IMAPData +{ +public: + IMAPData(); + ~IMAPData(); + + /* + Set the IMAP server login credentials. + + @param host - IMAP server e.g. imap.gmail.com. + @param port - IMAP port e.g. 993 for gmail. + @param loginEmail - The Email address of account. + @param loginPassword - The account password. + @rootCA - Root CA certificate base64 string + + */ + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + + /* + + Set STARTTLS mode to enable STARTTLS protocol + + @param starttls - bool flag that enables STARTTLS mode + + */ + void setSTARTTLS(bool starttls); + + /* + + Set debug print to serial + + @param debug - bool flag to enable debug + + */ + void setDebug(bool debug); + + /* + + Set the mailbox folder to search or fetch. + + @param folderName - Known mailbox folder. Ddefault value is INBOX + + */ + void setFolder(const String &folderName); + + /* + + Set the maximum message buffer size for text/html result from search or fetch the message. + + @param size - The message size in byte. + + */ + void setMessageBufferSize(size_t size); + + /* + + Set the maximum attachment file size to be downloaded. + + @param size - The attachement file size in byte. + + */ + void setAttachmentSizeLimit(size_t size); + + /* + Set the search criteria used in selected mailbox search. + + In case of message UID was set through setFetchUID function, search operation will not process, + you need to clear message UID by calling imapData.setFetchUID("") to clear. + + @param criteria - Search criteria String. + + If folder is not set, the INBOX folder will be used + + Example: + + "SINCE 10-Feb-2019" will search all messages that received since 10 Feb 2019 + "UID SEARCH ALL" will seach all message which will return the message UID that can be use later for fetch one or more messages. + + + Search criteria can be consisted these keywords + + ALL - All messages in the mailbox; the default initial key for ANDing. + ANSWERED - Messages with the \Answered flag set. + BCC - Messages that contain the specified string in the envelope structure's BCC field. + BEFORE - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. + BODY - Messages that contain the specified string in the body of the message. + CC - Messages that contain the specified string in the envelope structure's CC field. + DELETED - Messages with the \Deleted flag set. + DRAFT - Messages with the \Draft flag set. + FLAGGED - Messages with the \Flagged flag set. + FROM - Messages that contain the specified string in the envelope structure's FROM field. + HEADER - Messages that have a header with the specified field-name (as defined in [RFC-2822]) + and that contains the specified string in the text of the header (what comes after the colon). + + If the string to search is zero-length, this matches all messages that have a header line with + the specified field-name regardless of the contents. + + KEYWORD - Messages with the specified keyword flag set. + LARGER - Messages with an [RFC-2822] size larger than the specified number of octets. + NEW - Messages that have the \Recent flag set but not the \Seen flag. + This is functionally equivalent to "(RECENT UNSEEN)". + NOT - Messages that do not match the specified search key. + OLD - Messages that do not have the \Recent flag set. This is functionally equivalent to + "NOT RECENT" (as opposed to "NOT NEW"). + ON - Messages whose internal date (disregarding time and timezone) is within the specified date. + OR - Messages that match either search key. + RECENT - Messages that have the \Recent flag set. + SEEN - Messages that have the \Seen flag set. + SENTBEFORE - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is earlier than the specified date. + SENTON - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within the specified date. + SENTSINCE - Messages whose [RFC-2822] Date: header (disregarding time and timezone) is within or later than the specified date. + SINCE - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. + SMALLER - Messages with an [RFC-2822] size smaller than the specified number of octets. + SUBJECT - Messages that contain the specified string in the envelope structure's SUBJECT field. + TEXT - Messages that contain the specified string in the header or body of the message. + TO - Messages that contain the specified string in the envelope structure's TO field. + UID - Messages with unique identifiers corresponding to the specified unique identifier set. + Sequence set ranges are permitted. + UNANSWERED - Messages that do not have the \Answered flag set. + UNDELETED - Messages that do not have the \Deleted flag set. + UNDRAFT - Messages that do not have the \Draft flag set. + UNFLAGGED - Messages that do not have the \Flagged flag set. + UNKEYWORD - Messages that do not have the specified keyword flag set. + UNSEEN - Messages that do not have the \Seen flag set. + + */ + void setSearchCriteria(const String &criteria); + + /* + Set to search the unseen message. + + @param unseenSearch - Boolean flag to enable unseen message search. + + This function will be overridden (omitted) by setFetchUID as setSearchCriteria. + + */ + void setSearchUnseenMessage(bool unseenSearch); + + /* + Set the download folder. + + @param path - Path in SD card. + + All text/html message and attachemnts will be saved to message UID folder which created in defined path + e.g. "/{DEFINED_PATH}/{MESSAGE_UID}/{ATTACHMENT_FILE...}". + + */ + void setSaveFilePath(const String &path); + + /* + + Specify message UID to fetch or read. + + @param fetchUID - The message UID. + + Specify the message UID to fetch (read) only specific message instead of search. + + */ + void setFetchUID(const String &fetchUID); + + /* + + Set storage type to save download attached file or messages. + + @param storageType - The storage type to save file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + + */ + void setFileStorageType(uint8_t storageType); + + /* + + Enable/disable attachment download. + + @param download - Boolean flag to enable/disable attachment download. + + */ + void setDownloadAttachment(bool download); + + /* + + Enable/disable html message result. + + @param htmlFormat - Boolean flag to enable/disable html message result. + + The default value is false. + + */ + void setHTMLMessage(bool htmlFormat); + + /* + + Enable/disable plain text message result. + + @param textFormat - Boolean flag to enable/disable plain text message result. + + The default value is true. + + */ + void setTextMessage(bool textFormat); + + /* + + Set the maximum message to search. + + @param limit - Any number from 0 to 65535. + + The default value is 20. + + */ + void setSearchLimit(uint16_t limit); + + /* + + Enable/disable recent sort result. + + @param recentSort - Boolean flag to enable/disable recent message sort result. + + The default value is true. + + */ + void setRecentSort(bool recentSort); + + /* + + Assign callback function that return status of message fetching or reading. + + @param readCallback - The function that accept readStatusCallback as parameter. + + */ + void setReadCallback(readStatusCallback readCallback); + + /* + + Enable/disable attachement download progress while fetching or receiving message. + + @param report - Boolean flag to enable/disable attachement download progress while fetching or receiving message. + + To get the download status, Callback function should be set through setReadCallback. + + */ + void setDownloadReport(bool report); + + /* + + Determine only message header is return when search. + + */ + bool isHeaderOnly(); + + /* + + Get the sender name/Email for selected message from search result. + + @param messageIndex - The index of message. + + @return Sender name/Email String. + + */ + String getFrom(uint16_t messageIndex); + + /* + + Get the sender name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return Sender name/Email charactor encoding which use for decoding. + + */ + String getFromCharset(uint16_t messageIndex); + + /* + + Get the recipient name/Email for selected message index from search result. + + @param messageIndex - The index of message. + + @return Recipient name/Email String. + + */ + String getTo(uint16_t messageIndex); + + /* + + Get the recipient name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return Recipient name/Email charactor encoding which use in decoding to local language. + + */ + String getToCharset(uint16_t messageIndex); + + /* + + Get the CC name/Email for selected message index of IMAPData result. + + @param messageIndex - The index of message. + + @return CC name/Email String. + + */ + String getCC(uint16_t messageIndex); + + /* + + Get the CC name/Email charactor encoding. + + @param messageIndex - The index of message. + + @return CC name/Email charactor encoding which use in decoding to local language. + + */ + String getCCCharset(uint16_t messageIndex); + + /* + + Get the message subject for selected message index from search result. + + @param messageIndex - The index of message. + + @return Message subject name/Email String. + + */ + String getSubject(uint16_t messageIndex); + + /* + + Get the message subject charactor encoding. + + @param messageIndex - The index of message. + + @return Message subject charactor encoding which use in decoding to local language. + + */ + String getSubjectCharset(uint16_t messageIndex); + + /* + + Get the html message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The html message String or empty String upon the setHTMLMessage was set. + + */ + String getHTMLMessage(uint16_t messageIndex); + + /* + + Get the plain text message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The plain text message String or empty String upon the setTextMessage was set. + + */ + String getTextMessage(uint16_t messageIndex); + + /* + + Get the html message charactor encoding. + + @param messageIndex - The index of message. + + @return Html message charactor encoding which use in decoding to local language. + + */ + String getHTMLMessgaeCharset(uint16_t messageIndex); + + /* + + Get the text message charactor encoding. + + @param messageIndex - The index of message. + + @return The text message charactor encoding which use in decoding to local language. + + */ + String getTextMessgaeCharset(uint16_t messageIndex); + + /* + + Get the date of received message for selected message index from search result. + + @param messageIndex - The index of message. + + @return The date String. + + */ + String getDate(uint16_t messageIndex); + + /* + + Get the message UID for selected message index from search result. + + @param messageIndex - The index of message. + + @return UID String that can be use in setFetchUID. + + */ + String getUID(uint16_t messageIndex); + + /* + + Get the message number for selected message index from search result. + + @param messageIndex - The index of message. + + @return The message number which vary upon search criteria and sorting. + + */ + String getNumber(uint16_t messageIndex); + + /* + + Get the message ID for selected message index from search result. + + @param messageIndex - The index of message. + + @return The message ID String. + + */ + String getMessageID(uint16_t messageIndex); + + /* + + Get the accept language for selected message index from search result. + + @param messageIndex - The index of message. + + @return The accept language String. + + */ + String getAcceptLanguage(uint16_t messageIndex); + + /* + + Get the content language of text or html for selected message index from search result. + + @param messageIndex - The index of message. + + @return The content language String. + + */ + String getContentLanguage(uint16_t messageIndex); + + /* + + Determine fetch error status for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch error status. + + */ + bool isFetchMessageFailed(uint16_t messageIndex); + + /* + Get fetch error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch error reason String for selected message index. + + */ + String getFetchMessageFailedReason(uint16_t messageIndex); + + /* + Determine the attachment download error for selected message index from search result. + + @param messageIndex - The index of message. + + @return Fetch status. + + */ + bool isDownloadAttachmentFailed(uint16_t messageIndex, size_t attachmentIndex); + + /* + + Get the attachment download error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Download error reason String for selected message index. + + */ + String getDownloadAttachmentFailedReason(uint16_t messageIndex, size_t attachmentIndex); + + /* + + Determine the downloaded/saved text message error status for selected message index from search result. + + @param messageIndex - The index of message. + + @return Text message download status. + + */ + bool isDownloadMessageFailed(uint16_t messageIndex); + + /* + + Get the attachment or message downloadeds error reason for selected message index from search result. + + @param messageIndex - The index of message. + + @return Downloaded error reason String for selected message index. + + */ + String getDownloadMessageFailedReason(uint16_t messageIndex); + + /* + + Assign the download and decode flags for html message download. + + @param download - Boolean flag to enable/disable message download. + + @param decoded - Boolean flag to enable/disable html message decoding (support utf8 and base64 encoding). + + */ + void saveHTMLMessage(bool download, bool decoded); + + /* + + Assign the download and decode flags for plain text message download. + + @param download - Boolean flag to enable/disable message download. + + @param decoded - Boolean flag to enable/disable plain text message decoding (support utf8 and base64 encoding). + + */ + void saveTextMessage(bool download, bool decoded); + + /* + + Determine the mailbox folder count. + + @return Folder count number. + + */ + uint16_t getFolderCount(); + + /* + Get the mailbox folder name at selected index. + + @param folderIndex - Index of folder. + + @return Folder name String. + + Use folder name from this function for fetch or search. + + */ + String getFolder(uint16_t folderIndex); + + /* + + Determin the number of supported flags count. + + @return Flag count number. + + */ + uint16_t getFlagCount(); + + /* + Get the flag name for selected index. + + @param folderIndex - Index of folder. + + @return Flag name String. + + Use flags from this function for fetch or search. + + */ + String getFlag(uint16_t flagIndex); + + /* + + Get the number of message in selected mailbox folder. + + @return Total message number. + + */ + size_t totalMessages(); + + /* + + Get the number of message from search result. + + @return Search result number. + + */ + size_t searchCount(); + + /* + + Get the number of message available from search result which less than search limit. + + @return Available message number. + + */ + size_t availableMessages(); + + /* + + Get the number of attachments for selected message index from search result. + + @param messageIndex - Index of message. + + @return Number of attachments + + */ + size_t getAttachmentCount(uint16_t messageIndex); + + /* + + Get file name of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment file name String at the selected index. + + */ + String getAttachmentFileName(size_t messageIndex, size_t attachmentIndex); + + /* + + Get the name of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment name String at the selected index. + + */ + String getAttachmentName(size_t messageIndex, size_t attachmentIndex); + + /* + + Get attachment file size for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment file size in byte at the selected index. + + */ + int getAttachmentFileSize(size_t messageIndex, size_t attachmentIndex); + + /* + + Get creation date of attachment for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return The attachment creation date String at the selected index. + + */ + String getAttachmentCreationDate(size_t messageIndex, size_t attachmentIndex); + + /* + Get attachment file type for selected attachment index and message index from search result. + + @param messageIndex - Index of message. + @param attachmentIndex - Index of attachment. + + @return File MIME String at the selected index e.g. image/jpeg. + + */ + String getAttachmentType(size_t messageIndex, size_t attachmentIndex); + + /* + + Clear all IMAPData object data. + + */ + void empty(); + + /* + + Clear IMAPData object message data. + + */ + void clearMessageData(); + + friend ESP32_MailClient; + +private: + String getMessage(uint16_t messageIndex, bool htmlFormat); + + + size_t _totalMessage = 0; + std::string _host = ""; + uint16_t _port = 993; + uint8_t _storageType = 1; + bool _unseen = false; + std::string _loginEmail = ""; + std::string _loginPassword = ""; + std::string _currentFolder = "INBOX"; + std::string _nextUID = ""; + std::string _searchCriteria = "ALL*"; + std::string _fetchUID = ""; + std::string _savePath = ""; + + bool _downloadAttachment = false; + bool _recentSort = true; + bool _htmlFormat = false; + bool _textFormat = false; + bool _headerOnly = true; + bool _uidSearch = false; + bool _saveHTMLMsg = false; + bool _saveTextMsg = false; + bool _saveDecodedHTML = false; + bool _saveDecodedText = false; + bool _downloadReport = false; + bool _headerSaved = false; + + size_t _message_buffer_size = 200; + size_t _attacement_max_size = 1024 * 1024; + uint16_t _emailNumMax = 20; + int _searchCount; + bool _starttls = false; + bool _debug = false; + readStatusCallback _readCallback = NULL; + + std::vector _date = std::vector(); + std::vector _subject = std::vector(); + std::vector _subject_charset = std::vector(); + std::vector _from = std::vector(); + std::vector _from_charset = std::vector(); + std::vector _to = std::vector(); + std::vector _to_charset = std::vector(); + std::vector _cc = std::vector(); + std::vector _cc_charset = std::vector(); + std::vector _msgNum = std::vector(); + std::vector _msgID = std::vector(); + std::vector _contentLanguage = std::vector(); + std::vector _acceptLanguage = std::vector(); + + std::vector _folders = std::vector(); + std::vector _flag = std::vector(); + std::vector _attachmentCount = std::vector(); + std::vector> _messageDataInfo = std::vector>(); + std::vector _totalAttachFileSize = std::vector(); + std::vector _downloadedByte = std::vector(); + std::vector _messageDataCount = std::vector(); + std::vector _errorMsg = std::vector(); + std::vector _error = std::vector(); + std::vector _rootCA = std::vector(); + + + std::unique_ptr _net = std::unique_ptr(new ESP32MailHTTPClient()); + + ReadStatus _cbData; +}; + +class SMTPData +{ +public: + SMTPData(); + ~SMTPData(); + + /* + + Set SMTP server login credentials + + @param host - SMTP server e.g. smtp.gmail.com + @param port - SMTP port. + @param loginEmail - The account Email. + @param loginPassword - The account password. + @rootCA - Root CA certificate base64 string + + */ + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword, const char *rootCA); + void setLogin(const String &host, uint16_t port, const String &loginEmail, const String &loginPassword); + + /* + + Set STARTTLS mode to enable STARTTLS protocol + + @param starttls - bool flag that enables STARTTLS mode + + */ + void setSTARTTLS(bool starttls); + + /* + + Set debug print to serial + + @param debug - bool flag to enable debug + + */ + void setDebug(bool debug); + /* + + Set Sender info + + @param fromName - Sender's name + @param senderEmail - Sender's Email. + + */ + void setSender(const String &fromName, const String &senderEmail); + + /* + + Get Sender's name + + @return Sender's name String. + + */ + String getFromName(); + + /* + + Get Sender's Email + + @return Sender's Email String. + + */ + String getSenderEmail(); + + /* + + Set Email priority or importance + + @param priority - Number from 1 to 5, 1 for highest, 3 for normal and 5 for lowest priority + + */ + void setPriority(int priority); + + /* + + Set Email priority or importance + + @param priority - String (High, Normal or Low) + + */ + void setPriority(const String &priority); + + /* + + Get Email priority + + @return number represents Email priority (1 for highest, 3 for normal, 5 for low priority). + + */ + uint8_t getPriority(); + + /* + + Add one or more recipient + + @param email - Recipient Email String of one recipient. + + To add multiple recipients, call addRecipient for each recipient. + + */ + void addRecipient(const String &email); + + /* + + Remove recipient + + @param email - Recipient Email String. + + */ + void removeRecipient(const String &email); + + /* + + Remove recipient + + @param index - Index of recipients in Email object that previously added. + + */ + void removeRecipient(uint8_t index); + + /* + Clear all recipients. + */ + void clearRecipient(); + + /* + + Get one recipient + + @param index - Index of recipients. + + @return Recipient Email String at the index. + + */ + String getRecipient(uint8_t index); + + /* + + Get number of recipients + + @return Number of recipients. + + */ + uint8_t recipientCount(); + + /* + + Set the Email subject + + @param subject - The subject. + + */ + void setSubject(const String &subject); + + /* + + Get the Email subject + + @return Subject String. + + */ + String getSubject(); + + /* + + Set the Email message + + @param message - The message can be in normal text or html format. + @param htmlFormat - The html format flag, True for send the message as html format + + */ + void setMessage(const String &message, bool htmlFormat); + + void clrMessage(bool htmlFormat); + + void addMessage(const String &message); + + + /* + + Get the message + + @return Message String. + + */ + String getMessage(); + + /* + + Determine message is being send in html format + + @return Boolean status. + + */ + bool htmlFormat(); + + /* + + Add Carbon Copy (CC) Email + + @param email - The CC Email String. + + */ + void addCC(const String &email); + + /* + + Remove specified Carbon Copy (CC) Email + + @param email - The CC Email String to remove. + + */ + void removeCC(const String &email); + + /* + + Remove specified Carbon Copy (CC) Email + + @param index - The CC Email index to remove. + + */ + void removeCC(uint8_t index); + + /* + + Clear all Carbon Copy (CC) Emails + + */ + void clearCC(); + + /* + + Get Carbon Copy (CC) Email at specified index + + @param index - The CC Email index to get. + @return The CC Email string at the index. + + */ + String getCC(uint8_t index); + + /* + + Get the number of Carbon Copy (CC) Email + + @return Number of CC Emails. + + */ + uint8_t ccCount(); + + /* + Add Blind Carbon Copy (BCC) Email + + @param email - The BCC Email String. + + */ + void addBCC(const String &email); + + /* + + Remove specified Blind Carbon Copy (BCC) Email + + @param email - The BCC Email String to remove. + + */ + void removeBCC(const String &email); + + /* + + Remove specified Blind Carbon Copy (BCC) Email + + @param index - The BCC Email index to remove. + + */ + void removeBCC(uint8_t index); + + /* + + Clear all Blind Carbon Copy (BCC) Emails + + */ + void clearBCC(); + + /* + + Get Blind Carbon Copy (BCC) Email at specified index + + @param index - The BCC Email index to get. + + @return The BCC Email string at the index. + + */ + String getBCC(uint8_t index); + + /* + + Get the number of Blind Carbon Copy (BCC) Email + + @return Number of BCC Emails. + + */ + uint8_t bccCount(); + + /* + + Add attchement data (binary) from internal memory (flash or ram) + + @param fileName - The file name String that recipient can be saved. + @param mimeType - The MIME type of file (image/jpeg, image/png, text/plain...). Can be empty String. + @param data - The byte array of data (uint8_t) + @param size - The data length in byte. + + */ + void addAttachData(const String &fileName, const String &mimeType, uint8_t *data, size_t size); + + /* + + Remove specified attachment data + + @param fileName - The file name of the attachment data to remove. + + */ + void removeAttachData(const String &fileName); + + /* + + Remove specified attachment data + + @param index - The index of the attachment data (count only data type attachment) to remove. + + */ + void removeAttachData(uint8_t index); + + /* + + Get the number of attachment data + + @return Number of attach data. + + */ + uint8_t attachDataCount(); + + /* + + Add attchement file from SD card + + @param fileName - The file name String that recipient can be saved. + @param mimeType - The MIME type of file (image/jpeg, image/png, text/plain...). Can be omitted. + + */ + void addAttachFile(const String &filePath, const String &mimeType = ""); + + /* + + Remove specified attachment file from Email object + + @param fileName - The file name of the attachment file to remove. + + */ + void removeAttachFile(const String &filePath); + + /* + + Remove specified attachment file + + @param index - The index of the attachment file (count only file type attachment) to remove. + + */ + void removeAttachFile(uint8_t index); + + /* + + Set storage type for all attach files. + + @param storageType - The storage type to read attach file, MailClientStorageType::SD or MailClientStorageType::SPIFFS + + */ + void setFileStorageType(uint8_t storageType); + + /* + + Clear all attachment data + + */ + void clearAttachData(); + + /* + + Clear all attachment file. + + */ + void clearAttachFile(); + + /* + + Clear all attachments (both data and file type attachments). + + */ + void clearAttachment(); + + /* + + Get number of attachments (both data and file type attachments). + + @return Number of all attachemnts. + + */ + uint8_t attachFileCount(); + + /* + + Add one or more custom message header field. + + @param field - custom header String inform of FIELD: VALUE + + This header field will add to message header. + + + */ + void addCustomMessageHeader(const String &field); + + /* + + Remove one custom message header field that previously added. + + @param field - custom custom message header field String to remove. + + + */ + void removeCustomMessageHeader(const String &field); + + /* + + Remove one custom message header field that previously added by its index. + + @param index - custom message header field index (number) to remove. + + + */ + void removeCustomMessageHeader(uint8_t index); + + /* + + Clear all ccustom message header field that previously added. + + */ + void clearCustomMessageHeader(); + + /* + + Get the number of custom message header field that previously added. + + @return Number of custom message header field. + + */ + uint8_t CustomMessageHeaderCount(); + + /* + + Get custom message header field that previously added by index + + @param index - The custom message header field index to get. + + @return The custom message header field string at the index. + + */ + String getCustomMessageHeader(uint8_t index); + + + + /* + + Clear all data from Email object to free memory. + + */ + void empty(); + + /* + + Set the Email sending status callback function to Email object. + + @param sendCallback - The callback function that accept the sendStatusCallback param. + + */ + void setSendCallback(sendStatusCallback sendCallback); + + friend ESP32_MailClient; + friend attachmentData; + +protected: + int _priority = -1; + string _loginEmail = ""; + string _loginPassword = ""; + string _host = ""; + uint16_t _port = 0; + uint8_t _storageType = 1; + + string _fromName = ""; + string _senderEmail = ""; + string _subject = ""; + string _message = ""; + bool _htmlFormat = false; + bool _starttls = false; + bool _debug = false; + sendStatusCallback _sendCallback = NULL; + + std::vector _recipient = std::vector(); + std::vector _customMessageHeader = std::vector(); + std::vector _cc = std::vector(); + std::vector _bcc = std::vector(); + attachmentData _attach; + SendStatus _cbData; + std::vector _rootCA = std::vector(); + std::unique_ptr _net = std::unique_ptr(new ESP32MailHTTPClient()); + +}; + + +static void __attribute__((used)) ESP32MailDebug(const char *msg) +{ + + Serial.print(FPSTR("[DEBUG] - ")); + Serial.println(msg); + +} + +static void __attribute__((used)) ESP32MailDebugLine(const char *msg, bool newline) +{ + if (!newline) + Serial.print(FPSTR("[DEBUG] - ")); + + if (newline) + Serial.println(msg); + else + Serial.print(msg); +} + + +extern ESP32_MailClient MailClient; + +#endif //ESP32 + +#endif //ESP32_MailClient_H diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.cpp b/libesp32/ESP32-Mail-Client/src/RFC2047.cpp new file mode 100755 index 000000000..993c132e3 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/RFC2047.cpp @@ -0,0 +1,240 @@ +#ifndef RFC2047_CPP +#define RFC2047_CPP + +#ifdef ESP32 + +#include "RFC2047.h" + +RFC2047::RFC2047(){} + + +void RFC2047::rfc2047Decode(char *d, const char *s, size_t dlen){ + + const char *p, *q; + size_t n; + int found_encoded = 0; + + dlen--; /* save room for the terminal nul */ + + while (*s && dlen > 0) + { + if ((p = strstr (s, "=?")) == NULL || + (q = strchr (p + 2, '?')) == NULL || + (q = strchr (q + 1, '?')) == NULL || + (q = strstr (q + 1, "?=")) == NULL) + { + /* no encoded words */ + if (d != s) + strfcpy (d, s, dlen + 1); + return; + } + + if (p != s) + { + n = (size_t) (p - s); + /* ignore spaces between encoded words */ + if (!found_encoded || strspn (s, " \t\r\n") != n) + { + if (n > dlen) + n = dlen; + if (d != s) + memcpy (d, s, n); + d += n; + dlen -= n; + } + } + + rfc2047DecodeWord (d, p, dlen); + found_encoded = 1; + s = q + 2; + n = strlen (d); + dlen -= n; + d += n; + } + *d = 0; + +} + +void RFC2047::rfc2047DecodeWord(char *d, const char *s, size_t dlen){ + + char *p = safe_strdup (s); + char *pp = p; + char *pd = d; + size_t len = dlen; + int enc = 0, filter = 0, count = 0, c1, c2, c3, c4; + + while ((pp = strtok (pp, "?")) != NULL) + { + count++; + switch (count) + { + case 2: + if (strcasecmp (pp, Charset) != 0) + { + filter = 1; + } + break; + case 3: + if (toupper (*pp) == 'Q') + enc = ENCQUOTEDPRINTABLE; + else if (toupper (*pp) == 'B') + enc = ENCBASE64; + else + return; + break; + case 4: + if (enc == ENCQUOTEDPRINTABLE) + { + while (*pp && len > 0) + { + if (*pp == '_') + { + *pd++ = ' '; + len--; + } + else if (*pp == '=') + { + *pd++ = (hexval(pp[1]) << 4) | hexval(pp[2]); + len--; + pp += 2; + } + else + { + *pd++ = *pp; + len--; + } + pp++; + } + *pd = 0; + } + else if (enc == ENCBASE64) + { + while (*pp && len > 0) + { + c1 = base64val(pp[0]); + c2 = base64val(pp[1]); + *pd++ = (c1 << 2) | ((c2 >> 4) & 0x3); + if (--len == 0) break; + + if (pp[2] == '=') break; + + c3 = base64val(pp[2]); + *pd++ = ((c2 & 0xf) << 4) | ((c3 >> 2) & 0xf); + if (--len == 0) + break; + + if (pp[3] == '=') + break; + + c4 = base64val(pp[3]); + *pd++ = ((c3 & 0x3) << 6) | c4; + if (--len == 0) + break; + + pp += 4; + } + *pd = 0; + } + break; + } + pp = 0; + } + safe_free (&p); + if (filter) + { + + pd = d; + while (*pd) + { + if (!IsPrint (*pd)) + *pd = '?'; + pd++; + } + } + return; +} + + +void *RFC2047::safe_calloc (size_t nmemb, size_t size) +{ + void *p; + + if (!nmemb || !size) + return NULL; + if (!(p = calloc (nmemb, size))) + { + //out of memory + return NULL; + } + return p; +} + +void *RFC2047::safe_malloc (unsigned int siz) +{ + void *p; + + if (siz == 0) + return 0; + if ((p = (void *) malloc (siz)) == 0) + { + //out of memory + return NULL; + } + return (p); +} + +void RFC2047::safe_realloc (void **p, size_t siz) +{ + void *r; + + if (siz == 0) + { + if (*p) + { + free (*p); + *p = NULL; + } + return; + } + + if (*p) + r = (void *) realloc (*p, siz); + else + { + r = (void *) malloc (siz); + } + + if (!r) + { + //out of memory + return; + } + + *p = r; +} + +void RFC2047::safe_free (void *ptr) +{ + void **p = (void **)ptr; + if (*p) + { + free (*p); + *p = 0; + } +} + +char *RFC2047::safe_strdup (const char *s) +{ + char *p; + size_t l; + + if (!s || !*s) return 0; + l = strlen (s) + 1; + p = (char *)safe_malloc (l); + memcpy (p, s, l); + return (p); +} + +#endif //ESP32 + +#endif //RFC2047_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/RFC2047.h b/libesp32/ESP32-Mail-Client/src/RFC2047.h new file mode 100755 index 000000000..aeee0e12c --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/RFC2047.h @@ -0,0 +1,70 @@ + +#ifndef RFC2047_H +#define RFC2047_H + +#ifdef ESP32 + +#include + + +#define strfcpy(A,B,C) strncpy(A,B,C), *(A+(C)-1)=0 + +enum +{ + ENCOTHER, + ENC7BIT, + ENC8BIT, + ENCQUOTEDPRINTABLE, + ENCBASE64, + ENCBINARY +}; + +__attribute__((used)) static const char *Charset = "utf-8"; + +__attribute__((used)) static int Index_hex[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +__attribute__((used)) static int Index_64[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + +#define IsPrint(c) (isprint((unsigned char)(c)) || \ + ((unsigned char)(c) >= 0xa0)) + +#define hexval(c) Index_hex[(unsigned int)(c)] +#define base64val(c) Index_64[(unsigned int)(c)] + +class RFC2047{ + + public: + RFC2047(); + void rfc2047Decode(char *d, const char *s, size_t dlen); + + + private: + void rfc2047DecodeWord(char *d, const char *s, size_t dlen); + void *safe_calloc (size_t nmemb, size_t size); + void *safe_malloc (unsigned int siz); + void safe_realloc (void **p, size_t siz); + void safe_free (void *ptr); + char *safe_strdup (const char *s); + + +}; + +#endif //ESP32 + +#endif //RFC2047_H \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp new file mode 100755 index 000000000..26460a604 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.cpp @@ -0,0 +1,397 @@ +/* + *Customized WiFiClientSecure.cpp to support STARTTLS protocol, version 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 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. +*/ + +/* + WiFiClientSecureESP32.cpp - Client Secure class for ESP32 + Copyright (c) 2016 Hristo Gochkov All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WiFiClientSecureESP32_CPP +#define WiFiClientSecureESP32_CPP + +#ifdef ESP32 + +#include "WiFiClientSecureESP32.h" +#include +#include +#include + +#undef connect +#undef write +#undef read + + +WiFiClientSecureESP32::WiFiClientSecureESP32() +{ + _connected = false; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + + +WiFiClientSecureESP32::WiFiClientSecureESP32(int sock) +{ + _connected = false; + _timeout = 0; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = sock; + sslclient->handshake_timeout = 120000; + + if (sock >= 0) { + _connected = true; + } + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +WiFiClientSecureESP32::WiFiClientSecureESP32(bool starttls) +{ + _connected = false; + + sslclient = new sslclient_context32; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + sslclient->starttls = true; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +WiFiClientSecureESP32::~WiFiClientSecureESP32() +{ + stop(); + delete sslclient; +} + +WiFiClientSecureESP32 &WiFiClientSecureESP32::operator=(const WiFiClientSecureESP32 &other) +{ + stop(); + sslclient->socket = other.sslclient->socket; + _connected = other._connected; + return *this; +} + +void WiFiClientSecureESP32::stop() +{ + if (sslclient->socket >= 0) { + close(sslclient->socket); + sslclient->socket = -1; + _connected = false; + _peek = -1; + } + stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(ip, port, _pskIdent, _psKey); + return connect(ip, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(ip, port); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(host, port, _pskIdent, _psKey); + return connect(host, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(host, port); +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key) +{ + return connect(ip.toString().c_str(), port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, const char *_CA_cert, const char *_cert, const char *_private_key) +{ + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, _CA_cert, _cert, _private_key, NULL, NULL); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int WiFiClientSecureESP32::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { + return connect(ip.toString().c_str(), port,_pskIdent, _psKey); +} + +int WiFiClientSecureESP32::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { + log_v("start_ssl_client with PSK"); + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, _pskIdent, _psKey); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int WiFiClientSecureESP32::peek(){ + if(_peek >= 0){ + return _peek; + } + _peek = timedRead(); + return _peek; +} + +size_t WiFiClientSecureESP32::write(uint8_t data) +{ + return write(&data, 1); +} + +int WiFiClientSecureESP32::read() +{ + uint8_t data = -1; + int res = read(&data, 1); + if (res < 0) { + return res; + } + return data; +} + +size_t WiFiClientSecureESP32::write(const uint8_t *buf, size_t size) +{ + if (!_connected) { + return 0; + } + int res = send_ssl_data(sslclient, buf, size); + if (res < 0) { + stop(); + res = 0; + } + return res; +} + +int WiFiClientSecureESP32::read(uint8_t *buf, size_t size) +{ + int peeked = 0; + int avail = available(); + if ((!buf && size) || avail <= 0) { + return -1; + } + if(!size){ + return 0; + } + if(_peek >= 0){ + buf[0] = _peek; + _peek = -1; + size--; + avail--; + if(!size || !avail){ + return 1; + } + buf++; + peeked = 1; + } + + int res = get_ssl_receive(sslclient, buf, size); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res + peeked; +} + +int WiFiClientSecureESP32::available() +{ + int peeked = (_peek >= 0); + if (!_connected) { + return peeked; + } + int res = data_to_read(sslclient); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res+peeked; +} + +uint8_t WiFiClientSecureESP32::connected() +{ + uint8_t dummy = 0; + read(&dummy, 0); + + return _connected; +} + +void WiFiClientSecureESP32::setCACert (const char *rootCA) +{ + _CA_cert = rootCA; +} + +void WiFiClientSecureESP32::setCertificate (const char *client_ca) +{ + _cert = client_ca; +} + +void WiFiClientSecureESP32::setPrivateKey (const char *private_key) +{ + _private_key = private_key; +} + +void WiFiClientSecureESP32::setPreSharedKey(const char *pskIdent, const char *psKey) { + _pskIdent = pskIdent; + _psKey = psKey; +} + +bool WiFiClientSecureESP32::verify(const char* fp, const char* domain_name) +{ + if (!sslclient) + return false; + + return verify_ssl_fingerprint(sslclient, fp, domain_name); +} + +char *WiFiClientSecureESP32::_streamLoad(Stream& stream, size_t size) { + static char *dest = nullptr; + if(dest) { + free(dest); + } + dest = (char*)malloc(size); + if (!dest) { + return nullptr; + } + if (size != stream.readBytes(dest, size)) { + free(dest); + dest = nullptr; + } + return dest; +} + +bool WiFiClientSecureESP32::loadCACert(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCACert(dest); + ret = true; + } + return ret; +} + +bool WiFiClientSecureESP32::loadCertificate(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCertificate(dest); + ret = true; + } + return ret; +} + +bool WiFiClientSecureESP32::loadPrivateKey(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setPrivateKey(dest); + ret = true; + } + return ret; +} + +int WiFiClientSecureESP32::lastError(char *buf, const size_t size) +{ + if (!_lastError) { + return 0; + } + char error_buf[100]; + mbedtls_strerror(_lastError, error_buf, 100); + snprintf(buf, size, "%s", error_buf); + return _lastError; +} + +void WiFiClientSecureESP32::setHandshakeTimeout(unsigned long handshake_timeout) +{ + sslclient->handshake_timeout = handshake_timeout * 1000; +} + +void WiFiClientSecureESP32::setSTARTTLS(bool starttls) +{ + sslclient->starttls = starttls; +} + +void WiFiClientSecureESP32::setDebugCB(DebugMsgCallback cb) +{ + sslclient->_debugCallback = std::move(cb); +} + +#endif //ESP32 + +#endif //WiFiClientSecureESP32_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h new file mode 100755 index 000000000..2a940c391 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/WiFiClientSecureESP32.h @@ -0,0 +1,145 @@ + +/* + *Customized WiFiClientSecure.h to support STARTTLS protocol, version 1.0.1 + * + * The MIT License (MIT) + * Copyright (c) 2019 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. +*/ + +/* + WiFiClientSecureESP32.h - Base class that provides Client SSL to ESP32 + Copyright (c) 2011 Adrian McEwen. All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef WiFiClientSecureESP32_H +#define WiFiClientSecureESP32_H + +#ifdef ESP32 + +#include "Arduino.h" +#include "IPAddress.h" +#include +#include "ssl_client32.h" + +typedef void (*DebugMsgCallback)(const char* msg); + +class WiFiClientSecureESP32 : public WiFiClient +{ +protected: + sslclient_context32 *sslclient; + + int _lastError = 0; + int _peek = -1; + int _timeout = 0; + const char *_CA_cert; + const char *_cert; + const char *_private_key; + const char *_pskIdent; // identity for PSK cipher suites + const char *_psKey; // key in hex for PSK cipher suites + DebugMsgCallback _debugCallback = NULL; + +public: + WiFiClientSecureESP32 *next; + WiFiClientSecureESP32(); + WiFiClientSecureESP32(int socket); + WiFiClientSecureESP32(bool starttls); + ~WiFiClientSecureESP32(); + int connect(IPAddress ip, uint16_t port); + int connect(IPAddress ip, uint16_t port, int32_t timeout); + int connect(const char *host, uint16_t port); + int connect(const char *host, uint16_t port, int32_t timeout); + int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); + int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); + int peek(); + size_t write(uint8_t data); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + void flush() {} + void stop(); + uint8_t connected(); + int lastError(char *buf, const size_t size); + void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + void setCACert(const char *rootCA); + void setCertificate(const char *client_ca); + void setPrivateKey (const char *private_key); + bool loadCACert(Stream& stream, size_t size); + bool loadCertificate(Stream& stream, size_t size); + bool loadPrivateKey(Stream& stream, size_t size); + bool verify(const char* fingerprint, const char* domain_name); + void setHandshakeTimeout(unsigned long handshake_timeout); + void setSTARTTLS(bool starttls); + void setDebugCB(DebugMsgCallback cb); + + operator bool() + { + return connected(); + } + WiFiClientSecureESP32 &operator=(const WiFiClientSecureESP32 &other); + bool operator==(const bool value) + { + return bool() == value; + } + bool operator!=(const bool value) + { + return bool() != value; + } + bool operator==(const WiFiClientSecureESP32 &); + bool operator!=(const WiFiClientSecureESP32 &rhs) + { + return !this->operator==(rhs); + }; + + int socket() + { + return sslclient->socket = -1; + } + +private: + char *_streamLoad(Stream& stream, size_t size); + + //friend class WiFiServer; + using Print::write; +}; + +#endif //ESP32 + +#endif //WiFiClientSecureESP32_H + + diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp b/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp new file mode 100755 index 000000000..06e3f11b1 --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ssl_client32.cpp @@ -0,0 +1,853 @@ +/* + *Customized ssl_client.cpp to support STARTTLS protocol, version 1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2019 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. + */ + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE +* +* Adapted from the ssl_client1 example of mbedtls. +* +* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. +* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. +*/ + +#ifndef SSL_CLIENT32_CPP +#define SSL_CLIENT32_CPP + +#ifdef ESP32 + +#include "Arduino.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ssl_client32.h" +#include "WiFi.h" + +const char *pers32 = "esp32-tls"; + +static int handle_error(int err) +{ + if (err == -30848) + { + return err; + } +#ifdef MBEDTLS_ERROR_C + char error_buf[100]; + mbedtls_strerror(err, error_buf, 100); + log_e("%s", error_buf); +#endif + log_e("MbedTLS message code: %d", err); + return err; +} + +void ssl_init(sslclient_context32 *ssl_client) +{ + mbedtls_ssl_init(&ssl_client->ssl_ctx); + mbedtls_ssl_config_init(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); + mbedtls_net_init(&ssl_client->server_fd); +} + +int start_ssl_client(sslclient_context32 *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey) +{ + char buf[512]; + int ret, flags; + int enable = 1; + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_2, ssl_client); + + log_v("Free internal heap before TLS %u", ESP.getFreeHeap()); + + log_v("Starting socket"); + ssl_client->socket = -1; + + ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ssl_client->socket < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_3, ssl_client); + log_e("ERROR opening socket"); + return ssl_client->socket; + } + + IPAddress srv((uint32_t)0); + if (!WiFiGenericClass::hostByName(host, srv)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_4, ssl_client); + return -1; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = srv; + serv_addr.sin_port = htons(port); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_5, ssl_client); + + if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) + { + if (timeout <= 0) + { + timeout = 30000; + } + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_6, ssl_client); + } + else + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_7, ssl_client); + log_e("Connect to Server failed!"); + return -1; + } + + fcntl(ssl_client->socket, F_SETFL, fcntl(ssl_client->socket, F_GETFL, 0) | O_NONBLOCK); + + if (ssl_client->starttls && (port == 25 || port == 587 || port == 143)) + { + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_8, ssl_client); + + if ((ret = starttlsHandshake(ssl_client, port)) != 0) + { + log_e("STARTTLS failed!"); + return -1; + } + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_9, ssl_client); + + log_v("Seeding the random number generator"); + mbedtls_entropy_init(&ssl_client->entropy_ctx); + + ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, + &ssl_client->entropy_ctx, (const unsigned char *)pers32, strlen(pers32)); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + + return handle_error(ret); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_10, ssl_client); + + log_v("Setting up the SSL/TLS structure..."); + + if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and + // MBEDTLS_SSL_VERIFY_NONE if not. + + if (rootCABuff != NULL) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_11, ssl_client); + log_v("Loading CA cert"); + mbedtls_x509_crt_init(&ssl_client->ca_cert); + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); + mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); + //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + } + else if (pskIdent != NULL && psKey != NULL) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_12, ssl_client); + log_v("Setting up PSK"); + // convert PSK from hex to binary + if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2 * MBEDTLS_PSK_MAX_LEN) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_13, ssl_client); + log_e("pre-shared key not valid hex or too long"); + return -1; + } + unsigned char psk[MBEDTLS_PSK_MAX_LEN]; + size_t psk_len = strlen(psKey) / 2; + for (int j = 0; j < strlen(psKey); j += 2) + { + char c = psKey[j]; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + return -1; + psk[j / 2] = c << 4; + c = psKey[j + 1]; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else + return -1; + psk[j / 2] |= c; + } + // set mbedtls config + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_14, ssl_client); + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, + (const unsigned char *)pskIdent, strlen(pskIdent)); + if (ret != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + log_e("mbedtls_ssl_conf_psk returned %d", ret); + return handle_error(ret); + } + } + else + { + + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); + log_i("WARNING: Use certificates for a more secure communication!"); + } + + if (cli_cert != NULL && cli_key != NULL) + { + + mbedtls_x509_crt_init(&ssl_client->client_cert); + mbedtls_pk_init(&ssl_client->client_key); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_15, ssl_client); + + log_v("Loading CRT cert"); + + ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); + if (ret < 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_16, ssl_client); + + log_v("Loading private key"); + ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); + + if (ret != 0) + { + return handle_error(ret); + } + + mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_17, ssl_client); + + log_v("Setting hostname for TLS session..."); + + // Hostname set here should match CN in server certificate + if ((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + + mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); + + if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + + return handle_error(ret); + } + + mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL); + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_18, ssl_client); + + log_v("Performing the SSL/TLS handshake..."); + unsigned long handshake_start_time = millis(); + while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + if (ssl_client->_debugCallback) + { + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + } + return handle_error(ret); + } + if ((millis() - handshake_start_time) > ssl_client->handshake_timeout) + return -1; + vTaskDelay(10 / portTICK_PERIOD_MS); + } + + if (cli_cert != NULL && cli_key != NULL) + { + log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); + if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) + { + + log_d("Record expansion is %d", ret); + } + else + { + log_w("Record expansion is unknown (compression)"); + } + } + + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_19, ssl_client); + + log_v("Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) + { + bzero(buf, sizeof(buf)); + mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_20, ssl_client); + log_e("Failed to verify peer certificate! verification info: %s", buf); + stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. + return handle_error(ret); + } + else + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_21, ssl_client); + log_v("Certificate verified."); + } + + if (rootCABuff != NULL) + { + mbedtls_x509_crt_free(&ssl_client->ca_cert); + } + + if (cli_cert != NULL) + { + mbedtls_x509_crt_free(&ssl_client->client_cert); + } + + if (cli_key != NULL) + { + mbedtls_pk_free(&ssl_client->client_key); + } + + log_v("Free internal heap after TLS %u", ESP.getFreeHeap()); + + return ssl_client->socket; +} + +void stop_ssl_socket(sslclient_context32 *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key) +{ + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_22, ssl_client); + log_v("Cleaning SSL connection."); + + if (ssl_client->socket >= 0) + { + close(ssl_client->socket); + ssl_client->socket = -1; + } + + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + mbedtls_entropy_free(&ssl_client->entropy_ctx); +} + +int data_to_read(sslclient_context32 *ssl_client) +{ + int ret, res; + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); + //log_e("RET: %i",ret); //for low level debug + res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); + //log_e("RES: %i",res); //for low level debug + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) + { + if (ssl_client->_debugCallback) + { + char *buf = new char[512]; + char *error_buf = new char[100]; + memset(buf, 0, 512); + strcpy_P(buf, ESP32_SSL_CLIENT_STR_1); + mbedtls_strerror(ret, error_buf, 100); + strcat(buf, error_buf); + ssl_client->_debugCallback(buf); + delete[] error_buf; + delete[] buf; + } + return handle_error(ret); + } + + return res; +} + +int send_ssl_data(sslclient_context32 *ssl_client, const uint8_t *data, uint16_t len) +{ + + log_v("Writing HTTP request..."); //for low level debug + int ret = -1; + + while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + return handle_error(ret); + } + } + + len = ret; + //log_v("%d bytes written", len); //for low level debug + return ret; +} + +int get_ssl_receive(sslclient_context32 *ssl_client, uint8_t *data, int length) +{ + + //log_d( "Reading HTTP response..."); //for low level debug + int ret = -1; + + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); + + //log_v( "%d bytes read", ret); //for low level debug + return ret; +} + +static bool parseHexNibble(char pb, uint8_t *res) +{ + if (pb >= '0' && pb <= '9') + { + *res = (uint8_t)(pb - '0'); + return true; + } + else if (pb >= 'a' && pb <= 'f') + { + *res = (uint8_t)(pb - 'a' + 10); + return true; + } + else if (pb >= 'A' && pb <= 'F') + { + *res = (uint8_t)(pb - 'A' + 10); + return true; + } + return false; +} + +// Compare a name from certificate and domain name, return true if they match +static bool matchName(const std::string &name, const std::string &domainName) +{ + size_t wildcardPos = name.find('*'); + if (wildcardPos == std::string::npos) + { + // Not a wildcard, expect an exact match + return name == domainName; + } + + size_t firstDotPos = name.find('.'); + if (wildcardPos > firstDotPos) + { + // Wildcard is not part of leftmost component of domain name + // Do not attempt to match (rfc6125 6.4.3.1) + return false; + } + if (wildcardPos != 0 || firstDotPos != 1) + { + // Matching of wildcards such as baz*.example.com and b*z.example.com + // is optional. Maybe implement this in the future? + return false; + } + size_t domainNameFirstDotPos = domainName.find('.'); + if (domainNameFirstDotPos == std::string::npos) + { + return false; + } + return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos); +} + +// Verifies certificate provided by the peer to match specified SHA256 fingerprint +bool verify_ssl_fingerprint(sslclient_context32 *ssl_client, const char *fp, const char *domain_name) +{ + // Convert hex string to byte array + uint8_t fingerprint_local[32]; + int len = strlen(fp); + int pos = 0; + for (size_t i = 0; i < sizeof(fingerprint_local); ++i) + { + while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) + { + ++pos; + } + if (pos > len - 2) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_23, ssl_client); + log_d("pos:%d len:%d fingerprint too short", pos, len); + return false; + } + uint8_t high, low; + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos + 1], &low)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_24, ssl_client); + log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos + 1]); + return false; + } + pos += 2; + fingerprint_local[i] = low | (high << 4); + } + + // Get certificate provided by the peer + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + if (!crt) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_25, ssl_client); + log_d("could not fetch peer certificate"); + return false; + } + + // Calculate certificate's SHA256 fingerprint + uint8_t fingerprint_remote[32]; + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); + + // Check if fingerprints match + if (memcmp(fingerprint_local, fingerprint_remote, 32)) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_26, ssl_client); + log_d("fingerprint doesn't match"); + return false; + } + + // Additionally check if certificate has domain name if provided + if (domain_name) + return verify_ssl_dn(ssl_client, domain_name); + else + return true; +} + +// Checks if peer certificate has specified domain in CN or SANs +bool verify_ssl_dn(sslclient_context32 *ssl_client, const char *domain_name) +{ + log_d("domain name: '%s'", (domain_name) ? domain_name : "(null)"); + std::string domain_name_str(domain_name); + std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower); + + // Get certificate provided by the peer + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + // Check for domain name in SANs + const mbedtls_x509_sequence *san = &crt->subject_alt_names; + while (san != nullptr) + { + std::string san_str((const char *)san->buf.p, san->buf.len); + std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower); + + if (matchName(san_str, domain_name_str)) + return true; + + log_d("SAN '%s': no match", san_str.c_str()); + + // Fetch next SAN + san = san->next; + } + + // Check for domain name in CN + const mbedtls_asn1_named_data *common_name = &crt->subject; + while (common_name != nullptr) + { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + std::string common_name_str((const char *)common_name->val.p, common_name->val.len); + + if (matchName(common_name_str, domain_name_str)) + return true; + + log_d("CN '%s': not match", common_name_str.c_str()); + } + + // Fetch next DN object + common_name = common_name->next; + } + + return false; +} + +int starttlsHandshake(sslclient_context32 *ssl_client, int port) +{ + + int ret = 0; + size_t msgLen = 100; + size_t bufLen = 512; + char *buf = new char[bufLen]; + char *hMsg = new char[msgLen]; + + fd_set readset; + fd_set writeset; + fd_set errset; + + struct timeval tv; + + FD_ZERO(&readset); + FD_SET(ssl_client->socket, &readset); + FD_ZERO(&writeset); + FD_SET(ssl_client->socket, &writeset); + + FD_ZERO(&errset); + FD_SET(ssl_client->socket, &errset); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_27, ssl_client); + + goto starttls_exit; + } + + ret = read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_28, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + + if (port == 587 || port == 25) + { + + memset(hMsg, 0, msgLen); + strcpy_P(hMsg, ESP32_SSL_CLIENT_STR_29); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_30, ssl_client); + ret = lwip_write(ssl_client->socket, hMsg, strlen(hMsg)); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_31, ssl_client); + goto starttls_exit; + } + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_32, ssl_client); + goto starttls_exit; + } + + memset(buf, 0, bufLen); + ret = lwip_read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_33, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + } + + memset(hMsg, 0, msgLen); + strcpy_P(hMsg, ESP32_SSL_CLIENT_STR_34); + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_35, ssl_client); + ret = lwip_write(ssl_client->socket, hMsg, strlen(hMsg)); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_36, ssl_client); + goto starttls_exit; + } + + ret = lwip_select(ssl_client->socket, &readset, &writeset, &errset, &tv); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_37, ssl_client); + goto starttls_exit; + } + + memset(buf, 0, bufLen); + ret = lwip_read(ssl_client->socket, buf, bufLen); + + if (ret < 0) + { + if (ssl_client->_debugCallback) + ESP32SSLClientDebugInfo(ESP32_SSL_CLIENT_STR_38, ssl_client); + goto starttls_exit; + } + else + { + if (ssl_client->_debugCallback) + ssl_client->_debugCallback(buf); + } + + delete[] buf; + delete[] hMsg; + + return 0; + +starttls_exit: + + delete[] buf; + delete[] hMsg; + + return -1; +} + +void ESP32SSLClientDebugInfo(PGM_P info, sslclient_context32 *ssl_client) +{ + size_t dbgInfoLen = strlen_P(info) + 1; + char *dbgInfo = new char[dbgInfoLen]; + memset(dbgInfo, 0, dbgInfoLen); + strcpy_P(dbgInfo, info); + ssl_client->_debugCallback(dbgInfo); + delete[] dbgInfo; +} + +#endif //ESP32 + +#endif //SSL_CLIENT32_CPP \ No newline at end of file diff --git a/libesp32/ESP32-Mail-Client/src/ssl_client32.h b/libesp32/ESP32-Mail-Client/src/ssl_client32.h new file mode 100755 index 000000000..3e2d3b09a --- /dev/null +++ b/libesp32/ESP32-Mail-Client/src/ssl_client32.h @@ -0,0 +1,116 @@ +/* + *Customized ssl_client.h to support STARTTLS protocol, version 1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2019 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. +*/ + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE + * by Evandro Copercini - 2017 - Apache 2.0 License + */ + +#ifndef SSL_CLIENT32_H +#define SSL_CLIENT32_H + +#ifdef ESP32 + +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" + +static const char ESP32_SSL_CLIENT_STR_1[] PROGMEM = "ERROR: "; +static const char ESP32_SSL_CLIENT_STR_2[] PROGMEM = "INFO: starting socket"; +static const char ESP32_SSL_CLIENT_STR_3[] PROGMEM = "ERROR: opening socket"; +static const char ESP32_SSL_CLIENT_STR_4[] PROGMEM = "ERROR: could not get ip from host"; +static const char ESP32_SSL_CLIENT_STR_5[] PROGMEM = "INFO: connecting to Server..."; +static const char ESP32_SSL_CLIENT_STR_6[] PROGMEM = "INFO: server connected"; +static const char ESP32_SSL_CLIENT_STR_7[] PROGMEM = "ERROR: connect to Server failed!"; +static const char ESP32_SSL_CLIENT_STR_8[] PROGMEM = "INFO: begin STARTTLS handshake"; +static const char ESP32_SSL_CLIENT_STR_9[] PROGMEM = "INFO: seeding the random number generator"; +static const char ESP32_SSL_CLIENT_STR_10[] PROGMEM = "INFO: setting up the SSL/TLS structure..."; +static const char ESP32_SSL_CLIENT_STR_11[] PROGMEM = "INFO: loading CA cert"; +static const char ESP32_SSL_CLIENT_STR_12[] PROGMEM = "INFO: setting up PSK"; +static const char ESP32_SSL_CLIENT_STR_13[] PROGMEM = "ERROR: pre-shared key not valid hex or too long"; +static const char ESP32_SSL_CLIENT_STR_14[] PROGMEM = "INFO: set mbedtls config"; +static const char ESP32_SSL_CLIENT_STR_15[] PROGMEM = "INFO: loading CRT cert"; +static const char ESP32_SSL_CLIENT_STR_16[] PROGMEM = "INFO: loading private key"; +static const char ESP32_SSL_CLIENT_STR_17[] PROGMEM = "INFO: setting hostname for TLS session..."; +static const char ESP32_SSL_CLIENT_STR_18[] PROGMEM = "INFO: performing the SSL/TLS handshake..."; +static const char ESP32_SSL_CLIENT_STR_19[] PROGMEM = "INFO: verifying peer X.509 certificate..."; +static const char ESP32_SSL_CLIENT_STR_20[] PROGMEM = "ERROR: failed to verify peer certificate!"; +static const char ESP32_SSL_CLIENT_STR_21[] PROGMEM = "INFO: certificate verified"; +static const char ESP32_SSL_CLIENT_STR_22[] PROGMEM = "INFO: cleaning SSL connection"; +static const char ESP32_SSL_CLIENT_STR_23[] PROGMEM = "ERROR: fingerprint too short"; +static const char ESP32_SSL_CLIENT_STR_24[] PROGMEM = "ERROR: invalid hex sequence"; +static const char ESP32_SSL_CLIENT_STR_25[] PROGMEM = "ERROR: could not fetch peer certificate"; +static const char ESP32_SSL_CLIENT_STR_26[] PROGMEM = "ERROR: fingerprint doesn't match"; +static const char ESP32_SSL_CLIENT_STR_27[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_28[] PROGMEM = "ERROR: reading incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_29[] PROGMEM = "EHLO DUDE\r\n"; +static const char ESP32_SSL_CLIENT_STR_30[] PROGMEM = "INFO: send SMTP command extended HELO"; +static const char ESP32_SSL_CLIENT_STR_31[] PROGMEM = "ERROR: send SMTP command failed!"; +static const char ESP32_SSL_CLIENT_STR_32[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_33[] PROGMEM = "ERROR: reading incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_34[] PROGMEM = "STARTTLS\r\n"; +static const char ESP32_SSL_CLIENT_STR_35[] PROGMEM = "INFO: send STARTTLS protocol command"; +static const char ESP32_SSL_CLIENT_STR_36[] PROGMEM = "ERROR: send STARTTLS protocol command failed!"; +static const char ESP32_SSL_CLIENT_STR_37[] PROGMEM = "ERROR: waiting incoming data failed!"; +static const char ESP32_SSL_CLIENT_STR_38[] PROGMEM = "ERROR: reading incoming data failed!"; + +typedef void (*DebugMsgCallback)(const char *msg); + +typedef struct sslclient_context32 { + int socket; + bool starttls; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + mbedtls_net_context server_fd; + + mbedtls_ctr_drbg_context drbg_ctx; + mbedtls_entropy_context entropy_ctx; + + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt client_cert; + mbedtls_pk_context client_key; + DebugMsgCallback _debugCallback; + + unsigned long handshake_timeout; +} sslclient_context32; + + +void ssl_init(sslclient_context32 *ssl_client); +int start_ssl_client(sslclient_context32 *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey); +void stop_ssl_socket(sslclient_context32 *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); +int data_to_read(sslclient_context32 *ssl_client); +int send_ssl_data(sslclient_context32 *ssl_client, const uint8_t *data, uint16_t len); +int get_ssl_receive(sslclient_context32 *ssl_client, uint8_t *data, int length); +bool verify_ssl_fingerprint(sslclient_context32 *ssl_client, const char* fp, const char* domain_name); +bool verify_ssl_dn(sslclient_context32 *ssl_client, const char* domain_name); +int starttlsHandshake(sslclient_context32 *ssl_client, int port); +void ESP32SSLClientDebugInfo(PGM_P info, sslclient_context32 *ssl_client); + +#endif //ESP32 + +#endif //SSL_CLIENT32_H diff --git a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index 6023507ae..4d943a052 100644 --- a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -28,9 +28,27 @@ #include + +// webcam uses channel 0, so we offset standard PWM +#define PWM_CHANNEL_OFFSET 2 // Analog + +uint8_t pwm_channel[8]={99,99,99,99,99,99,99,99}; + +inline uint32_t pin2chan(uint32_t pin) { + for (uint32_t cnt=0;cnt<8;cnt++) { + if ((pwm_channel[cnt]<99) && (pwm_channel[cnt]==pin)) { + return cnt; + } + } + return 0; +} + inline void analogWrite(uint8_t pin, int val) { + uint32_t channel=pin2chan(pin); + ledcWrite(channel+PWM_CHANNEL_OFFSET,val); + //Serial.printf("write %d - %d\n",channel,val); } inline void analogWriteFreq(uint32_t freq) @@ -40,6 +58,31 @@ inline void analogWriteRange(uint32_t range) { } +inline void analogAttach(uint32_t pin, uint32_t channel) { + pwm_channel[channel&7]=pin; + ledcAttachPin(pin,channel+PWM_CHANNEL_OFFSET); + //Serial.printf("attach %d - %d\n",channel,pin); +} + +inline uint32_t pow2(uint32_t x) { +uint32_t power = 1,bits=0; + while (power < x) { + power*=2; + bits++; + } + return bits-1; +} +// input range is in full range, ledc needs bits +inline void analogWriteFreqRange(uint32_t channel,uint32_t freq, uint32_t irange) { + uint32_t range=pow2(irange); + for (uint32_t cnt=0;cnt<8;cnt++) { + if (pwm_channel[cnt]<99) { + ledcSetup(cnt+PWM_CHANNEL_OFFSET,freq,range); + } + } + Serial.printf("freq - range %d - %d\n",freq,range); +} + #define INPUT_PULLDOWN_16 INPUT_PULLUP typedef double real64_t; @@ -57,7 +100,7 @@ typedef double real64_t; // Serial minimal type to hold the config typedef int SerConfu8; typedef int SerialConfig; -#define analogWrite(a, b) +//#define analogWrite(a, b) // // WS2812 diff --git a/platformio.ini b/platformio.ini index 7be94333c..b748ce0b9 100755 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ extra_configs = platformio_tasmota_env.ini default_envs = ; *** Uncomment by deleting ";" in the line(s) below to select version(s) ; tasmota -; tasmota-ircustom ; alternative to 'tasmota' with full IR protocols activated, you will need to disable some features to keep code not too big +; tasmota-ircustom ; tasmota-minimal ; tasmota-lite ; tasmota-knx @@ -112,9 +112,9 @@ platform_packages = ${tasmota_core.platform_packages} build_flags = ${tasmota_core.build_flags} [tasmota_core] -; *** Esp8266 Arduino core version Tasmota +; *** Esp8266 Arduino core 2.7.0 platform = espressif8266@2.4.0 -platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#36e047e908cfa6eafaaf824988070b49f2c2ff2a +platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino/releases/download/2.7.0/esp8266-2.7.0.zip build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC ; NONOSDK22x_190703 = 2.2.2-dev(38a443e) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 3c3ca8cfd..afd1e6a4b 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -70,67 +70,19 @@ extra_scripts = ${scripts_defaults.extra_scripts} ; pio/http-uploader.py [core_active] -; Select one core set for platform and build_flags +; Activate only (one set) if you want to override the standard core defined in platformio.ini !!! -;platform = ${core_2_6_3.platform} -;platform_packages = ${core_2_6_3.platform_packages} -;build_flags = ${core_2_6_3.build_flags} - -platform = ${tasmota_feature_stage.platform} -platform_packages = ${tasmota_feature_stage.platform_packages} -build_flags = ${tasmota_feature_stage.build_flags} +;platform = ${tasmota_stage.platform} +;platform_packages = ${tasmota_stage.platform_packages} +;build_flags = ${tasmota_stage.build_flags} ;platform = ${core_stage.platform} ;platform_packages = ${core_stage.platform_packages} ;build_flags = ${core_stage.build_flags} -[core_2_6_3] -; *** Esp8266 core for Arduino version 2.6.3 -platform = espressif8266@2.4.0 -platform_packages = -build_flags = ${esp82xx_defaults.build_flags} - -DBEARSSL_SSL_BASIC -; NONOSDK221 -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 -; NONOSDK22x_190313 -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313 -; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105) - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 -; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1) -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024 -; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b) -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105 -; NONOSDK3V0 (known issues) -; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 -; lwIP 1.4 -; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -; lwIP 2 - Low Memory -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -; lwIP 2 - Higher Bandwidth -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -; lwIP 2 - Higher Bandwidth Low Memory no Features -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH -; lwIP 2 - Higher Bandwidth no Features (Tasmota default) - -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -; lwIP 2 - Higher Bandwidth IPv6 (use ONLY if you need IPv6, experimental!) -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH -; VTABLES in Flash (Tasmota default) - -DVTABLES_IN_FLASH -; VTABLES in Heap -; -DVTABLES_IN_DRAM -; VTABLES in IRAM -; -DVTABLES_IN_IRAM -; enable one option set -> No exception recommended -; No exception code in firmware - -fno-exceptions - -lstdc++ -; Exception code in firmware /needs much space! 90k -; -fexceptions -; -lstdc++-exc - -[tasmota_feature_stage] -; *** Esp8266 core for Arduino version Tasmota feature stage +[tasmota_stage] +; *** Esp8266 core for Arduino version Tasmota stage platform = espressif8266@2.4.0 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#36e047e908cfa6eafaaf824988070b49f2c2ff2a build_flags = ${esp82xx_defaults.build_flags} @@ -174,7 +126,7 @@ build_flags = ${esp82xx_defaults.build_flags} ; -lstdc++-exc [core_stage] -; *** Esp8266 core for Arduino version latest beta +; *** Esp8266 core for Arduino version latest development version platform = espressif8266@2.4.0 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git build_flags = ${esp82xx_defaults.build_flags} diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 9d6b6c8e0..8bdb07297 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased (development) +### 8.2.0.6 20200501 + +- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt + ### 8.2.0.5 20200425 - Breaking Change Device Groups multicast address and port (#8270) @@ -12,6 +16,7 @@ - Fix Zigbee DimmerUp/DimmerDown malformed - Add config version tag - Add command ``SetOption73 1`` for button decoupling and send multi-press and hold MQTT messages by Federico Leoni (#8235) +- Add command ``SetOption92 1`` to set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) - Add command ``SO`` as shortcut for command ``SetOption`` ### 8.2.0.3 20200329 diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index 98d19704c..22c3081c5 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/cs-CZ.h b/tasmota/language/cs-CZ.h index 0129bf38b..8ffc87dc2 100644 --- a/tasmota/language/cs-CZ.h +++ b/tasmota/language/cs-CZ.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/de-DE.h b/tasmota/language/de-DE.h index 02a52049e..0fdf11495 100644 --- a/tasmota/language/de-DE.h +++ b/tasmota/language/de-DE.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/el-GR.h b/tasmota/language/el-GR.h index 6da4ffd3d..cbdeee519 100644 --- a/tasmota/language/el-GR.h +++ b/tasmota/language/el-GR.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/en-GB.h b/tasmota/language/en-GB.h index 5941a657d..ac3ffd5a6 100644 --- a/tasmota/language/en-GB.h +++ b/tasmota/language/en-GB.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/es-ES.h b/tasmota/language/es-ES.h index 115aeba12..673aaf6a1 100644 --- a/tasmota/language/es-ES.h +++ b/tasmota/language/es-ES.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/fr-FR.h b/tasmota/language/fr-FR.h index d936bda86..5d985eb3e 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr-FR.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/he-HE.h b/tasmota/language/he-HE.h index 75bbda308..c5624b807 100644 --- a/tasmota/language/he-HE.h +++ b/tasmota/language/he-HE.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/hu-HU.h b/tasmota/language/hu-HU.h index 209c69311..303103212 100644 --- a/tasmota/language/hu-HU.h +++ b/tasmota/language/hu-HU.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/it-IT.h b/tasmota/language/it-IT.h index 7127a498e..2a24880d1 100644 --- a/tasmota/language/it-IT.h +++ b/tasmota/language/it-IT.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ko-KO.h b/tasmota/language/ko-KO.h index 2734cfcb3..b336a27d8 100644 --- a/tasmota/language/ko-KO.h +++ b/tasmota/language/ko-KO.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/nl-NL.h b/tasmota/language/nl-NL.h index aa28fe7d6..49859f5b6 100644 --- a/tasmota/language/nl-NL.h +++ b/tasmota/language/nl-NL.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pl-PL.h b/tasmota/language/pl-PL.h index 679231503..4671885e9 100644 --- a/tasmota/language/pl-PL.h +++ b/tasmota/language/pl-PL.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pt-BR.h b/tasmota/language/pt-BR.h index ca9ceac9d..c5c17bb2d 100644 --- a/tasmota/language/pt-BR.h +++ b/tasmota/language/pt-BR.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pt-PT.h b/tasmota/language/pt-PT.h index 7038200c9..d259939a6 100644 --- a/tasmota/language/pt-PT.h +++ b/tasmota/language/pt-PT.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ro-RO.h b/tasmota/language/ro-RO.h index 8f2a08e4b..1c8559e70 100644 --- a/tasmota/language/ro-RO.h +++ b/tasmota/language/ro-RO.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ru-RU.h b/tasmota/language/ru-RU.h index 1df96769d..53d493065 100644 --- a/tasmota/language/ru-RU.h +++ b/tasmota/language/ru-RU.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "А" diff --git a/tasmota/language/sk-SK.h b/tasmota/language/sk-SK.h index 8e16935e9..85801b8d0 100644 --- a/tasmota/language/sk-SK.h +++ b/tasmota/language/sk-SK.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/sv-SE.h b/tasmota/language/sv-SE.h index f55c6554d..768ed3f74 100644 --- a/tasmota/language/sv-SE.h +++ b/tasmota/language/sv-SE.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/tr-TR.h b/tasmota/language/tr-TR.h index fc345e730..15722b9bf 100644 --- a/tasmota/language/tr-TR.h +++ b/tasmota/language/tr-TR.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/uk-UA.h b/tasmota/language/uk-UA.h index d9501d492..42199bea0 100644 --- a/tasmota/language/uk-UA.h +++ b/tasmota/language/uk-UA.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "А" diff --git a/tasmota/language/zh-CN.h b/tasmota/language/zh-CN.h index 6f5abfc1a..a84c05922 100644 --- a/tasmota/language/zh-CN.h +++ b/tasmota/language/zh-CN.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "安" diff --git a/tasmota/language/zh-TW.h b/tasmota/language/zh-TW.h index fbcfb75b0..23918d54e 100644 --- a/tasmota/language/zh-TW.h +++ b/tasmota/language/zh-TW.h @@ -667,6 +667,27 @@ #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" #define D_SENSOR_WINDMETER_SPEED "WindMeter Speed" +#define D_GPIO_WEBCAM_PWDN_GPIO_NUM "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET_GPIO_NUM "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK_GPIO_NUM "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD_GPIO_NUM "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC_GPIO_NUM "CAM_SIOC" +#define D_GPIO_WEBCAM_Y9_GPIO_NUM "CAM_Y9" +#define D_GPIO_WEBCAM_Y8_GPIO_NUM "CAM_Y8" +#define D_GPIO_WEBCAM_Y7_GPIO_NUM "CAM_Y7" +#define D_GPIO_WEBCAM_Y6_GPIO_NUM "CAM_Y6" +#define D_GPIO_WEBCAM_Y5_GPIO_NUM "CAM_Y5" +#define D_GPIO_WEBCAM_Y4_GPIO_NUM "CAM_Y4" +#define D_GPIO_WEBCAM_Y3_GPIO_NUM "CAM_Y3" +#define D_GPIO_WEBCAM_Y2_GPIO_NUM "CAM_Y2" +#define D_GPIO_WEBCAM_VSYNC_GPIO_NUM "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF_GPIO_NUM "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK_GPIO_NUM "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK_GPIO_NUM "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD1_GPIO_NUM "CAM_HSD1" +#define D_GPIO_WEBCAM_HSD2_GPIO_NUM "CAM_HSD2" +#define D_GPIO_WEBCAM_HSD3_GPIO_NUM "CAM_HSD3" +#define D_GPIO_WEBCAM_PSRCS_GPIO_NUM "CAM_PSRCS" // Units #define D_UNIT_AMPERE "安" diff --git a/tasmota/sendemail.h b/tasmota/sendemail.h index f5a9334ea..d95ba9311 100644 --- a/tasmota/sendemail.h +++ b/tasmota/sendemail.h @@ -41,6 +41,7 @@ class SendEmail public: SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); bool send(const String& from, const String& to, const String& subject, const char *msg); + void send_message_txt(char *msg); ~SendEmail() {client->stop(); delete client;} }; diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino index 3b34f02ab..f04cbf668 100644 --- a/tasmota/sendemail.ino +++ b/tasmota/sendemail.ino @@ -1,5 +1,7 @@ #ifdef USE_SENDMAIL +#ifndef USE_ESP32MAIL + #include "sendemail.h" // enable serial debugging @@ -26,6 +28,8 @@ #define SEND_MAIL_MINRAM 12*1024 #endif +void script_send_email_body(void(*func)(char *)); + #define xPSTR(a) a uint16_t SendMail(char *buffer) { @@ -47,7 +51,7 @@ uint16_t SendMail(char *buffer) { // return if not enough memory - uint16_t mem=ESP_getFreeHeap(); + uint16_t mem=ESP.getFreeHeap(); if (memprintln(msg); } @@ -370,5 +377,289 @@ exit: return status; } +void xsend_message_txt(char *msg) { + g_client->println(msg); +} +#else + +/* + * Created by K. Suwatchai (Mobizt) + * + * Email: k_suwatchai@hotmail.com + * + * Github: https://github.com/mobizt + * + * Copyright (c) 2019 mobizt + * +*/ + + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//To receive Email for Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + +#include "ESP32_MailClient.h" +#include "SD.h" + +//For demo only +//#include "image.h" + +#ifndef SEND_MAIL32_MINRAM +#define SEND_MAIL32_MINRAM 30*1024 +#endif + +void script_send_email_body(void(*func)(char *)); + +#define xPSTR(a) a +//The Email Sending data object contains config and data to send +SMTPData smtpData; + +//Callback function to get the Email sending status +//void sendCallback(SendStatus info); +//#define DEBUG_EMAIL_PORT + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + uint16_t status=1; + uint16_t blen; + char *endcmd; + + // return if not enough memory + uint32_t mem=ESP.getFreeHeap(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); + if (mem"); + + //Set the storage types to read the attach files (SD is default) + //smtpData.setFileStorageType(MailClientStorageType::SPIFFS); +#if defined (USE_SCRIPT) && defined(USE_SCRIPT_FATFS) + smtpData.setFileStorageType(MailClientStorageType::SD); +#endif + //smtpData.setSendCallback(sendCallback); + + //Start sending Email, can be set callback function to track the status + if (!MailClient.sendMail(smtpData)) { + //Serial.println("Error sending Email, " + MailClient.smtpErrorReason()); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Error sending Email, %s"), MailClient.smtpErrorReason()); + + } else { + status=0; + } + //Clear all data from Email object to free memory + smtpData.empty(); + + exit: + if (oparams) free(oparams); + return status; +} + + +void send_message_txt(char *txt) { + if (*txt=='&') { + txt++; + smtpData.addAttachFile(txt); + } else if (*txt=='$') { + txt++; +#if defined(ESP32) && defined(USE_WEBCAM) + uint32_t cnt; + uint8_t *buff; + uint32_t len,picmax; + picmax=get_picstore(-1,0); + cnt=*txt&7; + if (cnt<1 || cnt>picmax) cnt=1; + len=get_picstore(cnt-1,&buff); + if (len) { + char str[12]; + sprintf(str,"img_%1d.jpg",cnt+1); + smtpData.addAttachData(str, "image/jpg",buff,len); + } +#endif + } else { + smtpData.addMessage(txt); + } +} + +/* +//Callback function to get the Email sending status +void sendCallback(SendStatus msg) +{ + //Print the current status + Serial.println(msg.info()); + + //Do something when complete + if (msg.success()) + { + Serial.println("----------------"); + } +} +*/ +#endif + #endif // USE_SENDMAIL diff --git a/tasmota/settings.h b/tasmota/settings.h index 0cf2bd9cf..76bf18f12 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -111,7 +111,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t zigbee_distinct_topics : 1; // bit 7 (v8.1.0.10) - SetOption89 - Distinct MQTT topics per device for Zigbee (#7835) uint32_t only_json_message : 1; // bit 8 (v8.2.0.3) - SetOption90 - Disable non-json MQTT response uint32_t fade_at_startup : 1; // bit 9 (v8.2.0.3) - SetOption91 - Enable light fading at start/power on - uint32_t pwm_ct_mode : 1; // bit 10 () - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) + uint32_t pwm_ct_mode : 1; // bit 10 (v8.2.0.4) - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...) uint32_t spare11 : 1; uint32_t spare12 : 1; uint32_t spare13 : 1; @@ -358,8 +358,15 @@ struct { SysBitfield3 flag3; // 3A0 uint8_t switchmode[MAX_SWITCHES]; // 3A4 (6.0.0b - moved from 0x4CA) +#ifdef ESP8266 char ex_friendlyname[4][33]; // 3AC char ex_switch_topic[33]; // 430 +#else // ESP32 + myio my_gp; // 3AC - 2 x 40 bytes (ESP32) + mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32) + + uint8_t free_esp32_446[11]; // 446 +#endif // ESP8266 - ESP32 char serial_delimiter; // 451 uint8_t seriallog_level; // 452 @@ -403,14 +410,7 @@ struct { uint32_t ip_address[4]; // 544 unsigned long energy_kWhtotal; // 554 -#ifdef ESP8266 char ex_mqtt_fulltopic[100]; // 558 -#else // ESP32 - myio my_gp; // 558 - 40 bytes (ESP32) - mytmplt user_template; // 580 - 37 bytes (ESP32) - - uint8_t free_esp32_5a5[23]; // 5A5 -#endif // ESP8266 - ESP32 SysBitfield2 flag2; // 5BC unsigned long pulse_counter[MAX_COUNTERS]; // 5C0 diff --git a/tasmota/settings.ino b/tasmota/settings.ino index f490f3eeb..2548e2b14 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -1386,6 +1386,13 @@ void SettingsDelta(void) #endif // ESP32 } + if (Settings.version < 0x08020006) { +#ifdef ESP32 + Settings.module = WEMOS; + ModuleDefault(WEMOS); +#endif // ESP32 + } + Settings.version = VERSION; SettingsSave(1); } diff --git a/tasmota/support.ino b/tasmota/support.ino index ebcca3294..ff767caf6 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1073,37 +1073,38 @@ int ResponseJsonEndEnd(void) * GPIO Module and Template management \*********************************************************************************************/ +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception +uint32_t Pin(uint32_t gpio, uint32_t index) ICACHE_RAM_ATTR; +#endif + uint32_t Pin(uint32_t gpio, uint32_t index = 0); uint32_t Pin(uint32_t gpio, uint32_t index) { -//#ifdef ESP8266 - return pin[gpio + index]; // Pin number configured for gpio or 99 if not used -/* -#else +#ifdef ESP8266 + uint16_t real_gpio = gpio + index; +#else // ESP32 uint16_t real_gpio = (gpio << 5) + index; - for (uint32_t i = 0; i < ARRAY_SIZE(pin); i++) { - if (pin[i] == real_gpio) { +#endif // ESP8266 - ESP32 + for (uint32_t i = 0; i < ARRAY_SIZE(gpio_pin); i++) { + if (gpio_pin[i] == real_gpio) { return i; // Pin number configured for gpio } } return 99; // No pin used for gpio -#endif -*/ +} + +boolean PinUsed(uint32_t gpio, uint32_t index = 0); +boolean PinUsed(uint32_t gpio, uint32_t index) { + return (Pin(gpio, index) < 99); } void SetPin(uint32_t lpin, uint32_t gpio) { -//#ifdef ESP8266 - pin[gpio] = lpin; -/* -#else - pin[lpin] = gpio; -#endif -*/ + gpio_pin[lpin] = gpio; } -void DigitalWrite(uint32_t gpio_pin, uint32_t state) +void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state) { - if (Pin(gpio_pin) < 99) { - digitalWrite(Pin(gpio_pin), state &1); + if (PinUsed(gpio_pin, index)) { + digitalWrite(Pin(gpio_pin, index), state &1); } } @@ -1147,13 +1148,13 @@ String ModuleName(void) void ModuleGpios(myio *gp) { -//#ifdef ESP8266 +#ifdef ESP8266 uint8_t *dest = (uint8_t *)gp; uint8_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; -//#else -// uint16_t *dest = (uint16_t *)gp; -// uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; -//#endif +#else // ESP32 + uint16_t *dest = (uint16_t *)gp; + uint16_t src[ARRAY_SIZE(Settings.user_template.gp.io)]; +#endif // ESP8266 - ESP32 memset(dest, GPIO_NONE, sizeof(myio)); if (USER_MODULE == Settings.module) { @@ -1185,24 +1186,15 @@ gpio_flag ModuleFlag(void) { gpio_flag flag; -#ifdef ESP8266 if (USER_MODULE == Settings.module) { flag = Settings.user_template.flag; } else { +#ifdef ESP8266 memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); - } #else // ESP32 - if (USER_MODULE == Settings.module) { -/* - gpio_flag gpio_adc0; - memcpy_P(&gpio_adc0, &Settings.user_template.gp + ADC0_PIN - MIN_FLASH_PINS, sizeof(gpio_flag)); - flag = Settings.user_template.flag.data + gpio_adc0.data; -*/ - memcpy_P(&flag, &Settings.user_template.gp + ADC0_PIN - MIN_FLASH_PINS, sizeof(gpio_flag)); - } else { - memcpy_P(&flag, &kModules.gp + ADC0_PIN - MIN_FLASH_PINS, sizeof(gpio_flag)); - } + memcpy_P(&flag, &kModules.flag, sizeof(gpio_flag)); #endif // ESP8266 - ESP32 + } return flag; } @@ -1230,7 +1222,7 @@ bool FlashPin(uint32_t pin) return (((pin > 5) && (pin < 9)) || (11 == pin)); } -uint8_t ValidPin(uint32_t pin, uint32_t gpio) +uint32_t ValidPin(uint32_t pin, uint32_t gpio) { if (FlashPin(pin)) { return GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11 @@ -1248,21 +1240,27 @@ uint8_t ValidPin(uint32_t pin, uint32_t gpio) bool ValidGPIO(uint32_t pin, uint32_t gpio) { +#ifdef ESP8266 return (GPIO_USER == ValidPin(pin, gpio)); // Only allow GPIO_USER pins +#else // ESP32 + return (GPIO_USER == ValidPin(pin, gpio >> 5)); // Only allow GPIO_USER pins +#endif // ESP8266 - ESP32 } +#ifdef ESP8266 bool ValidAdc(void) { gpio_flag flag = ModuleFlag(); uint32_t template_adc0 = flag.data &15; return (ADC0_USER == template_adc0); } +#endif // ESP8266 -//#ifdef ESP8266 +#ifdef ESP8266 bool GetUsedInModule(uint32_t val, uint8_t *arr) -//#else -//bool GetUsedInModule(uint32_t val, uint16_t *arr) -//#endif +#else // ESP32 +bool GetUsedInModule(uint32_t val, uint16_t *arr) +#endif // ESP8266 - ESP32 { int offset = 0; @@ -1332,7 +1330,7 @@ bool JsonTemplate(const char* dataBuf) #ifdef ESP8266 StaticJsonBuffer<400> jb; // 331 from https://arduinojson.org/v5/assistant/ #else - StaticJsonBuffer<800> jb; // 654 from https://arduinojson.org/v5/assistant/ + StaticJsonBuffer<999> jb; // 654 from https://arduinojson.org/v5/assistant/ #endif JsonObject& obj = jb.parseObject(dataBuf); if (!obj.success()) { return false; } @@ -1348,11 +1346,11 @@ bool JsonTemplate(const char* dataBuf) } } if (obj[D_JSON_FLAG].success()) { - uint8_t flag = obj[D_JSON_FLAG] | 0; + uint32_t flag = obj[D_JSON_FLAG] | 0; memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); } if (obj[D_JSON_BASE].success()) { - uint8_t base = obj[D_JSON_BASE]; + uint32_t base = obj[D_JSON_BASE]; if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } Settings.user_template_base = base -1; // Default WEMOS } @@ -1851,3 +1849,16 @@ void AddLogMissed(const char *sensor, uint32_t misses) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); } + +void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) { + snprintf_P(log_data, sizeof(log_data), PSTR("DMP:")); + for (uint32_t i = 0; i < count; i++) { + if (1 == size) { // uint8_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer)); + } else { // uint16_t + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X%02X"), log_data, *(buffer +1), *(buffer)); + } + buffer += size; + } + AddLog(loglevel); +} diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino index 59b90e39d..659a02348 100644 --- a/tasmota/support_button.ino +++ b/tasmota/support_button.ino @@ -60,7 +60,7 @@ void ButtonInit(void) { Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (Pin(GPIO_KEY1, i) < 99) { + if (PinUsed(GPIO_KEY1, i)) { Button.present++; pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } @@ -135,7 +135,7 @@ void ButtonHandler(void) } else #endif // ESP8266 - if (Pin(GPIO_KEY1, button_index) < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); } diff --git a/tasmota/support_button_v2.ino b/tasmota/support_button_v2.ino index 3bb1f9153..4dc94cf13 100644 --- a/tasmota/support_button_v2.ino +++ b/tasmota/support_button_v2.ino @@ -60,7 +60,7 @@ void ButtonInit(void) { Button.present = 0; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (Pin(GPIO_KEY1, i) < 99) { + if (PinUsed(GPIO_KEY1, i)) { Button.present++; pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); } @@ -132,7 +132,7 @@ void ButtonHandler(void) } else #endif // ESP8266 - if (Pin(GPIO_KEY1, button_index) < 99) { + if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index)); } @@ -266,7 +266,7 @@ void ButtonHandler(void) } else { SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules - if ((Button.press_counter[button_index] > 1 && Pin(GPIO_REL1, Button.press_counter[button_index]-1) < 99) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { + if ((Button.press_counter[button_index] > 1 && PinUsed(GPIO_REL1, Button.press_counter[button_index]-1)) && Button.press_counter[button_index] <= MAX_RELAY_BUTTON1) { ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1)); } diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 2066ee56c..475517d47 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -1045,11 +1045,22 @@ void CmndGpio(void) if (XdrvMailbox.index < ARRAY_SIZE(Settings.my_gp.io)) { myio cmodule; ModuleGpios(&cmodule); - if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < AGPIO(GPIO_SENSOR_END))) { bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == XdrvMailbox.payload) { present = true; } + if (midx == XdrvMailbox.payload) { + present = true; + break; + } +#else // ESP32 + uint32_t midx = pgm_read_word(kGpioNiceList + i); + if ((XdrvMailbox.payload >= (midx & 0xFFE0)) && (XdrvMailbox.payload < midx)) { + present = true; + break; + } +#endif // ESP8266 - ESP32 } if (present) { for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { @@ -1064,25 +1075,38 @@ void CmndGpio(void) Response_P(PSTR("{")); bool jsflg = false; for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { - if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { + if (ValidGPIO(i, cmodule.io[i]) || ((AGPIO(GPIO_USER) == XdrvMailbox.payload) && !FlashPin(i))) { if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; - uint8_t sensor_type = Settings.my_gp.io[i]; + uint32_t sensor_type = Settings.my_gp.io[i]; if (!ValidGPIO(i, cmodule.io[i])) { sensor_type = cmodule.io[i]; - if (GPIO_USER == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here + if (AGPIO(GPIO_USER) == sensor_type) { // A user GPIO equals a not connected (=GPIO_NONE) GPIO here sensor_type = GPIO_NONE; } } - uint8_t sensor_name_idx = sensor_type; + char sindex[4] = { 0 }; +#ifdef ESP8266 + uint32_t sensor_name_idx = sensor_type; +#else // ESP32 + uint32_t sensor_name_idx = sensor_type >> 5; + uint32_t nice_list_search = sensor_type & 0xFFE0; + for (uint32_t j = 0; j < ARRAY_SIZE(kGpioNiceList); j++) { + uint32_t nls_idx = pgm_read_word(kGpioNiceList + j); + if (((nls_idx & 0xFFE0) == nice_list_search) && ((nls_idx & 0x001F) > 0)) { + snprintf_P(sindex, sizeof(sindex), PSTR("%d"), (sensor_type & 0x001F) +1); + break; + } + } +#endif // ESP8266 - ESP32 const char *sensor_names = kSensorNames; - if (sensor_type > GPIO_FIX_START) { - sensor_name_idx = sensor_type - GPIO_FIX_START -1; + if (sensor_name_idx > GPIO_FIX_START) { + sensor_name_idx = sensor_name_idx - GPIO_FIX_START -1; sensor_names = kSensorNamesFixed; } char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), - i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex); } } if (jsflg) { @@ -1099,8 +1123,14 @@ void CmndGpios(void) ModuleGpios(&cmodule); uint32_t lines = 1; bool jsflg = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { +#ifdef ESP8266 uint32_t midx = pgm_read_byte(kGpioNiceList + i); + uint32_t ridx = midx; +#else // ESP32 + uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; + uint32_t midx = ridx >> 5; +#endif // ESP8266 - ESP32 if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } if (!jsflg) { Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); @@ -1109,7 +1139,7 @@ void CmndGpios(void) } jsflg = true; char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), ridx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == ARRAY_SIZE(kGpioNiceList) -1)) { ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); jsflg = false; @@ -1167,7 +1197,7 @@ void CmndTemplate(void) void CmndPwm(void) { if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (Pin(GPIO_PWM1, XdrvMailbox.index -1) < 99)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && PinUsed(GPIO_PWM1, XdrvMailbox.index -1)) { Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; analogWrite(Pin(GPIO_PWM1, XdrvMailbox.index -1), bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); } @@ -1181,7 +1211,11 @@ void CmndPwmfrequency(void) { if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; +#ifdef ESP8266 analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) +#else + analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); +#endif } ResponseCmndNumber(Settings.pwm_frequency); } @@ -1195,7 +1229,11 @@ void CmndPwmrange(void) Settings.pwm_value[i] = Settings.pwm_range; } } +#ifdef ESP8266 analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) +#else + analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); +#endif } ResponseCmndNumber(Settings.pwm_range); } @@ -1694,7 +1732,7 @@ void CmndAltitude(void) void CmndLedPower(void) { if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { - if (99 == Pin(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } + if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; } if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { Settings.ledstate &= 8; // Disable power control uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to control @@ -1713,14 +1751,14 @@ void CmndLedPower(void) break; } blinks = 0; - if (99 == Pin(GPIO_LEDLNK)) { + if (!PinUsed(GPIO_LEDLNK)) { SetLedPower(Settings.ledstate &8); } else { SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); } } bool state = bitRead(led_power, XdrvMailbox.index -1); - if (99 == Pin(GPIO_LEDLNK)) { + if (!PinUsed(GPIO_LEDLNK)) { state = bitRead(Settings.ledstate, 3); } ResponseCmndIdxChar(GetStateText(state)); diff --git a/tasmota/support_rotary.ino b/tasmota/support_rotary.ino index f93b288c5..25c064a18 100644 --- a/tasmota/support_rotary.ino +++ b/tasmota/support_rotary.ino @@ -80,7 +80,7 @@ bool RotaryButtonPressed(void) void RotaryInit(void) { Rotary.present = 0; - if ((Pin(GPIO_ROT1A) < 99) && (Pin(GPIO_ROT1B) < 99)) { + if (PinUsed(GPIO_ROT1A) && PinUsed(GPIO_ROT1B)) { Rotary.present++; pinMode(Pin(GPIO_ROT1A), INPUT_PULLUP); pinMode(Pin(GPIO_ROT1B), INPUT_PULLUP); diff --git a/tasmota/support_switch.ino b/tasmota/support_switch.ino index 3fba37b3c..64837c74c 100644 --- a/tasmota/support_switch.ino +++ b/tasmota/support_switch.ino @@ -86,7 +86,7 @@ void SwitchProbe(void) uint8_t force_low = (Settings.switch_debounce % 50) &2; // 52, 102, 152 etc for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { // Olimex user_switch2.c code to fix 50Hz induced pulses if (1 == digitalRead(Pin(GPIO_SWT1, i))) { @@ -127,7 +127,7 @@ void SwitchInit(void) Switch.present = 0; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Switch.last_state[i] = 1; // Init global to virtual switch state; - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { Switch.present++; pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_SWT1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check @@ -148,7 +148,7 @@ void SwitchHandler(uint8_t mode) uint16_t loops_per_second = 1000 / Settings.switch_debounce; for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if ((Pin(GPIO_SWT1, i) < 99) || (mode)) { + if (PinUsed(GPIO_SWT1, i) || (mode)) { uint8_t button = Switch.virtual_state[i]; uint8_t switchflag = POWER_TOGGLE +1; diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 1d66b978c..c335bd9cb 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -166,7 +166,7 @@ void SetLatchingRelay(power_t lpower, uint32_t state) for (uint32_t i = 0; i < devices_present; i++) { uint32_t port = (i << 1) + ((latching_power >> i) &1); - DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); + DigitalWrite(GPIO_REL1, port, bitRead(rel_inverted, port) ? !state : state); } } @@ -228,7 +228,7 @@ void SetDevicePower(power_t rpower, uint32_t source) for (uint32_t i = 0; i < devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); + DigitalWrite(GPIO_REL1, i, bitRead(rel_inverted, i) ? !state : state); } rpower >>= 1; } @@ -325,7 +325,7 @@ void SetPowerOnState(void) // Issue #526 and #909 for (uint32_t i = 0; i < devices_present; i++) { if (!Settings.flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 - if ((i < MAX_RELAYS) && (Pin(GPIO_REL1, i) < 99)) { + if ((i < MAX_RELAYS) && PinUsed(GPIO_REL1, i)) { bitWrite(power, i, digitalRead(Pin(GPIO_REL1, i)) ^ bitRead(rel_inverted, i)); } } @@ -338,12 +338,12 @@ void SetPowerOnState(void) void SetLedPowerIdx(uint32_t led, uint32_t state) { - if ((99 == Pin(GPIO_LEDLNK)) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present - if (Pin(GPIO_LED1, 1) < 99) { + if (!PinUsed(GPIO_LEDLNK) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present + if (PinUsed(GPIO_LED1, 1)) { led = 1; } } - if (Pin(GPIO_LED1, led) < 99) { + if (PinUsed(GPIO_LED1, led)) { uint32_t mask = 1 << led; if (state) { state = 1; @@ -351,7 +351,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) } else { led_power &= (0xFF ^ mask); } - DigitalWrite(Pin(GPIO_LED1, led), bitRead(led_inverted, led) ? !state : state); + DigitalWrite(GPIO_LED1, led, bitRead(led_inverted, led) ? !state : state); } #ifdef USE_BUZZER if (led == 0) { @@ -362,7 +362,7 @@ void SetLedPowerIdx(uint32_t led, uint32_t state) void SetLedPower(uint32_t state) { - if (99 == Pin(GPIO_LEDLNK)) { // Legacy - Only use LED1 and/or LED2 + if (!PinUsed(GPIO_LEDLNK)) { // Legacy - Only use LED1 and/or LED2 SetLedPowerIdx(0, state); } else { power_t mask = 1; @@ -608,7 +608,7 @@ void MqttShowPWMState(void) ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); bool first = true; for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); first = false; } @@ -703,9 +703,9 @@ bool MqttShowSensor(void) int json_data_start = strlen(mqtt_data); for (uint32_t i = 0; i < MAX_SWITCHES; i++) { #ifdef USE_TM1638 - if ((Pin(GPIO_SWT1, i) < 99) || ((Pin(GPIO_TM16CLK) < 99) && (Pin(GPIO_TM16DIO) < 99) && (Pin(GPIO_TM16STB) < 99))) { + if (PinUsed(GPIO_SWT1, i) || (PinUsed(GPIO_TM16CLK) && PinUsed(GPIO_TM16DIO) && PinUsed(GPIO_TM16STB))) { #else - if (Pin(GPIO_SWT1, i) < 99) { + if (PinUsed(GPIO_SWT1, i)) { #endif // USE_TM1638 ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); } @@ -913,7 +913,7 @@ void Every250mSeconds(void) if (200 == blinks) blinks = 0; // Disable blink } } - if (Settings.ledstate &1 && (Pin(GPIO_LEDLNK) < 99 || !(blinks || restart_flag || ota_state_flag)) ) { + if (Settings.ledstate &1 && (PinUsed(GPIO_LEDLNK) || !(blinks || restart_flag || ota_state_flag)) ) { bool tstate = power & Settings.ledmask; #ifdef ESP8266 if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { @@ -1320,7 +1320,7 @@ void SerialInput(void) void ResetPwm(void) { for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); // analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); } @@ -1353,24 +1353,25 @@ void GpioInit(void) } for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { - if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { - Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template + if ((Settings.user_template.gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.user_template.gp.io[i] < AGPIO(GPIO_USER))) { + Settings.user_template.gp.io[i] = AGPIO(GPIO_USER); // Fix not supported sensor ids in template } } myio def_gp; ModuleGpios(&def_gp); for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { - if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { + if ((Settings.my_gp.io[i] >= AGPIO(GPIO_SENSOR_END)) && (Settings.my_gp.io[i] < AGPIO(GPIO_USER))) { Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module } else if (Settings.my_gp.io[i] > GPIO_NONE) { my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors } - if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < AGPIO(GPIO_USER))) { my_module.io[i] = def_gp.io[i]; // Force Template override } } +#ifdef ESP8266 if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { Settings.my_adc0 = ADC0_NONE; // Fix not supported sensor ids in module } @@ -1382,51 +1383,49 @@ void GpioInit(void) if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { my_adc0 = template_adc0; // Force Template override } +#endif - for (uint32_t i = 0; i < GPIO_MAX; i++) { - SetPin(99, i); - } for (uint32_t i = 0; i < ARRAY_SIZE(my_module.io); i++) { uint32_t mpin = ValidPin(i, my_module.io[i]); DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - if (mpin) { + if (mpin) { // Above GPIO_NONE XdrvMailbox.index = mpin; XdrvMailbox.payload = i; - if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - SwitchPullupFlag(mpin - GPIO_SWT1_NP); - mpin -= (GPIO_SWT1_NP - GPIO_SWT1); + if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES))) { + SwitchPullupFlag(mpin - AGPIO(GPIO_SWT1_NP)); + mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); } - else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3 - mpin -= (GPIO_KEY1_INV - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS))) { + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3 - mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); + else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS))) { + ButtonPullupFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV_NP)); // 0 .. 3 + mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); } - else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { - bitSet(rel_inverted, mpin - GPIO_REL1_INV); - mpin -= (GPIO_REL1_INV - GPIO_REL1); + else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS))) { + bitSet(rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); + mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } - else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { - bitSet(led_inverted, mpin - GPIO_LED1_INV); - mpin -= (GPIO_LED1_INV - GPIO_LED1); + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { + bitSet(led_inverted, mpin - AGPIO(GPIO_LED1_INV)); + mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); } - else if (mpin == GPIO_LEDLNK_INV) { + else if (mpin == AGPIO(GPIO_LEDLNK_INV)) { ledlnk_inverted = 1; - mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); + mpin -= (AGPIO(GPIO_LEDLNK_INV) - AGPIO(GPIO_LEDLNK)); } - else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { - bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); - mpin -= (GPIO_PWM1_INV - GPIO_PWM1); + else if ((mpin >= AGPIO(GPIO_PWM1_INV)) && (mpin < (AGPIO(GPIO_PWM1_INV) + MAX_PWMS))) { + bitSet(pwm_inverted, mpin - AGPIO(GPIO_PWM1_INV)); + mpin -= (AGPIO(GPIO_PWM1_INV) - AGPIO(GPIO_PWM1)); } else if (XdrvCall(FUNC_PIN_STATE)) { mpin = XdrvMailbox.index; @@ -1435,22 +1434,25 @@ void GpioInit(void) mpin = XdrvMailbox.index; }; } - if (mpin) { SetPin(i, mpin); } + if (mpin) { SetPin(i, mpin); } // Anything above GPIO_NONE and below GPIO_SENSOR_END } +// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)gpio_pin, ARRAY_SIZE(gpio_pin), sizeof(gpio_pin[0])); + #ifdef ESP8266 if ((2 == Pin(GPIO_TXD)) || (H801 == my_module_type)) { Serial.set_tx(2); } #endif // ESP8266 +#ifdef ESP8266 analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h) analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c) +#else + analogWriteFreqRange(0,Settings.pwm_frequency,Settings.pwm_range); +#endif #ifdef USE_SPI - spi_flg = ((((Pin(GPIO_SPI_CS) < 99) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || (((Pin(GPIO_SPI_DC) < 99) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); + spi_flg = (((PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_CS) > 14)) || (Pin(GPIO_SPI_CS) < 12)) || ((PinUsed(GPIO_SPI_DC) && (Pin(GPIO_SPI_DC) > 14)) || (Pin(GPIO_SPI_DC) < 12))); if (spi_flg) { - for (uint32_t i = 0; i < GPIO_MAX; i++) { - if ((Pin(i) >= 12) && (Pin(i) <=14)) { SetPin(99, i); } - } my_module.io[12] = GPIO_SPI_MISO; SetPin(12, GPIO_SPI_MISO); my_module.io[13] = GPIO_SPI_MOSI; @@ -1458,7 +1460,7 @@ void GpioInit(void) my_module.io[14] = GPIO_SPI_CLK; SetPin(14, GPIO_SPI_CLK); } - soft_spi_flg = ((Pin(GPIO_SSPI_CS) < 99) && (Pin(GPIO_SSPI_SCLK) < 99) && ((Pin(GPIO_SSPI_MOSI) < 99) || (Pin(GPIO_SSPI_MOSI) < 99))); + soft_spi_flg = (PinUsed(GPIO_SSPI_CS) && PinUsed(GPIO_SSPI_SCLK) && (PinUsed(GPIO_SSPI_MOSI) || PinUsed(GPIO_SSPI_MISO))); #endif // USE_SPI // Set any non-used GPIO to INPUT - Related to resetPins() in support_legacy_cores.ino @@ -1474,7 +1476,7 @@ void GpioInit(void) } #ifdef USE_I2C - i2c_flg = ((Pin(GPIO_I2C_SCL) < 99) && (Pin(GPIO_I2C_SDA) < 99)); + i2c_flg = (PinUsed(GPIO_I2C_SCL) && PinUsed(GPIO_I2C_SDA)); if (i2c_flg) { Wire.begin(Pin(GPIO_I2C_SDA), Pin(GPIO_I2C_SCL)); } @@ -1506,8 +1508,13 @@ void GpioInit(void) #endif // ESP8266 for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (Pin(GPIO_PWM1, i) < 99) { + if (PinUsed(GPIO_PWM1, i)) { pinMode(Pin(GPIO_PWM1, i), OUTPUT); +#ifdef ESP32 + analogAttach(Pin(GPIO_PWM1, i),i); + analogWriteFreqRange(i,Settings.pwm_frequency,Settings.pwm_range); +#endif + if (light_type) { // force PWM GPIOs to low or high mode, see #7165 analogWrite(Pin(GPIO_PWM1, i), bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); @@ -1519,7 +1526,7 @@ void GpioInit(void) } for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (Pin(GPIO_REL1, i) < 99) { + if (PinUsed(GPIO_REL1, i)) { pinMode(Pin(GPIO_REL1, i), OUTPUT); devices_present++; #ifdef ESP8266 @@ -1532,11 +1539,10 @@ void GpioInit(void) } for (uint32_t i = 0; i < MAX_LEDS; i++) { - if (Pin(GPIO_LED1, i) < 99) { + if (PinUsed(GPIO_LED1, i)) { #ifdef USE_ARILUX_RF - if ((3 == i) && (leds_present < 2) && (99 == Pin(GPIO_ARIRFSEL))) { + if ((3 == i) && (leds_present < 2) && !PinUsed(GPIO_ARIRFSEL)) { SetPin(Pin(GPIO_LED4), GPIO_ARIRFSEL); // Legacy support where LED4 was Arilux RF enable - SetPin(99, GPIO_LED4); } else { #endif pinMode(Pin(GPIO_LED1, i), OUTPUT); @@ -1547,13 +1553,13 @@ void GpioInit(void) #endif } } - if (Pin(GPIO_LEDLNK) < 99) { + if (PinUsed(GPIO_LEDLNK)) { pinMode(Pin(GPIO_LEDLNK), OUTPUT); digitalWrite(Pin(GPIO_LEDLNK), ledlnk_inverted); } #ifdef USE_PWM_DIMMER - if (PWM_DIMMER == my_module_type && Pin(GPIO_REL1) < 99) { devices_present--; } + if (PWM_DIMMER == my_module_type && PinUsed(GPIO_REL1)) { devices_present--; } #endif // USE_PWM_DIMMER ButtonInit(); diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index aae4df24c..a1a3cb473 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -41,14 +41,8 @@ * Power Type \*********************************************************************************************/ -//#ifdef ESP8266 typedef unsigned long power_t; // Power (Relay) type const uint32_t POWER_MASK = 0xffffffffUL; // Power (Relay) full mask -//#endif // ESP8266 -//#ifdef ESP32 -//typedef uint64_t power_t; // Power (Relay) type -//const uint64_t POWER_MASK = 0xffffffffffffffffull; // Power (Relay) full mask -//#endif // ESP32 /*********************************************************************************************\ * Constants diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 69be1bbe5..6425d7fae 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -118,9 +118,11 @@ uint16_t tele_period = 9999; // Tele period timer uint16_t blink_counter = 0; // Number of blink cycles uint16_t seriallog_timer = 0; // Timer to disable Seriallog uint16_t syslog_timer = 0; // Timer to re-enable syslog_level -//#ifdef ESP32 -//uint16_t pin[MAX_GPIO_PIN]; // Possible pin configurations -//#endif + +#ifdef ESP32 +uint16_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number +#endif // ESP32 + int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) uint8_t mqtt_cmnd_blocked = 0; // Ignore flag for publish command @@ -129,9 +131,11 @@ uint8_t state_250mS = 0; // State 250msecond per second flag uint8_t latching_relay_pulse = 0; // Latching relay pulse timer uint8_t ssleep; // Current copy of Settings.sleep uint8_t blinkspeed = 1; // LED blink rate -//#ifdef ESP8266 -uint8_t pin[GPIO_MAX]; // Possible pin configurations -//#endif + +#ifdef ESP8266 +uint8_t gpio_pin[MAX_GPIO_PIN] = { 0 }; // GPIO functions indexed by pin number +#endif // ESP8266 - ESP32 + uint8_t active_device = 1; // Active device in ExecuteCommandPower uint8_t leds_present = 0; // Max number of LED supported uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off)) @@ -147,7 +151,7 @@ uint8_t devices_present = 0; // Max number of devices supported uint8_t seriallog_level; // Current copy of Settings.seriallog_level uint8_t syslog_level; // Current copy of Settings.syslog_level uint8_t my_module_type; // Current copy of Settings.module or user template type -uint8_t my_adc0; // Active copy of Module ADC0 +uint8_t my_adc0 = 0; // Active copy of Module ADC0 uint8_t last_source = 0; // Last command source uint8_t shutters_present = 0; // Number of actual define shutters uint8_t prepped_loglevel = 0; // Delayed log level message @@ -268,16 +272,17 @@ void setup(void) for (uint32_t i = 0; i < ARRAY_SIZE(Settings.my_gp.io); i++) { Settings.my_gp.io[i] = GPIO_NONE; // Reset user defined GPIO disabling sensors } +#ifdef ESP8266 Settings.my_adc0 = ADC0_NONE; // Reset user defined ADC0 disabling sensors +#endif } if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { // Restarted 6 times #ifdef ESP8266 Settings.module = SONOFF_BASIC; // Reset module to Sonoff Basic // Settings.last_module = SONOFF_BASIC; -#endif // ESP8266 -#ifdef ESP32 +#else // ESP32 Settings.module = WEMOS; // Reset module to Wemos -#endif // ESP32 +#endif // ESP8266 - ESP32 } AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); } diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 9b72ba954..27a730ec5 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -53,10 +53,6 @@ extern "C" void resetPins(); * Mandatory defines satisfying disabled defines \*********************************************************************************************/ -#ifndef MODULE -#define MODULE SONOFF_BASIC // [Module] Select default model -#endif - #ifdef USE_EMULATION_HUE #define USE_EMULATION #endif @@ -278,6 +274,10 @@ const char kWebColors[] PROGMEM = #ifdef ESP8266 +#ifndef MODULE +#define MODULE SONOFF_BASIC // [Module] Select default model +#endif + #ifndef ARDUINO_ESP8266_RELEASE #define ARDUINO_CORE_RELEASE "STAGE" #else @@ -288,6 +288,10 @@ const char kWebColors[] PROGMEM = #ifdef ESP32 +#ifndef MODULE +#define MODULE WEMOS // [Module] Select default model +#endif + #ifndef ARDUINO_ESP32_RELEASE #define ARDUINO_CORE_RELEASE "STAGE" #else @@ -295,7 +299,7 @@ const char kWebColors[] PROGMEM = #endif // ARDUINO_ESP32_RELEASE #undef USE_HM10 // Disable support for HM-10 as a BLE-bridge as an alternative is using the internal ESP32 BLE -#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm ss it's library cc1101 is not compatible with ESP32 +#undef USE_KEELOQ // Disable support for Jarolift rollers by Keeloq algorithm as it's library cc1101 is not compatible with ESP32 #undef USE_DISPLAY_ILI9488 // Disable as it's library JaretBurkett_ILI9488-gemu-1.0 is not compatible with ESP32 #undef USE_DISPLAY_SSD1351 // Disable as it's library Adafruit_SSD1351_gemu-1.0 is not compatible with ESP32 @@ -322,6 +326,12 @@ const char kWebColors[] PROGMEM = #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif +#ifdef ESP8266 +#define AGPIO(x) (x) +#else // ESP32 +#define AGPIO(x) (x<<5) +#endif // ESP8266 - ESP32 + #ifdef USE_DEVICE_GROUPS #define SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, ...) _SendDeviceGroupMessage(DEVICE_INDEX, REQUEST_TYPE, __VA_ARGS__, 0) #define SendLocalDeviceGroupMessage(REQUEST_TYPE, ...) _SendDeviceGroupMessage(0, REQUEST_TYPE, __VA_ARGS__, 0) diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index 8a55c0f8d..09d6a5bd8 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -20,6 +20,8 @@ #ifndef _TASMOTA_TEMPLATE_H_ #define _TASMOTA_TEMPLATE_H_ +#ifdef ESP8266 + // User selectable GPIO functionality // ATTENTION: Only add at the end of this list just before GPIO_SENSOR_END // Then add the same name(s) in a nice location in array kGpioNiceList @@ -704,31 +706,14 @@ const char kAdc0Names[] PROGMEM = /********************************************************************************************/ -#ifdef ESP8266 - #define MAX_GPIO_PIN 17 // Number of supported GPIO #define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 13 // MAX_GPIO_PIN - MIN_FLASH_PINS #define ADC0_PIN 17 // Pin number of ADC0 #define WEMOS_MODULE 17 // Wemos module const char PINS_WEMOS[] PROGMEM = "D3TXD4RXD2D1flashcFLFLolD6D7D5D8D0A0"; -#else // ESP32 - -// esp32 has more pins -#define USER_MODULE 255 -#define MAX_GPIO_PIN 40 // Number of supported GPIO -#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) -#define ADC0_PIN 33 // Pin number of ADC0 -#define WEMOS_MODULE 0 // Wemos module - -// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 -const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; - -#endif // ESP8266 - -#define MAX_USER_PINS MAX_GPIO_PIN-MIN_FLASH_PINS - /********************************************************************************************/ typedef struct MYIO { @@ -763,10 +748,8 @@ typedef struct MYTMPLT { } mytmplt; /********************************************************************************************/ - -#ifdef ESP8266 - // Supported hardware modules + enum SupportedModules { SONOFF_BASIC, SONOFF_RF, SONOFF_SV, SONOFF_TH, SONOFF_DUAL, SONOFF_POW, SONOFF_4CH, SONOFF_S2X, SLAMPHER, SONOFF_TOUCH, SONOFF_LED, CH1, CH4, MOTOR, ELECTRODRAGON, EXS_RELAY, WION, WEMOS, SONOFF_DEV, H801, diff --git a/tasmota/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h index 831532a59..2a356a46a 100644 --- a/tasmota/tasmota_template_ESP32.h +++ b/tasmota/tasmota_template_ESP32.h @@ -42,12 +42,678 @@ #undef USE_TUYA_MCU #undef USE_PS_16_DZ +enum UserSelectablePins { + GPIO_NONE, // Not used + GPIO_KEY1, // 4 x Button usually connected to GPIO0 + GPIO_KEY1_NP, + GPIO_KEY1_INV, + GPIO_KEY1_INV_NP, + GPIO_SWT1, // 8 x User connected external switches + GPIO_SWT1_NP, + GPIO_REL1, // 8 x Relays + GPIO_REL1_INV, + GPIO_LED1, // 4 x Leds + GPIO_LED1_INV, + GPIO_CNTR1, // 4 x Counter + GPIO_CNTR1_NP, + GPIO_PWM1, // 5 x PWM + GPIO_PWM1_INV, + GPIO_BUZZER, // Buzzer + GPIO_BUZZER_INV, // Inverted buzzer + GPIO_LEDLNK, // Link led + GPIO_LEDLNK_INV, // Inverted link led + GPIO_I2C_SCL, // I2C SCL + GPIO_I2C_SDA, // I2C SDA + GPIO_SPI_MISO, // SPI MISO + GPIO_SPI_MOSI, // SPI MOSI + GPIO_SPI_CLK, // SPI Clk + GPIO_SPI_CS, // SPI Chip Select + GPIO_SPI_DC, // SPI Data Direction + GPIO_SSPI_MISO, // Software SPI Master Input Slave Output + GPIO_SSPI_MOSI, // Software SPI Master Output Slave Input + GPIO_SSPI_SCLK, // Software SPI Serial Clock + GPIO_SSPI_CS, // Software SPI Chip Select + GPIO_SSPI_DC, // Software SPI Data or Command + GPIO_BACKLIGHT, // Display backlight control + GPIO_OLED_RESET, // OLED Display Reset + GPIO_IRSEND, // IR remote + GPIO_IRRECV, // IR receiver + GPIO_RFSEND, // RF transmitter + GPIO_RFRECV, // RF receiver + GPIO_DHT11, // DHT11 + GPIO_DHT22, // DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_SI7021, // iTead SI7021 + GPIO_DHT11_OUT, // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 + GPIO_DSB, // Single wire DS18B20 or DS18S20 + GPIO_DSB_OUT, // Pseudo Single wire DS18B20 or DS18S20 + GPIO_WS2812, // WS2812 Led string + GPIO_MHZ_TXD, // MH-Z19 Serial interface + GPIO_MHZ_RXD, // MH-Z19 Serial interface + GPIO_PZEM0XX_TX, // PZEM0XX Serial interface + GPIO_PZEM004_RX, // PZEM004T Serial interface + GPIO_PZEM016_RX, // PZEM-014,016 Serial Modbus interface + GPIO_PZEM017_RX, // PZEM-003,017 Serial Modbus interface + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface + GPIO_PMS5003_TX, // Plantower PMS5003 Serial interface + GPIO_PMS5003_RX, // Plantower PMS5003 Serial interface + GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface + GPIO_SDS0X1_RX, // Nova Fitness SDS011 Serial interface + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface + GPIO_SR04_TRIG, // SR04 Trigger/TX pin + GPIO_SR04_ECHO, // SR04 Echo/RX pin + GPIO_SDM120_TX, // SDM120 Serial interface + GPIO_SDM120_RX, // SDM120 Serial interface + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface + GPIO_TM16CLK, // TM1638 Clock + GPIO_TM16DIO, // TM1638 Data I/O + GPIO_TM16STB, // TM1638 Strobe + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player + GPIO_HX711_SCK, // HX711 Load Cell clock + GPIO_HX711_DAT, // HX711 Load Cell data + GPIO_TX2X_TXD_BLACK, // TX20/TX23 Transmission Pin + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface + GPIO_MGC3130_XFER, // MGC3130 Transfer + GPIO_MGC3130_RESET, // MGC3130 Reset + GPIO_RF_SENSOR, // Rf receiver with sensor decoding + GPIO_AZ_TXD, // AZ-Instrument 7798 Serial interface + GPIO_AZ_RXD, // AZ-Instrument 7798 Serial interface + GPIO_MAX31855CS, // MAX31855 Serial interface + GPIO_MAX31855CLK, // MAX31855 Serial interface + GPIO_MAX31855DO, // MAX31855 Serial interface + GPIO_NRG_SEL, // HLW8012/HLJ-01 Sel output (1 = Voltage) + GPIO_NRG_SEL_INV, // HLW8012/HLJ-01 Sel output (0 = Voltage) + GPIO_NRG_CF1, // HLW8012/HLJ-01 CF1 voltage / current + GPIO_HLW_CF, // HLW8012 CF power + GPIO_HJL_CF, // HJL-01/BL0937 CF power + GPIO_MCP39F5_TX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RX, // MCP39F501 Serial interface (Shelly2) + GPIO_MCP39F5_RST, // MCP39F501 Reset (Shelly2) + GPIO_PN532_TXD, // PN532 NFC Serial Tx + GPIO_PN532_RXD, // PN532 NFC Serial Rx + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT + GPIO_DI, // my92x1 PWM input + GPIO_DCKI, // my92x1 CLK input + GPIO_CSE7766_TX, // CSE7766 Serial interface (S31 and Pow R2) - Not used anymore 20200121 + GPIO_CSE7766_RX, // CSE7766 Serial interface (S31 and Pow R2) + GPIO_ARIRFRCV, // AriLux RF Receive input + GPIO_TXD, // Serial interface + GPIO_RXD, // Serial interface + GPIO_ROT1A, // Rotary switch1 A Pin + GPIO_ROT1B, // Rotary switch1 B Pin + GPIO_ROT2A, // Rotary switch2 A Pin + GPIO_ROT2B, // Rotary switch2 B Pin + GPIO_HRE_CLOCK, // Clock/Power line for HR-E Water Meter + GPIO_HRE_DATA, // Data line for HR-E Water Meter + GPIO_ADE7953_IRQ, // ADE7953 IRQ + GPIO_ARIRFSEL, // Arilux RF Receive input selected + GPIO_SOLAXX1_TX, // Solax Inverter tx pin + GPIO_SOLAXX1_RX, // Solax Inverter rx pin + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface + GPIO_RDM6300_RX, // RDM6300 RX + GPIO_IBEACON_TX, // HM17 IBEACON TX + GPIO_IBEACON_RX, // HM17 IBEACON RX + GPIO_A4988_DIR, // A4988 direction pin + GPIO_A4988_STP, // A4988 step pin + GPIO_A4988_ENA, // A4988 enabled pin + GPIO_A4988_MS1, // A4988 microstep pin1 + GPIO_A4988_MS2, // A4988 microstep pin2 + GPIO_A4988_MS3, // A4988 microstep pin3 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface + GPIO_DDSU666_TX, // DDSU666 Serial interface + GPIO_DDSU666_RX, // DDSU666 Serial interface + GPIO_SM2135_CLK, // SM2135 Clk + GPIO_SM2135_DAT, // SM2135 Dat + GPIO_DEEPSLEEP, // Kill switch for deepsleep + GPIO_EXS_ENABLE, // EXS MCU Enable + GPIO_TASMOTASLAVE_TXD, // Slave TX + GPIO_TASMOTASLAVE_RXD, // Slave RX + GPIO_TASMOTASLAVE_RST, // Slave Reset Pin + GPIO_TASMOTASLAVE_RST_INV, // Slave Reset Inverted + GPIO_HPMA_RX, // Honeywell HPMA115S0 Serial interface + GPIO_HPMA_TX, // Honeywell HPMA115S0 Serial interface + GPIO_GPS_RX, // GPS serial interface + GPIO_GPS_TX, // GPS serial interface + GPIO_HM10_RX, // HM10-BLE-Mijia-bridge serial interface + GPIO_HM10_TX, // HM10-BLE-Mijia-bridge serial interface + GPIO_LE01MR_RX, // F&F LE-01MR energy meter + GPIO_LE01MR_TX, // F&F LE-01MR energy meter + GPIO_CC1101_GDO0, // CC1101 pin for RX + GPIO_CC1101_GDO2, // CC1101 pin for RX + GPIO_HRXL_RX, // Data from MaxBotix HRXL sonar range sensor + GPIO_ELECTRIQ_MOODL_TX, // ElectriQ iQ-wifiMOODL Serial TX + GPIO_AS3935, + ADC0_INPUT, // Analog input + ADC0_TEMP, // Thermistor + ADC0_LIGHT, // Light sensor + ADC0_BUTTON, // Button + ADC0_BUTTON_INV, + ADC0_RANGE, // Range + ADC0_CT_POWER, // Current + // webcam interface + GPIO_WEBCAM_PWDN_GPIO_NUM, + GPIO_WEBCAM_RESET_GPIO_NUM, + GPIO_WEBCAM_XCLK_GPIO_NUM, + GPIO_WEBCAM_SIOD_GPIO_NUM, + GPIO_WEBCAM_SIOC_GPIO_NUM, + GPIO_WEBCAM_Y9_GPIO_NUM, + GPIO_WEBCAM_Y8_GPIO_NUM, + GPIO_WEBCAM_Y7_GPIO_NUM, + GPIO_WEBCAM_Y6_GPIO_NUM, + GPIO_WEBCAM_Y5_GPIO_NUM, + GPIO_WEBCAM_Y4_GPIO_NUM, + GPIO_WEBCAM_Y3_GPIO_NUM, + GPIO_WEBCAM_Y2_GPIO_NUM, + GPIO_WEBCAM_VSYNC_GPIO_NUM, + GPIO_WEBCAM_HREF_GPIO_NUM, + GPIO_WEBCAM_PCLK_GPIO_NUM, + GPIO_WEBCAM_PSCLK_GPIO_NUM, + GPIO_WEBCAM_HSD1_GPIO_NUM, + GPIO_WEBCAM_HSD2_GPIO_NUM, + GPIO_WEBCAM_HSD3_GPIO_NUM, + GPIO_WEBCAM_PSRCS_GPIO_NUM, + GPIO_SENSOR_END }; + +enum ProgramSelectablePins { +// GPIO_FIX_START = 254, + GPIO_FIX_START = 2046, + GPIO_USER, // User configurable needs to be 2047 + GPIO_MAX }; + +// Text in webpage Module Parameters and commands GPIOS and GPIO +const char kSensorNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_BUTTON "|" + D_SENSOR_BUTTON "n|" + D_SENSOR_BUTTON "i|" + D_SENSOR_BUTTON "in|" + D_SENSOR_SWITCH "|" + D_SENSOR_SWITCH "n|" + D_SENSOR_RELAY "|" + D_SENSOR_RELAY "i|" + D_SENSOR_LED "|" + D_SENSOR_LED "i|" + D_SENSOR_COUNTER "|" + D_SENSOR_COUNTER "n|" + D_SENSOR_PWM "|" + D_SENSOR_PWM "i|" + D_SENSOR_BUZZER "|" + D_SENSOR_BUZZER "i|" + D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|" + D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_SPI_MISO "|" D_SENSOR_SPI_MOSI "|" D_SENSOR_SPI_CLK "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" + D_SENSOR_SSPI_MISO "|" D_SENSOR_SSPI_MOSI "|" D_SENSOR_SSPI_SCLK "|" D_SENSOR_SSPI_CS "|" D_SENSOR_SSPI_DC "|" + D_SENSOR_BACKLIGHT "|" D_SENSOR_OLED_RESET "|" + D_SENSOR_IRSEND "|" D_SENSOR_IRRECV "|" + D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" D_SENSOR_DHT11 "o|" + D_SENSOR_DS18X20 "|" D_SENSOR_DS18X20 "o|" + D_SENSOR_WS2812 "|" + D_SENSOR_MHZ_TX "|" D_SENSOR_MHZ_RX "|" + D_SENSOR_PZEM0XX_TX "|" D_SENSOR_PZEM004_RX "|" D_SENSOR_PZEM016_RX "|" D_SENSOR_PZEM017_RX "|" + D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" + D_SENSOR_PMS5003_TX "|" D_SENSOR_PMS5003_RX "|" + D_SENSOR_SDS0X1_TX "|" D_SENSOR_SDS0X1_RX "|" + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX "|" + D_SENSOR_SR04_TRIG "|" D_SENSOR_SR04_ECHO "|" + D_SENSOR_SDM120_TX "|" D_SENSOR_SDM120_RX "|" + D_SENSOR_SDM630_TX "|" D_SENSOR_SDM630_RX "|" + D_SENSOR_TM1638_CLK "|" D_SENSOR_TM1638_DIO "|" D_SENSOR_TM1638_STB "|" + D_SENSOR_DFR562 "|" + D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" + D_SENSOR_TX2X_TX "|" + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" + D_SENSOR_MGC3130_XFER "|" D_SENSOR_MGC3130_RESET "|" + D_SENSOR_RF_SENSOR "|" + D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" + D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" + D_SENSOR_NRG_SEL "|" D_SENSOR_NRG_SEL "i|" D_SENSOR_NRG_CF1 "|" D_SENSOR_HLW_CF "|" D_SENSOR_HJL_CF "|" + D_SENSOR_MCP39F5_TX "|" D_SENSOR_MCP39F5_RX "|" D_SENSOR_MCP39F5_RST "|" + D_SENSOR_PN532_TX "|" D_SENSOR_PN532_RX "|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_POWER "|" + D_SENSOR_MY92X1_DI "|" D_SENSOR_MY92X1_DCKI "|" + D_SENSOR_CSE7766_TX "|" D_SENSOR_CSE7766_RX "|" + D_SENSOR_ARIRFRCV "|" + D_SENSOR_TXD "|" D_SENSOR_RXD "|" + D_SENSOR_ROTARY "1a|" D_SENSOR_ROTARY "1b|" D_SENSOR_ROTARY "2a|" D_SENSOR_ROTARY "2b|" + D_SENSOR_HRE_CLOCK "|" D_SENSOR_HRE_DATA "|" + D_SENSOR_ADE7953_IRQ "|" + D_SENSOR_ARIRFSEL "|" + D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" + D_SENSOR_RDM6300_RX "|" + D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" + D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" + D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|" + D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|" + D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_ENABLE "|" + D_SENSOR_SLAVE_TX "|" D_SENSOR_SLAVE_RX "|" D_SENSOR_SLAVE_RESET "|" D_SENSOR_SLAVE_RESET "i|" + D_SENSOR_HPMA_RX "|" D_SENSOR_HPMA_TX "|" + D_SENSOR_GPS_RX "|" D_SENSOR_GPS_TX "|" + D_SENSOR_HM10_RX "|" D_SENSOR_HM10_TX "|" + D_SENSOR_LE01MR_RX "|" D_SENSOR_LE01MR_TX "|" + D_SENSOR_CC1101_GDO0 "|" D_SENSOR_CC1101_GDO2 "|" + D_SENSOR_HRXL_RX "|" + D_SENSOR_ELECTRIQ_MOODL "|" + D_SENSOR_AS3935 "|" + D_ANALOG_INPUT "|" + D_TEMPERATURE "|" D_LIGHT "|" + D_SENSOR_BUTTON "|" D_SENSOR_BUTTON "i|" + D_RANGE "|" + D_CT_POWER "|" + D_GPIO_WEBCAM_PWDN_GPIO_NUM "|" + D_GPIO_WEBCAM_RESET_GPIO_NUM "|" + D_GPIO_WEBCAM_XCLK_GPIO_NUM "|" + D_GPIO_WEBCAM_SIOD_GPIO_NUM "|" + D_GPIO_WEBCAM_SIOC_GPIO_NUM "|" + D_GPIO_WEBCAM_Y9_GPIO_NUM "|" + D_GPIO_WEBCAM_Y8_GPIO_NUM "|" + D_GPIO_WEBCAM_Y7_GPIO_NUM "|" + D_GPIO_WEBCAM_Y6_GPIO_NUM "|" + D_GPIO_WEBCAM_Y5_GPIO_NUM "|" + D_GPIO_WEBCAM_Y4_GPIO_NUM "|" + D_GPIO_WEBCAM_Y3_GPIO_NUM "|" + D_GPIO_WEBCAM_Y2_GPIO_NUM "|" + D_GPIO_WEBCAM_VSYNC_GPIO_NUM "|" + D_GPIO_WEBCAM_HREF_GPIO_NUM "|" + D_GPIO_WEBCAM_PCLK_GPIO_NUM "|" + D_GPIO_WEBCAM_PSCLK_GPIO_NUM "|" + D_GPIO_WEBCAM_HSD1_GPIO_NUM "|" + D_GPIO_WEBCAM_HSD2_GPIO_NUM "|" + D_GPIO_WEBCAM_HSD3_GPIO_NUM "|" + D_GPIO_WEBCAM_PSRCS_GPIO_NUM + ; + +const char kSensorNamesFixed[] PROGMEM = + D_SENSOR_USER; + +const uint16_t kGpioNiceList[] PROGMEM = { + GPIO_NONE, // Not used + AGPIO(GPIO_KEY1) + MAX_KEYS, // Buttons + AGPIO(GPIO_KEY1_NP) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV) + MAX_KEYS, + AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS, + AGPIO(GPIO_SWT1) + MAX_SWITCHES, // User connected external switches + AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES, + AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays + AGPIO(GPIO_REL1_INV) + MAX_RELAYS, + AGPIO(GPIO_LED1) + MAX_LEDS, // Leds + AGPIO(GPIO_LED1_INV) + MAX_LEDS, +#ifdef USE_COUNTER + AGPIO(GPIO_CNTR1) + MAX_COUNTERS, // Counters + AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS, +#endif + AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White + AGPIO(GPIO_PWM1_INV) + MAX_PWMS, +#ifdef USE_BUZZER + AGPIO(GPIO_BUZZER), // Buzzer + AGPIO(GPIO_BUZZER_INV), // Inverted buzzer +#endif + AGPIO(GPIO_LEDLNK), // Link led + AGPIO(GPIO_LEDLNK_INV), // Inverted link led +#ifdef USE_I2C + AGPIO(GPIO_I2C_SCL), // I2C SCL + AGPIO(GPIO_I2C_SDA), // I2C SDA +#endif +#ifdef USE_SPI + AGPIO(GPIO_SPI_MISO), // SPI MISO + AGPIO(GPIO_SPI_MOSI), // SPI MOSI + AGPIO(GPIO_SPI_CLK), // SPI Clk + AGPIO(GPIO_SPI_CS), // SPI Chip Select + AGPIO(GPIO_SPI_DC), // SPI Data Direction + AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Slave Output + AGPIO(GPIO_SSPI_MOSI), // Software SPI Master Output Slave Input + AGPIO(GPIO_SSPI_SCLK), // Software SPI Serial Clock + AGPIO(GPIO_SSPI_CS), // Software SPI Chip Select + AGPIO(GPIO_SSPI_DC), // Software SPI Data or Command +#endif +#ifdef USE_DISPLAY + AGPIO(GPIO_BACKLIGHT), // Display backlight control + AGPIO(GPIO_OLED_RESET), // OLED Display Reset +#endif + + AGPIO(GPIO_TXD), // Serial interface + AGPIO(GPIO_RXD), // Serial interface + +#ifdef USE_DHT + AGPIO(GPIO_DHT11), // DHT11 + AGPIO(GPIO_DHT22), // DHT21, DHT22, AM2301, AM2302, AM2321 + AGPIO(GPIO_SI7021), // iTead SI7021 + AGPIO(GPIO_DHT11_OUT), // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321 +#endif +#ifdef USE_DS18x20 + AGPIO(GPIO_DSB), // Single wire DS18B20 or DS18S20 + AGPIO(GPIO_DSB_OUT), // Pseudo Single wire DS18B20 or DS18S20 +#endif + +// Light +#ifdef USE_LIGHT +#ifdef USE_WS2812 + AGPIO(GPIO_WS2812), // WS2812 Led string +#endif +#ifdef USE_ARILUX_RF + AGPIO(GPIO_ARIRFRCV), // AriLux RF Receive input + AGPIO(GPIO_ARIRFSEL), // Arilux RF Receive input selected +#endif +#ifdef USE_MY92X1 + AGPIO(GPIO_DI), // my92x1 PWM input + AGPIO(GPIO_DCKI), // my92x1 CLK input +#endif // USE_MY92X1 +#ifdef USE_SM16716 + AGPIO(GPIO_SM16716_CLK), // SM16716 CLOCK + AGPIO(GPIO_SM16716_DAT), // SM16716 DATA + AGPIO(GPIO_SM16716_SEL), // SM16716 SELECT +#endif // USE_SM16716 +#ifdef USE_SM2135 + AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK + AGPIO(GPIO_SM2135_DAT), // SM2135 DATA +#endif // USE_SM2135 +#ifdef USE_TUYA_MCU + AGPIO(GPIO_TUYA_TX), // Tuya Serial interface + AGPIO(GPIO_TUYA_RX), // Tuya Serial interface +#endif +#ifdef USE_EXS_DIMMER + AGPIO(GPIO_EXS_ENABLE), // EXS MCU Enable +#endif +#ifdef USE_ELECTRIQ_MOODL + AGPIO(GPIO_ELECTRIQ_MOODL_TX), +#endif +#endif // USE_LIGHT + +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + AGPIO(GPIO_IRSEND), // IR remote +#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) + AGPIO(GPIO_IRRECV), // IR receiver +#endif +#endif + +#ifdef USE_RC_SWITCH + AGPIO(GPIO_RFSEND), // RF transmitter + AGPIO(GPIO_RFRECV), // RF receiver +#endif +#ifdef USE_RF_SENSOR + AGPIO(GPIO_RF_SENSOR), // Rf receiver with sensor decoding +#endif +#ifdef USE_SR04 + AGPIO(GPIO_SR04_TRIG), // SR04 Tri/TXgger pin + AGPIO(GPIO_SR04_ECHO), // SR04 Ech/RXo pin +#endif +#ifdef USE_TM1638 + AGPIO(GPIO_TM16CLK), // TM1638 Clock + AGPIO(GPIO_TM16DIO), // TM1638 Data I/O + AGPIO(GPIO_TM16STB), // TM1638 Strobe +#endif +#ifdef USE_HX711 + AGPIO(GPIO_HX711_SCK), // HX711 Load Cell clock + AGPIO(GPIO_HX711_DAT), // HX711 Load Cell data +#endif + +// Energy sensors +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + AGPIO(GPIO_NRG_SEL), // HLW8012/HLJ-01 Sel output (1 = Voltage) + AGPIO(GPIO_NRG_SEL_INV), // HLW8012/HLJ-01 Sel output (0 = Voltage) + AGPIO(GPIO_NRG_CF1), // HLW8012/HLJ-01 CF1 voltage / current + AGPIO(GPIO_HLW_CF), // HLW8012 CF power + AGPIO(GPIO_HJL_CF), // HJL-01/BL0937 CF power +#endif +#if defined(USE_I2C) && defined(USE_ADE7953) + AGPIO(GPIO_ADE7953_IRQ), // ADE7953 IRQ +#endif +#ifdef USE_CSE7766 + AGPIO(GPIO_CSE7766_TX), // CSE7766 Serial interface (S31 and Pow R2) + AGPIO(GPIO_CSE7766_RX), // CSE7766 Serial interface (S31 and Pow R2) +#endif +#ifdef USE_MCP39F501 + AGPIO(GPIO_MCP39F5_TX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RX), // MCP39F501 Serial interface (Shelly2) + AGPIO(GPIO_MCP39F5_RST), // MCP39F501 Reset (Shelly2) +#endif +#if defined(USE_PZEM004T) || defined(USE_PZEM_AC) || defined(USE_PZEM_DC) + AGPIO(GPIO_PZEM0XX_TX), // PZEM0XX Serial interface +#endif +#ifdef USE_PZEM004T + AGPIO(GPIO_PZEM004_RX), // PZEM004T Serial interface +#endif +#ifdef USE_PZEM_AC + AGPIO(GPIO_PZEM016_RX), // PZEM-014,016 Serial Modbus interface +#endif +#ifdef USE_PZEM_DC + AGPIO(GPIO_PZEM017_RX), // PZEM-003,017 Serial Modbus interface +#endif +#ifdef USE_SDM120 + AGPIO(GPIO_SDM120_TX), // SDM120 Serial interface + AGPIO(GPIO_SDM120_RX), // SDM120 Serial interface +#endif +#ifdef USE_SDM630 + AGPIO(GPIO_SDM630_TX), // SDM630 Serial interface + AGPIO(GPIO_SDM630_RX), // SDM630 Serial interface +#endif +#ifdef USE_DDS2382 + AGPIO(GPIO_DDS2382_TX), // DDS2382 Serial interface + AGPIO(GPIO_DDS2382_RX), // DDS2382 Serial interface +#endif +#ifdef USE_DDSU666 + AGPIO(GPIO_DDSU666_TX), // DDSU666 Serial interface + AGPIO(GPIO_DDSU666_RX), // DDSU666 Serial interface +#endif // USE_DDSU666 +#ifdef USE_SOLAX_X1 + AGPIO(GPIO_SOLAXX1_TX), // Solax Inverter tx pin + AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin +#endif // USE_SOLAX_X1 +#ifdef USE_LE01MR + AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin + AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin +#endif // IFDEF:USE_LE01MR +#endif // USE_ENERGY_SENSOR + +// Serial +#ifdef USE_SERIAL_BRIDGE + AGPIO(GPIO_SBR_TX), // Serial Bridge Serial interface + AGPIO(GPIO_SBR_RX), // Serial Bridge Serial interface +#endif +#ifdef USE_ZIGBEE + AGPIO(GPIO_ZIGBEE_TX), // Zigbee Serial interface + AGPIO(GPIO_ZIGBEE_RX), // Zigbee Serial interface +#endif +#ifdef USE_MHZ19 + AGPIO(GPIO_MHZ_TXD), // MH-Z19 Serial interface + AGPIO(GPIO_MHZ_RXD), // MH-Z19 Serial interface +#endif +#ifdef USE_SENSEAIR + AGPIO(GPIO_SAIR_TX), // SenseAir Serial interface + AGPIO(GPIO_SAIR_RX), // SenseAir Serial interface +#endif +#ifdef USE_NOVA_SDS + AGPIO(GPIO_SDS0X1_TX), // Nova Fitness SDS011 Serial interface + AGPIO(GPIO_SDS0X1_RX), // Nova Fitness SDS011 Serial interface +#endif +#ifdef USE_HPMA + AGPIO(GPIO_HPMA_TX), // Honeywell HPMA115S0 Serial interface + AGPIO(GPIO_HPMA_RX), // Honeywell HPMA115S0 Serial interface +#endif +#ifdef USE_PMS5003 + AGPIO(GPIO_PMS5003_TX), // Plantower PMS5003 Serial interface + AGPIO(GPIO_PMS5003_RX), // Plantower PMS5003 Serial interface +#endif +#if defined(USE_TX20_WIND_SENSOR) || defined(USE_TX23_WIND_SENSOR) + AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin +#endif +#ifdef USE_MP3_PLAYER + AGPIO(GPIO_MP3_DFR562), // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface +#endif +#ifdef USE_AZ7798 + AGPIO(GPIO_AZ_TXD), // AZ-Instrument 7798 CO2 datalogger Serial interface + AGPIO(GPIO_AZ_RXD), // AZ-Instrument 7798 CO2 datalogger Serial interface +#endif +#ifdef USE_PN532_HSU + AGPIO(GPIO_PN532_TXD), // PN532 HSU Tx + AGPIO(GPIO_PN532_RXD), // PN532 HSU Rx +#endif +#ifdef USE_TASMOTA_SLAVE + AGPIO(GPIO_TASMOTASLAVE_TXD), // Tasmota Slave TX + AGPIO(GPIO_TASMOTASLAVE_RXD), // Tasmota Slave RX + AGPIO(GPIO_TASMOTASLAVE_RST), // Tasmota Slave Reset + AGPIO(GPIO_TASMOTASLAVE_RST_INV), // Tasmota Slave Reset Inverted +#endif +#ifdef USE_RDM6300 + AGPIO(GPIO_RDM6300_RX), +#endif +#ifdef USE_IBEACON + AGPIO(GPIO_IBEACON_RX), + AGPIO(GPIO_IBEACON_TX), +#endif +#ifdef USE_GPS + AGPIO(GPIO_GPS_RX), // GPS serial interface + AGPIO(GPIO_GPS_TX), // GPS serial interface +#endif +#ifdef USE_HM10 + AGPIO(GPIO_HM10_RX), // GPS serial interface + AGPIO(GPIO_HM10_TX), // GPS serial interface +#endif + +#ifdef USE_MGC3130 + AGPIO(GPIO_MGC3130_XFER), + AGPIO(GPIO_MGC3130_RESET), +#endif +#ifdef USE_MAX31855 + AGPIO(GPIO_MAX31855CS), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855CLK), // MAX31855 Serial interface + AGPIO(GPIO_MAX31855DO), // MAX31855 Serial interface +#endif +#ifdef ROTARY_V1 + AGPIO(GPIO_ROT1A), // Rotary switch1 A Pin + AGPIO(GPIO_ROT1B), // Rotary switch1 B Pin + AGPIO(GPIO_ROT2A), // Rotary switch2 A Pin + AGPIO(GPIO_ROT2B), // Rotary switch2 B Pin +#endif +#ifdef USE_HRE + AGPIO(GPIO_HRE_CLOCK), + AGPIO(GPIO_HRE_DATA), +#endif +#ifdef USE_A4988_STEPPER + AGPIO(GPIO_A4988_DIR), // A4988 direction pin + AGPIO(GPIO_A4988_STP), // A4988 step pin + // folowing are not mandatory + AGPIO(GPIO_A4988_ENA), // A4988 enabled pin + AGPIO(GPIO_A4988_MS1), // A4988 microstep pin1 + AGPIO(GPIO_A4988_MS2), // A4988 microstep pin2 + AGPIO(GPIO_A4988_MS3), // A4988 microstep pin3 +#endif +#ifdef USE_DEEPSLEEP + AGPIO(GPIO_DEEPSLEEP), +#endif +#ifdef USE_KEELOQ + AGPIO(GPIO_CC1101_GDO0), // CC1101 pin for RX + AGPIO(GPIO_CC1101_GDO2), // CC1101 pin for RX +#endif +#ifdef USE_HRXL + AGPIO(GPIO_HRXL_RX), +#endif +#ifdef USE_AS3935 + AGPIO(GPIO_AS3935), +#endif +/* +#ifndef USE_ADC_VCC + AGPIO(ADC0_INPUT), // Analog input + AGPIO(ADC0_TEMP), // Thermistor + AGPIO(ADC0_LIGHT), // Light sensor + AGPIO(ADC0_BUTTON), // Button + AGPIO(ADC0_BUTTON_INV), + AGPIO(ADC0_RANGE), // Range + AGPIO(ADC0_CT_POWER), // Current +#endif +*/ +#if defined(ESP32) && defined(USE_WEBCAM) +AGPIO(GPIO_WEBCAM_PWDN_GPIO_NUM), +AGPIO(GPIO_WEBCAM_RESET_GPIO_NUM), +AGPIO(GPIO_WEBCAM_XCLK_GPIO_NUM), +AGPIO(GPIO_WEBCAM_SIOD_GPIO_NUM), +AGPIO(GPIO_WEBCAM_SIOC_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y9_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y8_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y7_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y6_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y5_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y4_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y3_GPIO_NUM), +AGPIO(GPIO_WEBCAM_Y2_GPIO_NUM), +AGPIO(GPIO_WEBCAM_VSYNC_GPIO_NUM), +AGPIO(GPIO_WEBCAM_HREF_GPIO_NUM), +AGPIO(GPIO_WEBCAM_PCLK_GPIO_NUM), +AGPIO(GPIO_WEBCAM_PSCLK_GPIO_NUM), +AGPIO(GPIO_WEBCAM_HSD1_GPIO_NUM), +AGPIO(GPIO_WEBCAM_HSD2_GPIO_NUM), +AGPIO(GPIO_WEBCAM_HSD3_GPIO_NUM), +AGPIO(GPIO_WEBCAM_PSRCS_GPIO_NUM), +#endif +}; + +//******************************************************************************************** + +#define MAX_GPIO_PIN 40 // Number of supported GPIO +#define MIN_FLASH_PINS 4 // Number of flash chip pins unusable for configuration (GPIO6, 7, 8 and 11) +#define MAX_USER_PINS 36 // MAX_GPIO_PIN - MIN_FLASH_PINS +#define WEMOS_MODULE 0 // Wemos module + +// 0 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839 +const char PINS_WEMOS[] PROGMEM = "IOTXIORXIOIOflashcFLFLolIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIOA6A7A0IoIoA3"; + +//******************************************************************************************** + +typedef struct MYIO { + uint16_t io[MAX_GPIO_PIN]; +} myio; // 40 * 2 = 80 bytes + +typedef struct MYCFGIO { + uint16_t io[MAX_USER_PINS]; +} mycfgio; // 36 * 2 = 72 bytes + +#define GPIO_FLAG_USED 0 // Currently no flags used + +typedef union { + uint16_t data; + struct { + uint16_t spare00 : 1; + uint16_t spare01 : 1; + uint16_t spare02 : 1; + uint16_t spare03 : 1; + uint16_t spare04 : 1; + uint16_t spare05 : 1; + uint16_t spare06 : 1; + uint16_t spare07 : 1; + uint16_t spare08 : 1; + uint16_t spare09 : 1; + uint16_t spare10 : 1; + uint16_t spare11 : 1; + uint16_t spare12 : 1; + uint16_t spare13 : 1; + uint16_t spare14 : 1; + uint16_t spare15 : 1; + }; +} gpio_flag; // 2 bytes + +typedef struct MYTMPLT { + mycfgio gp; // 72 bytes + gpio_flag flag; // 2 bytes +} mytmplt; // 74 bytes + /********************************************************************************************/ // Supported hardware modules enum SupportedModules { WEMOS, ESP32_CAM_AITHINKER, MAXMODULE}; +#define USER_MODULE 255 + const char kModuleNames[] PROGMEM = "ESP32-DevKit|ESP32 Cam AiThinker"; @@ -58,48 +724,48 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { }; const mytmplt kModules PROGMEM = -{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) - GPIO_USER, //0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK - GPIO_USER, //1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 - GPIO_USER, //2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 - GPIO_USER, //3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 - GPIO_USER, //4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER - GPIO_USER, //5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK - //6 IO GPIO6, Flash CLK - //7 IO GPIO7, Flash D0 - //8 IO GPIO8, Flash D1 - GPIO_USER, //9 IO GPIO9, Flash D2, U1RXD - GPIO_USER, //10 IO GPIO10, Flash D3, U1TXD - //11 IO GPIO11, Flash CMD - GPIO_USER, //12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) - GPIO_USER, //13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER - GPIO_USER, //14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 - GPIO_USER, //15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) - GPIO_USER, //16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT - GPIO_USER, //17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 - GPIO_USER, //18 IO GPIO18, VSPICLK, HS1_DATA7 - GPIO_USER, //19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 - 0, //20 - GPIO_USER, //21 IO GPIO21, VSPIHD, EMAC_TX_EN - GPIO_USER, //22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 - GPIO_USER, //23 IO GPIO23, VSPID, HS1_STROBE - 0, //24 - GPIO_USER, //25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 - GPIO_USER, //26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 - GPIO_USER, //27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV - 0, //28 - 0, //29 - 0, //30 - 0, //31 - GPIO_USER, //32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 - GPIO_USER, //33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 - GPIO_USER, //34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 - GPIO_USER, //35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 - GPIO_USER, //36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 - 0, //37 NO PULLUP - 0, //38 NO PULLUP - GPIO_USER, //39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 - 0 // Flag +{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32) + AGPIO(GPIO_USER), // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK + AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2 + AGPIO(GPIO_USER), // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0 + AGPIO(GPIO_USER), // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2 + AGPIO(GPIO_USER), // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER + AGPIO(GPIO_USER), // 5 IO GPIO5, VSPICS0, HS1_DATA6, EMAC_RX_CLK + // 6 IO GPIO6, Flash CLK + // 7 IO GPIO7, Flash D0 + // 8 IO GPIO8, Flash D1 + AGPIO(GPIO_USER), // 9 IO GPIO9, Flash D2, U1RXD + AGPIO(GPIO_USER), // 10 IO GPIO10, Flash D3, U1TXD + // 11 IO GPIO11, Flash CMD + AGPIO(GPIO_USER), // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.) + AGPIO(GPIO_USER), // 13 IO GPIO13, ADC2_CH4, TOUCH4, RTC_GPIO14, MTCK, HSPID, HS2_DATA3, SD_DATA3, EMAC_RX_ER + AGPIO(GPIO_USER), // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2 + AGPIO(GPIO_USER), // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.) + AGPIO(GPIO_USER), // 16 IO GPIO16, HS1_DATA4, U2RXD, EMAC_CLK_OUT + AGPIO(GPIO_USER), // 17 IO GPIO17, HS1_DATA5, U2TXD, EMAC_CLK_OUT_180 + AGPIO(GPIO_USER), // 18 IO GPIO18, VSPICLK, HS1_DATA7 + AGPIO(GPIO_USER), // 19 IO GPIO19, VSPIQ, U0CTS, EMAC_TXD0 + 0, // 20 + AGPIO(GPIO_USER), // 21 IO GPIO21, VSPIHD, EMAC_TX_EN + AGPIO(GPIO_USER), // 22 IO LED GPIO22, VSPIWP, U0RTS, EMAC_TXD1 + AGPIO(GPIO_USER), // 23 IO GPIO23, VSPID, HS1_STROBE + 0, // 24 + AGPIO(GPIO_USER), // 25 IO GPIO25, DAC_1, ADC2_CH8, RTC_GPIO6, EMAC_RXD0 + AGPIO(GPIO_USER), // 26 IO GPIO26, DAC_2, ADC2_CH9, RTC_GPIO7, EMAC_RXD1 + AGPIO(GPIO_USER), // 27 IO GPIO27, ADC2_CH7, TOUCH7, RTC_GPIO17, EMAC_RX_DV + 0, // 28 + 0, // 29 + 0, // 30 + 0, // 31 + AGPIO(GPIO_USER), // 32 IO GPIO32, XTAL_32K_P (32.768 kHz crystal oscillator input), ADC1_CH4, TOUCH9, RTC_GPIO9 + AGPIO(GPIO_USER), // 33 IO GPIO33, XTAL_32K_N (32.768 kHz crystal oscillator output), ADC1_CH5, TOUCH8, RTC_GPIO8 + AGPIO(GPIO_USER), // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4 + AGPIO(GPIO_USER), // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5 + AGPIO(GPIO_USER), // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0 + 0, // 37 NO PULLUP + 0, // 38 NO PULLUP + AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3 + 0 // Flag }; #endif // ESP32 diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h index c753484d1..610798621 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/tasmota_version.h @@ -20,7 +20,7 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x08020005; +const uint32_t VERSION = 0x08020006; // Lowest compatible version const uint32_t VERSION_COMPATIBLE = 0x07010006; diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 61f98e649..d22b07b5c 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -231,12 +231,38 @@ const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM = "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM = +#ifdef ESP8266 "var os;" "function sk(s,g){" // s = value, g = id and name "var o=os.replace(/}2/g,\"