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 3cfc54994..4d943a052 100644 --- a/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -28,6 +28,9 @@ #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}; @@ -44,8 +47,8 @@ inline uint32_t pin2chan(uint32_t pin) { inline void analogWrite(uint8_t pin, int val) { uint32_t channel=pin2chan(pin); - ledcWrite(channel,val); - Serial.printf("write %d - %d\n",channel,val); + ledcWrite(channel+PWM_CHANNEL_OFFSET,val); + //Serial.printf("write %d - %d\n",channel,val); } inline void analogWriteFreq(uint32_t freq) @@ -57,8 +60,8 @@ inline void analogWriteRange(uint32_t range) inline void analogAttach(uint32_t pin, uint32_t channel) { pwm_channel[channel&7]=pin; - ledcAttachPin(pin,channel); - Serial.printf("attach %d - %d\n",channel,pin); + ledcAttachPin(pin,channel+PWM_CHANNEL_OFFSET); + //Serial.printf("attach %d - %d\n",channel,pin); } inline uint32_t pow2(uint32_t x) { @@ -74,7 +77,7 @@ 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,freq,range); + ledcSetup(cnt+PWM_CHANNEL_OFFSET,freq,range); } } Serial.printf("freq - range %d - %d\n",freq,range); diff --git a/tasmota/language/bg-BG.h b/tasmota/language/bg-BG.h index b0795b58b..396720703 100644 --- a/tasmota/language/bg-BG.h +++ b/tasmota/language/bg-BG.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 15d2cead2..a4dd20a8e 100644 --- a/tasmota/language/cs-CZ.h +++ b/tasmota/language/cs-CZ.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 bafccdca0..84a2606a7 100644 --- a/tasmota/language/de-DE.h +++ b/tasmota/language/de-DE.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 d5ae776d5..12c61fabe 100644 --- a/tasmota/language/el-GR.h +++ b/tasmota/language/el-GR.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 3d958cae9..eef76a921 100644 --- a/tasmota/language/en-GB.h +++ b/tasmota/language/en-GB.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 95dd0eeb6..db429dbd6 100644 --- a/tasmota/language/es-ES.h +++ b/tasmota/language/es-ES.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 5019f27ee..0427b56f3 100644 --- a/tasmota/language/fr-FR.h +++ b/tasmota/language/fr-FR.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 28e52585a..0d1d29808 100644 --- a/tasmota/language/he-HE.h +++ b/tasmota/language/he-HE.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 c8a05062a..4b7e98794 100644 --- a/tasmota/language/hu-HU.h +++ b/tasmota/language/hu-HU.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 b0477f96a..694c85f3f 100644 --- a/tasmota/language/it-IT.h +++ b/tasmota/language/it-IT.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL - RX" #define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX" #define D_SENSOR_AS3935 "AS3935" +#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 3ef98b5a2..a9eeef2c1 100644 --- a/tasmota/language/ko-KO.h +++ b/tasmota/language/ko-KO.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 7f90a0b79..89346fffa 100644 --- a/tasmota/language/nl-NL.h +++ b/tasmota/language/nl-NL.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 045f3c6d6..eea75e882 100644 --- a/tasmota/language/pl-PL.h +++ b/tasmota/language/pl-PL.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 ce291548b..218c83ccc 100644 --- a/tasmota/language/pt-BR.h +++ b/tasmota/language/pt-BR.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 8716bf471..e2eb4cbee 100644 --- a/tasmota/language/pt-PT.h +++ b/tasmota/language/pt-PT.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 74e583524..7ed26efb3 100644 --- a/tasmota/language/ro-RO.h +++ b/tasmota/language/ro-RO.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 f7a03da44..45af96454 100644 --- a/tasmota/language/ru-RU.h +++ b/tasmota/language/ru-RU.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 de740a482..c6589ddfd 100644 --- a/tasmota/language/sk-SK.h +++ b/tasmota/language/sk-SK.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 c17876a8e..faf8a1000 100644 --- a/tasmota/language/sv-SE.h +++ b/tasmota/language/sv-SE.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 fc2598ec7..7a2407dd1 100644 --- a/tasmota/language/tr-TR.h +++ b/tasmota/language/tr-TR.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 bf955f241..986f3efe3 100644 --- a/tasmota/language/uk-UA.h +++ b/tasmota/language/uk-UA.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 578c7ec2c..e54501afe 100644 --- a/tasmota/language/zh-CN.h +++ b/tasmota/language/zh-CN.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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 26f2e0ef1..e495f858d 100644 --- a/tasmota/language/zh-TW.h +++ b/tasmota/language/zh-TW.h @@ -666,6 +666,27 @@ #define D_SENSOR_HRXL_RX "HRXL Rx" #define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" #define D_SENSOR_AS3935 "AS3935" +#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/tasmota_template_ESP32.h b/tasmota/tasmota_template_ESP32.h index 09a3153d8..2a356a46a 100644 --- a/tasmota/tasmota_template_ESP32.h +++ b/tasmota/tasmota_template_ESP32.h @@ -197,6 +197,28 @@ enum UserSelectablePins { 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 { @@ -287,6 +309,27 @@ const char kSensorNames[] PROGMEM = 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 = @@ -589,6 +632,29 @@ const uint16_t kGpioNiceList[] PROGMEM = { 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 }; //******************************************************************************************** diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 8208606fa..8f01bae2f 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -62,6 +62,7 @@ keywords if then else endif, or, and are better readable for beginners (others m #define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + uint32_t EncodeLightId(uint8_t relay_id); uint32_t DecodeLightId(uint32_t hue_id); @@ -102,14 +103,36 @@ enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; #ifdef USE_SCRIPT_FATFS #include + +//#define USE_MMC + +#ifdef USE_MMC +#include +#undef FS_USED +#define FS_USED SD_MMC +#else #include +#undef FS_USED +#define FS_USED SD +#endif + +#ifndef ESP32 +#undef FILE_WRITE +#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT) +#define FILE_APPEND (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::O_APPEND) +#endif + #ifndef FAT_SCRIPT_SIZE #define FAT_SCRIPT_SIZE 4096 #endif +#ifdef ESP32 +#undef FAT_SCRIPT_NAME +#define FAT_SCRIPT_NAME "/script.txt" +#else +#undef FAT_SCRIPT_NAME #define FAT_SCRIPT_NAME "script.txt" -#if USE_LONG_FILE_NAMES==1 -#warning ("FATFS long filenames not supported"); #endif + #if USE_STANDARD_SPI_LIBRARY==0 #warning ("FATFS standard spi should be used"); #endif @@ -266,7 +289,6 @@ void RulesTeleperiod(void) { #define EEP_SCRIPT_SIZE 4095 #endif - static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); // eeprom.writeBytes(address, length, buffer); #define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); @@ -588,7 +610,11 @@ char *script; #ifdef USE_SCRIPT_FATFS if (!glob_script_mem.script_sd_found) { - if (SD.begin(USE_SCRIPT_FATFS)) { +#ifdef USE_MMC + if (FS_USED.begin()) { +#else + if (FS_USED.begin(USE_SCRIPT_FATFS)) { +#endif glob_script_mem.script_sd_found=1; } else { glob_script_mem.script_sd_found=0; @@ -1166,20 +1192,36 @@ chknext: goto exit; } break; -#ifdef USE_SCRIPT_FATFS case 'f': +#ifdef USE_SCRIPT_FATFS if (!strncmp(vname,"fo(",3)) { lp+=3; char str[SCRIPT_MAXSSIZE]; lp=GetStringResult(lp,OPER_EQU,str,0); while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t mode=fvar; + uint8_t mode=0; + if ((*lp=='r') || (*lp=='w') || (*lp=='a')) { + switch (*lp) { + case 'r': + mode=0; + break; + case 'w': + mode=1; + break; + case 'a': + mode=2; + break; + } + lp++; + } else { + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + mode=fvar; + } fvar=-1; for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; + if (glob_script_mem.file_flags[ind].is_open) { + uint8_t *buff; + float maxps=get_picstore(-1,0); + if (fvar<1 || fvar>maxps) fvar=1; + uint32_t len=get_picstore(fvar-1, &buff); + if (len) { + //glob_script_mem.files[ind].seek(0,SeekEnd); + fvar=glob_script_mem.files[ind].write(buff,len); + } else { + fvar=0; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR("picture save: %d"), len); + } else { + fvar=0; + } + lp++; + len=0; + goto exit; + } +#endif #ifdef USE_SCRIPT_FATFS_EXT if (!strncmp(vname,"fe(",3)) { lp+=3; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); // execute script - File ef=SD.open(str); + File ef=FS_USED.open(str); if (ef) { uint16_t fsiz=ef.size(); if (fsiz<2048) { @@ -1346,7 +1429,7 @@ chknext: lp+=4; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.mkdir(str); + fvar=FS_USED.mkdir(str); lp++; len=0; goto exit; @@ -1355,7 +1438,7 @@ chknext: lp+=4; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.rmdir(str); + fvar=FS_USED.rmdir(str); lp++; len=0; goto exit; @@ -1364,13 +1447,13 @@ chknext: lp+=3; char str[glob_script_mem.max_ssize+1]; lp=GetStringResult(lp,OPER_EQU,str,0); - if (SD.exists(str)) fvar=1; + if (FS_USED.exists(str)) fvar=1; else fvar=0; lp++; len=0; goto exit; } -#endif +#endif // USE_SCRIPT_FATFS_EXT if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { uint8_t lknum=*(lp+2)&3; lp+=4; @@ -1388,9 +1471,16 @@ chknext: //card_init(); goto exit; } - break; - #endif //USE_SCRIPT_FATFS + if (!strncmp(vname,"freq",4)) { +#ifdef ESP32 + fvar=getCpuFrequencyMhz(); +#else + fvar=ESP.getCpuFreqMHz(); +#endif + goto exit; + } + break; case 'g': if (!strncmp(vname,"gtmp",4)) { fvar=global_temperature; @@ -1573,6 +1663,13 @@ chknext: } if (!strncmp(vname,"pn[",3)) { GetNumericResult(vname+3,OPER_EQU,&fvar,0); + fvar=Pin(fvar); + // skip ] bracket + len++; + goto exit; + } + if (!strncmp(vname,"pn[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); // fvar=pin_gpio[(uint8_t)fvar]; fvar=Pin(fvar); // skip ] bracket @@ -1790,7 +1887,7 @@ chknext: goto exit; } #endif -#ifdef USE_SML_SCRIPT_CMD +#if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD) if (!strncmp(vname,"sml(",4)) { lp+=4; float fvar1; @@ -1902,6 +1999,60 @@ chknext: break; case 'w': +#if defined(ESP32) && defined(USE_WEBCAM) + if (!strncmp(vname,"wc(",3)) { + lp+=3; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + switch ((uint32)fvar1) { + case 0: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_setup(fvar2); + } + break; + case 1: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_get_frame(fvar2); + } + break; + case 2: + { float fvar2,fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + fvar=wc_set_options(fvar2,fvar3); + } + break; + case 3: + fvar=wc_get_width(); + break; + case 4: + fvar=wc_get_height(); + break; + case 5: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_set_streamserver(fvar2); + } + break; + case 6: + { float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=wc_set_motion_detect(fvar2); + } + break; + + default: + fvar=0; + } + lp++; + len=0; + goto exit; + } +#endif if (!strncmp(vname,"wday",4)) { fvar=RtcTime.day_of_week; goto exit; @@ -2722,9 +2873,29 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { lp=GetNumericResult(lp,OPER_EQU,&fvar,0); int8_t pinnr=fvar; SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - pinMode(pinnr,mode&3); + uint8_t mode=0; + if ((*lp=='I') || (*lp=='O') || (*lp=='P')) { + switch (*lp) { + case 'I': + mode=0; + break; + case 'O': + mode=1; + break; + case 'P': + mode=2; + break; + } + lp++; + } else { + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + mode=fvar; + } + uint8_t pm=0; + if (mode==0) pm=INPUT; + if (mode==1) pm=OUTPUT; + if (mode==2) pm=INPUT_PULLUP; + pinMode(pinnr,pm); goto next_line; } else if (!strncmp(lp,"spin(",5)) { lp+=5; @@ -2763,6 +2934,21 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { goto next_line; } #endif +#endif +#ifdef ESP32 + else if (!strncmp(lp,"beep(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + if (fvar<0) { + ledcSetup(7,500,10); + ledcAttachPin(-fvar,7); + ledcWriteTone(7,0); + } else { + ledcWriteTone(7,fvar); + } + lp++; + goto next_line; + } #endif else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { @@ -3274,7 +3460,7 @@ const char HTTP_FORM_FILE_UPGb[] PROGMEM = const char HTTP_FORM_SDC_DIRa[] PROGMEM = "
"; const char HTTP_FORM_SDC_DIRb[] PROGMEM = - "
%s    %d
"; + "
%s     %s : %8d
"; const char HTTP_FORM_SDC_DIRd[] PROGMEM = "
%s
"; const char HTTP_FORM_SDC_DIRc[] PROGMEM = @@ -3297,6 +3483,7 @@ const char HTTP_FORM_SDC_HREF[] PROGMEM = uint8_t reject(char *name) { + while (*name=='/') name++; if (*name=='_') return 1; if (*name=='.') return 1; @@ -3311,6 +3498,8 @@ uint8_t reject(char *name) { if (!strcasecmp(name,"FSEVEN~1")) return 1; if (!strcasecmp(name,"SYSTEM~1")) return 1; #endif + + if (!strncasecmp(name,"System Volume",13)) return 1; return 0; } @@ -3320,7 +3509,7 @@ void ListDir(char *path, uint8_t depth) { char format[12]; sprintf(format,"%%-%ds",24-depth); - File dir=SD.open(path); + File dir=FS_USED.open(path); if (dir) { dir.rewindDirectory(); if (strlen(path)>1) { @@ -3334,35 +3523,48 @@ void ListDir(char *path, uint8_t depth) { } WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); } + char *ep; while (true) { File entry=dir.openNextFile(); if (!entry) { break; } + // esp32 returns path here, shorten to filename + ep=(char*)entry.name(); + if (*ep=='/') ep++; + char *lcp = strrchr(ep,'/'); + if (lcp) { + ep=lcp+1; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR("entry: %s"),ep); + time_t tm=entry.getLastWrite(); + char tstr[24]; + strftime(tstr, 22, "%d-%m-%Y - %H:%M:%S ", localtime(&tm)); + char *pp=path; if (!*(pp+1)) pp++; char *cp=name; // osx formatted disks contain a lot of stuff we dont want - if (reject((char*)entry.name())) goto fclose; + if (reject((char*)ep)) goto fclose; for (uint8_t cnt=0;cnt1) { strcat(path,"/"); } - strcat(path,entry.name()); + strcat(path,ep); ListDir(path,depth+4); path[plen]=0; } else { - snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); - WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); + snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,ep); + WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,ep,name,tstr,entry.size()); } fclose: entry.close(); @@ -3429,8 +3631,8 @@ void script_upload(void) { if (upload.status == UPLOAD_FILE_START) { char npath[48]; sprintf(npath,"%s/%s",path,upload.filename.c_str()); - SD.remove(npath); - upload_file=SD.open(npath,FILE_WRITE); + FS_USED.remove(npath); + upload_file=FS_USED.open(npath,FILE_WRITE); if (!upload_file) Web.upload_error=1; } else if(upload.status == UPLOAD_FILE_WRITE) { if (upload_file) upload_file.write(upload.buf,upload.currentSize); @@ -3449,12 +3651,12 @@ uint8_t DownloadFile(char *file) { File download_file; WiFiClient download_Client; - if (!SD.exists(file)) { + if (!FS_USED.exists(file)) { AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); return 0; } - download_file=SD.open(file,FILE_READ); + download_file=FS_USED.open(file,FILE_READ); if (!download_file) { AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); return 0; @@ -3518,6 +3720,7 @@ void HandleScriptTextareaConfiguration(void) { } } + void HandleScriptConfiguration(void) { if (!HttpCheckPriviledgedAccess()) { return; } @@ -3619,19 +3822,17 @@ void ScriptSaveSettings(void) { strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS +#if defined(USE_24C256) && !defined(USE_SCRIPT_FATFS) if (glob_script_mem.flags&1) { EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); } #endif -#endif -#ifdef USE_SCRIPT_FATFS +#if !defined(USE_24C256) && defined(USE_SCRIPT_FATFS) if (glob_script_mem.flags&1) { - SD.remove(FAT_SCRIPT_NAME); - File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); - file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + FS_USED.remove(FAT_SCRIPT_NAME); + File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_WRITE); + file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE); file.close(); } #endif @@ -4278,6 +4479,14 @@ void dateTime(uint16_t* date, uint16_t* time) { #ifdef SUPPORT_MQTT_EVENT + +#ifndef MQTT_EVENT_MSIZE +#define MQTT_EVENT_MSIZE 256 +#endif +#ifndef MQTT_EVENT_JSIZE +#define MQTT_EVENT_JSIZE 400 +#endif + /********************************************************************************************/ /* * Script: Process received MQTT message. @@ -4292,8 +4501,8 @@ bool ScriptMqttData(void) { bool serviced = false; //toLog(">>> 1"); - toLog(XdrvMailbox.data); - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + //toLog(XdrvMailbox.data); + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > MQTT_EVENT_MSIZE) { return false; } String sTopic = XdrvMailbox.topic; @@ -4313,7 +4522,7 @@ bool ScriptMqttData(void) if (event_item.Key.length() == 0) { //If did not specify Key value = sData; } else { //If specified Key, need to parse Key/Value from JSON data - StaticJsonBuffer<400> jsonBuf; + StaticJsonBuffer jsonBuf; JsonObject& jsonData = jsonBuf.parseObject(sData); String key1 = event_item.Key; String key2; @@ -4557,7 +4766,7 @@ uint32_t cnt; nbuf[cnt]=0; } -void ScriptWebShow(void) { +void ScriptWebShow(char mc) { uint8_t web_script=Run_Scripter(">W",-2,0); if (web_script==99) { char line[128]; @@ -4584,12 +4793,16 @@ void ScriptWebShow(void) { cp++; } char *lin=line; + if (!mc && (*lin!='&')) { + if (*lin=='@') { lin++; optflg=1; } else { optflg=0; } + + // check for input elements if (!strncmp(lin,"sl(",3)) { // insert slider sl(min max var left mid right) @@ -4749,6 +4962,12 @@ void ScriptWebShow(void) { WSContentSend_PD(PSTR("{s}%s{e}"),tmp); } } + } else { + if (*lin==mc) { + Replace_Cmd_Vars(lin+1,tmp,sizeof(tmp)); + WSContentSend_PD(PSTR("%s"),tmp); + } + } } if (*lp==SCRIPT_EOL) { lp++; @@ -4765,12 +4984,7 @@ void ScriptWebShow(void) { #ifdef USE_SENDMAIL -#ifdef ESP8266 -void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { -#else -void script_send_email_body(WiFiClient *client) { -#endif - +void script_send_email_body(void(*func)(char *)) { uint8_t msect=Run_Scripter(">m",-2,0); if (msect==99) { char line[128]; @@ -4796,7 +5010,8 @@ uint8_t msect=Run_Scripter(">m",-2,0); cp++; } Replace_Cmd_Vars(line,tmp,sizeof(tmp)); - client->println(tmp); + //client->println(tmp); + func(tmp); } if (*lp==SCRIPT_EOL) { lp++; @@ -4807,7 +5022,8 @@ uint8_t msect=Run_Scripter(">m",-2,0); } } } else { - client->println("*"); + //client->println("*"); + func((char*)"*"); } } #endif @@ -4868,14 +5084,8 @@ bool Xdrv10(uint8_t function) switch (function) { case FUNC_PRE_INIT: - /* -#ifdef USE_WEBCAM - if (Settings.module==ESP32_CAM_AITHINKER) { - webcam_setup(); - } -#endif -*/ // set defaults to rules memory + //bitWrite(Settings.rule_enabled,0,0); glob_script_mem.script_ram=Settings.rules[0]; glob_script_mem.script_size=MAX_SCRIPT_SIZE; glob_script_mem.flags=0; @@ -4915,15 +5125,29 @@ bool Xdrv10(uint8_t function) #endif #ifdef USE_SCRIPT_FATFS - if (SD.begin(USE_SCRIPT_FATFS)) { + +#ifdef USE_MMC + if (FS_USED.begin()) { +#else + +#ifdef ESP32 + if (PinUsed(GPIO_SPI_MOSI) && PinUsed(GPIO_SPI_MISO) && PinUsed(GPIO_SPI_CLK)) { + SPI.begin(Pin(GPIO_SPI_CLK),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_MOSI), -1); + } +#endif + if (FS_USED.begin(USE_SCRIPT_FATFS)) { +#endif + + //FS_USED.dateTimeCallback(dateTime); + glob_script_mem.script_sd_found=1; char *script; script=(char*)calloc(FAT_SCRIPT_SIZE+4,1); if (!script) break; glob_script_mem.script_ram=script; glob_script_mem.script_size=FAT_SCRIPT_SIZE; - if (SD.exists(FAT_SCRIPT_NAME)) { - File file=SD.open(FAT_SCRIPT_NAME,FILE_READ); + if (FS_USED.exists(FAT_SCRIPT_NAME)) { + File file=FS_USED.open(FAT_SCRIPT_NAME,FILE_READ); file.read((uint8_t*)script,FAT_SCRIPT_SIZE); file.close(); } @@ -4934,11 +5158,6 @@ bool Xdrv10(uint8_t function) glob_script_mem.flags=1; -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - // for unkonwn reasons is not defined in 2.52 - SdFile::dateTimeCallback(dateTime); -#endif - } else { glob_script_mem.script_sd_found=0; } @@ -5001,6 +5220,10 @@ bool Xdrv10(uint8_t function) case FUNC_WEB_ADD_BUTTON: WSContentSend_P(HTTP_BTN_MENU_RULES); break; + case FUNC_WEB_ADD_MAIN_BUTTON: + ScriptWebShow('&'); + break; + case FUNC_WEB_ADD_HANDLER: Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); @@ -5028,7 +5251,7 @@ bool Xdrv10(uint8_t function) #ifdef USE_SCRIPT_WEB_DISPLAY case FUNC_WEB_SENSOR: if (bitRead(Settings.rule_enabled, 0)) { - ScriptWebShow(); + ScriptWebShow(0); } break; #endif //USE_SCRIPT_WEB_DISPLAY diff --git a/tasmota/xdrv_39_webcam.ino b/tasmota/xdrv_39_webcam.ino new file mode 100644 index 000000000..1a2f3b260 --- /dev/null +++ b/tasmota/xdrv_39_webcam.ino @@ -0,0 +1,648 @@ + +#if defined(ESP32) && defined(USE_WEBCAM) + +#define XDRV_39 39 + +#define CAMERA_MODEL_AI_THINKER + +//#define USE_TEMPLATE + +#define WC_LOGLEVEL LOG_LEVEL_INFO + + +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + + +#include "esp_camera.h" +#include "sensor.h" + +uint8_t wc_up; +uint16_t wc_width; +uint16_t wc_height; +uint8_t wc_stream_active; + +uint32_t wc_setup(int32_t fsiz) { +bool psram; + + if (fsiz>10) fsiz=10; + + wc_stream_active=0; + + if (fsiz<0) { + esp_camera_deinit(); + return 0; + } + + if (wc_up) { + esp_camera_deinit(); + //return wc_up; + } + +//esp_log_level_set("*", ESP_LOG_VERBOSE); + +camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; +// config.pixel_format = PIXFORMAT_GRAYSCALE; +// config.pixel_format = PIXFORMAT_RGB565; + +#ifndef USE_TEMPLATE + +config.pin_d0 = Y2_GPIO_NUM; +config.pin_d1 = Y3_GPIO_NUM; +config.pin_d2 = Y4_GPIO_NUM; +config.pin_d3 = Y5_GPIO_NUM; +config.pin_d4 = Y6_GPIO_NUM; +config.pin_d5 = Y7_GPIO_NUM; +config.pin_d6 = Y8_GPIO_NUM; +config.pin_d7 = Y9_GPIO_NUM; +config.pin_xclk = XCLK_GPIO_NUM; +config.pin_pclk = PCLK_GPIO_NUM; +config.pin_vsync = VSYNC_GPIO_NUM; +config.pin_href = HREF_GPIO_NUM; +config.pin_sscb_sda = SIOD_GPIO_NUM; +config.pin_sscb_scl = SIOC_GPIO_NUM; +config.pin_pwdn = PWDN_GPIO_NUM; +config.pin_reset = RESET_GPIO_NUM; + +#else + +if (PinUsed(GPIO_WEBCAM_Y2_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y3_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y4_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y5_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_Y6_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y7_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y8_GPIO_NUM) && PinUsed(GPIO_WEBCAM_Y9_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_XCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_PCLK_GPIO_NUM) && PinUsed(GPIO_WEBCAM_VSYNC_GPIO_NUM) && PinUsed(GPIO_WEBCAM_HREF_GPIO_NUM)\ + && PinUsed(GPIO_WEBCAM_SIOD_GPIO_NUM) && PinUsed(GPIO_WEBCAM_SIOC_GPIO_NUM)) { + + config.pin_d0 = Pin(GPIO_WEBCAM_Y2_GPIO_NUM); //Y2_GPIO_NUM; + config.pin_d1 = Pin(GPIO_WEBCAM_Y3_GPIO_NUM); //Y3_GPIO_NUM; + config.pin_d2 = Pin(GPIO_WEBCAM_Y4_GPIO_NUM); //Y4_GPIO_NUM; + config.pin_d3 = Pin(GPIO_WEBCAM_Y5_GPIO_NUM); //Y5_GPIO_NUM; + config.pin_d4 = Pin(GPIO_WEBCAM_Y6_GPIO_NUM); //Y6_GPIO_NUM; + config.pin_d5 = Pin(GPIO_WEBCAM_Y7_GPIO_NUM); //Y7_GPIO_NUM; + config.pin_d6 = Pin(GPIO_WEBCAM_Y8_GPIO_NUM); //Y8_GPIO_NUM; + config.pin_d7 = Pin(GPIO_WEBCAM_Y9_GPIO_NUM); //Y9_GPIO_NUM; + config.pin_xclk = Pin(GPIO_WEBCAM_XCLK_GPIO_NUM); //XCLK_GPIO_NUM; + config.pin_pclk = Pin(GPIO_WEBCAM_PCLK_GPIO_NUM); //PCLK_GPIO_NUM; + config.pin_vsync = Pin(GPIO_WEBCAM_VSYNC_GPIO_NUM); //VSYNC_GPIO_NUM; + config.pin_href = Pin(GPIO_WEBCAM_HREF_GPIO_NUM); //HREF_GPIO_NUM; + config.pin_sscb_sda = Pin(GPIO_WEBCAM_SIOD_GPIO_NUM); //SIOD_GPIO_NUM; + config.pin_sscb_scl = Pin(GPIO_WEBCAM_SIOC_GPIO_NUM); //SIOC_GPIO_NUM; + + int16_t xpin; + xpin=Pin(GPIO_WEBCAM_PWDN_GPIO_NUM); + if (xpin==99) xpin=-1; + config.pin_pwdn = xpin; //PWDN_GPIO_NUM; + xpin=Pin(GPIO_WEBCAM_RESET_GPIO_NUM); + if (xpin==99) xpin=-1; + config.pin_reset = xpin; //RESET_GPIO_NUM; +} else { + // defaults to AI THINKER + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; +} + + +#endif + + //ESP.getPsramSize() + + //esp_log_level_set("*", ESP_LOG_INFO); + + + // if PSRAM IC present, init with UXGA resolution and higher JPEG quality + // for larger pre-allocated frame buffer. + + psram=psramFound(); + if (psram) { + config.frame_size = FRAMESIZE_UXGA; + config.jpeg_quality = 10; + config.fb_count = 2; + AddLog_P(WC_LOGLEVEL,"PSRAM found!"); + } else { + config.frame_size = FRAMESIZE_VGA; + config.jpeg_quality = 12; + config.fb_count = 1; + AddLog_P(WC_LOGLEVEL,"PSRAM not found!"); + } + + // stupid workaround camera diver eats up static ram should prefer PSRAM + // so we steal static ram to force driver to alloc PSRAM + //ESP.getMaxAllocHeap() + +// void *x=malloc(70000); +void *x=0; + + esp_err_t err = esp_camera_init(&config); + + if (x) free(x); + + if (err != ESP_OK) { + AddLog_P2(WC_LOGLEVEL,"Camera init failed with error 0x%x", err); + return 0; + } + + sensor_t * wc_s = esp_camera_sensor_get(); + // initial sensors are flipped vertically and colors are a bit saturated + if (wc_s->id.PID == OV3660_PID) { + wc_s->set_vflip(wc_s, 1); // flip it back + wc_s->set_brightness(wc_s, 1); // up the brightness just a bit + wc_s->set_saturation(wc_s, -2); // lower the saturation + } + // drop down frame size for higher initial frame rate + wc_s->set_framesize(wc_s, (framesize_t)fsiz); + + camera_fb_t *wc_fb = esp_camera_fb_get(); + wc_width=wc_fb->width; + wc_height=wc_fb->height; + esp_camera_fb_return(wc_fb); + + AddLog_P(WC_LOGLEVEL,"Camera successfully initialized!"); + + wc_up=1; + + if (psram) { + wc_up=2; + } + return wc_up; +} + + +int32_t wc_set_options(uint32_t sel,int32_t value) { + int32_t res=0; + sensor_t *s = esp_camera_sensor_get(); + if (!s) return -99; + + switch (sel) { + case 0: + if (value>=0) s->set_framesize(s,(framesize_t)value); + res = s->status.framesize; + break; + case 1: + if (value>=0) s->set_special_effect(s,value); + res = s->status.special_effect; + break; + case 2: + if (value>=0) s->set_vflip(s,value); + res = s->status.vflip; + break; + case 3: + if (value>=0) s->set_hmirror(s,value); + res = s->status.hmirror; + break; + case 4: + if (value>=-4) s->set_contrast(s,value); + res = s->status.contrast; + break; + case 5: + if (value>=-4) s->set_brightness(s,value); + res = s->status.brightness; + break; + case 6: + if (value>=-4) s->set_saturation(s,value); + res = s->status.saturation; + break; + } + + return res; +} + +uint32_t wc_get_width(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) return 0; + wc_width=wc_fb->width; + esp_camera_fb_return(wc_fb); + return wc_width; +} + +uint32_t wc_get_height(void) { + camera_fb_t *wc_fb = esp_camera_fb_get(); + if (!wc_fb) return 0; + wc_height=wc_fb->height; + esp_camera_fb_return(wc_fb); + return wc_height; +} + +#ifndef MAX_PICSTORE +#define MAX_PICSTORE 4 +#endif +struct PICSTORE { + uint8_t *buff; + uint32_t len; +}; + +struct PICSTORE picstore[MAX_PICSTORE]; + +#ifdef COPYFRAME +struct PICSTORE tmp_picstore; +#endif + +uint32_t get_picstore(int32_t num, uint8_t **buff) { + if (num<0) return MAX_PICSTORE; + *buff=picstore[num].buff; + return picstore[num].len; +} + +uint32_t wc_get_jpeg(uint8_t **buff) { +size_t _jpg_buf_len = 0; +uint8_t * _jpg_buf = NULL; +camera_fb_t *wc_fb; + wc_fb = esp_camera_fb_get(); + if (!wc_fb) return 0; + if (wc_fb->format!=PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + esp_camera_fb_return(wc_fb); + *buff=_jpg_buf; + return _jpg_buf_len; +} + + +uint32_t wc_get_frame(int32_t bnum) { + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + camera_fb_t *wc_fb=0; + bool jpeg_converted=false; + + if (bnum<0) { + if (bnum<-MAX_PICSTORE) bnum=-1; + bnum=-bnum; + bnum--; + if (picstore[bnum].buff) free(picstore[bnum].buff); + picstore[bnum].len=0; + return 0; + } + +#ifdef COPYFRAME + if (bnum&0x10) { + bnum&=0xf; + _jpg_buf=tmp_picstore.buff; + _jpg_buf_len=tmp_picstore.len; + if (!_jpg_buf_len) return 0; + goto pcopy; + } +#endif + + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + AddLog_P(WC_LOGLEVEL, "cant get frame"); + return 0; + } + if (!bnum) { + wc_width = wc_fb->width; + wc_height = wc_fb->height; + esp_camera_fb_return(wc_fb); + return 0; + } + + if (wc_fb->format!=PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + //Serial.println("JPEG compression failed"); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + +pcopy: + if (bnum<1 || bnum>MAX_PICSTORE) bnum=1; + bnum--; + if (picstore[bnum].buff) free(picstore[bnum].buff); + picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (picstore[bnum].buff) { + memcpy(picstore[bnum].buff,_jpg_buf,_jpg_buf_len); + picstore[bnum].len=_jpg_buf_len; + } else { + AddLog_P(WC_LOGLEVEL, "cant allocate picstore"); + picstore[bnum].len=0; + } + if (wc_fb) esp_camera_fb_return(wc_fb); + if (jpeg_converted) free(_jpg_buf); + if (!picstore[bnum].buff) return 0; + return _jpg_buf_len; +} + +bool HttpCheckPriviledgedAccess(bool); +extern ESP8266WebServer *Webserver; + +void HandleImage(void) { + if (!HttpCheckPriviledgedAccess(true)) { return; } + + uint32_t bnum = Webserver->arg(F("p")).toInt(); + if (bnum<0 || bnum>MAX_PICSTORE) bnum=1; + WiFiClient client = Webserver->client(); + String response = "HTTP/1.1 200 OK\r\n"; + response += "Content-disposition: inline; filename=cap.jpg\r\n"; + response += "Content-type: image/jpeg\r\n\r\n"; + Webserver->sendContent(response); + + if (!bnum) { + uint8_t *buff; + uint32_t len; + len=wc_get_jpeg(&buff); + if (len) { + client.write(buff,len); + free(buff); + } + } else { + bnum--; + if (!picstore[bnum].len) { + AddLog_P2(WC_LOGLEVEL, PSTR("no image #: %d"), bnum); + return; + } + client.write((char *)picstore[bnum].buff, picstore[bnum].len); + } + + AddLog_P2(WC_LOGLEVEL, PSTR("sending image #: %d"), bnum+1); + +} + +ESP8266WebServer *CamServer; +#define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" + +WiFiClient client; + +void handleMjpeg(void) { + AddLog_P(WC_LOGLEVEL, "handle camserver"); + //if (!wc_stream_active) { + wc_stream_active=1; + client = CamServer->client(); + AddLog_P(WC_LOGLEVEL, "create client"); + //} +} + + + + +void handleMjpeg_task(void) { + camera_fb_t *wc_fb; + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + + //WiFiClient client = CamServer->client(); + uint32_t tlen; + bool jpeg_converted=false; + + if (!client.connected()) { + wc_stream_active=0; + AddLog_P(WC_LOGLEVEL,"client fail"); + goto exit; + } + + if (wc_stream_active==1) { + client.flush(); + client.setTimeout(3); + AddLog_P(WC_LOGLEVEL, "start stream"); + client.print("HTTP/1.1 200 OK\r\n" + "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" + "\r\n"); + wc_stream_active=2; + } else { + + wc_fb = esp_camera_fb_get(); + if (!wc_fb) { + wc_stream_active=0; + AddLog_P(WC_LOGLEVEL, "frame fail"); + goto exit; + } + + if (wc_fb->format!=PIXFORMAT_JPEG) { + jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted){ + AddLog_P(WC_LOGLEVEL, "JPEG compression failed"); + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + } else { + _jpg_buf_len = wc_fb->len; + _jpg_buf = wc_fb->buf; + } + + client.printf("Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n" + "\r\n", static_cast(_jpg_buf_len)); + tlen=client.write(_jpg_buf, _jpg_buf_len); + /* + if (tlen!=_jpg_buf_len) { + esp_camera_fb_return(wc_fb); + wc_stream_active=0; + AddLog_P(WC_LOGLEVEL, "send fail"); + }*/ + client.print("\r\n--" BOUNDARY "\r\n"); + +#ifdef COPYFRAME + if (tmp_picstore.buff) free(tmp_picstore.buff); + tmp_picstore.buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (tmp_picstore.buff) { + memcpy(tmp_picstore.buff,_jpg_buf,_jpg_buf_len); + tmp_picstore.len=_jpg_buf_len; + } else { + tmp_picstore.len=0; + } +#endif + + if (jpeg_converted) free(_jpg_buf); + esp_camera_fb_return(wc_fb); + //AddLog_P(WC_LOGLEVEL, "send frame"); + +exit: + if (!wc_stream_active) { + AddLog_P(WC_LOGLEVEL, "stream exit"); + client.flush(); + client.stop(); + } + } +} + +void CamHandleRoot(void) { + //CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg"); + CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); + CamServer->send(302, "", ""); + Serial.printf("WC root called"); +} + +uint16_t motion_detect; +uint32_t motion_ltime; +uint32_t motion_trigger; +uint32_t motion_brightness; +uint8_t *last_motion_buffer; + +uint32_t wc_set_motion_detect(int32_t value) { + if (value>=0) motion_detect=value; + if (value==-1) { + return motion_trigger; + } else { + return motion_brightness; + } +} + +// optional motion detector +void detect_motion(void) { +camera_fb_t *wc_fb; +uint8_t *out_buf=0; + + if ((millis()-motion_ltime)>motion_detect) { + motion_ltime=millis(); + wc_fb = esp_camera_fb_get(); + if (!wc_fb) return; + + if (!last_motion_buffer) { + last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + if (last_motion_buffer) { + if (wc_fb->format==PIXFORMAT_JPEG) { + out_buf=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4,MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (out_buf) { + fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf); + uint32_t x,y; + uint8_t *pxi=out_buf; + uint8_t *pxr=last_motion_buffer; + // convert to bw + uint64_t accu=0; + uint64_t bright=0; + for (y=0;yheight;y++) { + for (x=0;xwidth;x++) { + int32_t gray=(pxi[0]+pxi[1]+pxi[2])/3; + int32_t lgray=pxr[0]; + pxr[0]=gray; + pxi+=3; + pxr++; + accu+=abs(gray-lgray); + bright+=gray; + } + } + motion_trigger=accu/((wc_fb->height*wc_fb->width)/100); + motion_brightness=bright/((wc_fb->height*wc_fb->width)/100); + free(out_buf); + } + } + } + esp_camera_fb_return(wc_fb); + } +} + +uint32_t wc_set_streamserver(uint32_t flag) { + + if (global_state.wifi_down) return 0; + + wc_stream_active=0; + + if (flag) { + if (!CamServer) { + CamServer = new ESP8266WebServer(81); + CamServer->on("/", CamHandleRoot); + CamServer->on("/cam.mjpeg", handleMjpeg); + CamServer->on("/cam.jpg", handleMjpeg); + CamServer->on("/stream", handleMjpeg); + AddLog_P(WC_LOGLEVEL, "cam stream init"); + CamServer->begin(); + } + } else { + if (CamServer) { + CamServer->stop(); + delete CamServer; + CamServer=NULL; + AddLog_P(WC_LOGLEVEL, "cam stream exit"); + + } + } + return 0; +} + +void wc_loop(void) { + if (CamServer) CamServer->handleClient(); + if (wc_stream_active) handleMjpeg_task(); + if (motion_detect) detect_motion(); +} + +void wc_pic_setup(void) { + Webserver->on("/wc.jpg", HandleImage); + Webserver->on("/wc.mjpeg", HandleImage); +} + +/* +typedef enum { + // FRAMESIZE_96x96, // 96x96 + FRAMESIZE_QQVGA, // 160x120 0 + FRAMESIZE_QQVGA2, // 128x160 1 + FRAMESIZE_QCIF, // 176x144 2 + FRAMESIZE_HQVGA, // 240x176 3 + + // FRAMESIZE_240x240, // 240x240 3 + + FRAMESIZE_QVGA, // 320x240 4 + FRAMESIZE_CIF, // 400x296 5 + FRAMESIZE_VGA, // 640x480 6 + FRAMESIZE_SVGA, // 800x600 7 + FRAMESIZE_XGA, // 1024x768 8 + FRAMESIZE_SXGA, // 1280x1024 9 + FRAMESIZE_UXGA, // 1600x1200 10 + + + FRAMESIZE_QXGA, // 2048*1536 + FRAMESIZE_INVALID +} framesize_t; + +flash led = gpio4 +red led = gpio 33 +*/ + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv39(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_LOOP: + wc_loop(); + break; + case FUNC_WEB_ADD_HANDLER: + wc_pic_setup(); + break; + } + return result; +} + +#endif