* `Sendmail` upgraded to ESP-Mail-Client v3.4.9 from v1.2.0, using BearSSL instead of MbedTLS * Fix compilation on ESP8266 * Fix compilation * fix compilation
436 lines
11 KiB
C++
436 lines
11 KiB
C++
#ifndef MB_Time_H
|
|
#define MB_Time_H
|
|
|
|
#include "ESP_Mail_Client_Version.h"
|
|
#if !VALID_VERSION_CHECK(30409)
|
|
#error "Mixed versions compilation."
|
|
#endif
|
|
|
|
/*
|
|
* Time helper class v1.0.7
|
|
*
|
|
* Created August 28, 2023
|
|
*
|
|
* Do not remove or modify this file as it required for AVR, ARM, SAMD devices and external client to work.
|
|
*
|
|
* The MIT License (MIT)
|
|
* Copyright (c) 2022 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.
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
#include "Networks_Provider.h"
|
|
|
|
#define ESP_TIME_DEFAULT_TS 1577836800
|
|
#define ESP_TIME_NON_TS -1000
|
|
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#if defined(ESP32) && !defined(ESP_ARDUINO_VERSION) /* ESP32 core < v2.0.x */
|
|
#include <sys/time.h>
|
|
#else
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#if defined(ESP_MAIL_USE_PSRAM)
|
|
#define MB_STRING_USE_PSRAM
|
|
#endif
|
|
|
|
#include "MB_String.h"
|
|
#include "MB_MCU.h"
|
|
|
|
#if defined(ESP8266)
|
|
#include "user_interface.h"
|
|
#endif
|
|
|
|
#if defined(__AVR__) || defined(MB_ARDUINO_TEENSY)
|
|
#define MB_TIME_PGM_ATTR
|
|
#else
|
|
#define MB_TIME_PGM_ATTR PROGMEM
|
|
#endif
|
|
|
|
#if !defined(MB_ARDUINO_ESP)
|
|
static const char *mb_months[12] MB_TIME_PGM_ATTR = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
#endif
|
|
|
|
enum base_time_type_t
|
|
{
|
|
base_time_type_undefined = -1,
|
|
base_time_type_unset = 0,
|
|
base_time_type_auto = 1,
|
|
base_time_type_user = 2
|
|
};
|
|
|
|
class MB_Time
|
|
{
|
|
public:
|
|
MB_Time()
|
|
{
|
|
}
|
|
|
|
~MB_Time()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
#if defined(ENABLE_NTP_TIME)
|
|
if (_sv1.length())
|
|
{
|
|
_sv1.clear();
|
|
_sv2.clear();
|
|
_sv3.clear();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void readNTPTime(unsigned long waitMs, bool debugProgress)
|
|
{
|
|
if (!timeReady())
|
|
{
|
|
if (WiFI_CONNECTED)
|
|
{
|
|
|
|
#if defined(ENABLE_NTP_TIME)
|
|
#if (defined(ESP32) || defined(ESP8266))
|
|
configTime(_gmt_offset * 3600, _daylight_offset * 60, _sv1.c_str(), _sv2.c_str(), _sv3.c_str());
|
|
#elif defined(ARDUINO_RASPBERRY_PI_PICO_W)
|
|
NTP.begin(_sv1.c_str(), _sv2.c_str());
|
|
NTP.waitSet();
|
|
#endif
|
|
#endif
|
|
unsigned long ms = millis();
|
|
|
|
#if !defined(SILENT_MODE)
|
|
unsigned long pgms = 0;
|
|
#endif
|
|
do
|
|
{
|
|
#if defined(ESP_MAIL_HAS_WIFI_TIME)
|
|
_base_ts = WiFi.getTime() > ESP_TIME_DEFAULT_TS ? WiFi.getTime() : _base_ts;
|
|
#elif defined(ENABLE_NTP_TIME)
|
|
_base_ts = time(nullptr) > ESP_TIME_DEFAULT_TS ? time(nullptr) : _base_ts;
|
|
#else
|
|
break;
|
|
#endif
|
|
|
|
#if !defined(SILENT_MODE)
|
|
if (debugProgress && millis() - pgms > 50)
|
|
{
|
|
pgms = millis();
|
|
esp_mail_debug_print(".", false);
|
|
}
|
|
#endif
|
|
|
|
yield_impl();
|
|
} while (millis() - ms < waitMs && _base_ts < ESP_TIME_DEFAULT_TS);
|
|
#if !defined(SILENT_MODE)
|
|
if (debugProgress)
|
|
esp_mail_debug_print("", true);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
int setTimestamp(time_t ts, float gmtOffset)
|
|
{
|
|
_gmt_offset = gmtOffset;
|
|
_base_time_type = ts > ESP_TIME_DEFAULT_TS ? base_time_type_user : base_time_type_unset;
|
|
#if defined(MB_ARDUINO_ESP)
|
|
struct timeval tm; // sec, us
|
|
tm.tv_sec = ts;
|
|
tm.tv_usec = 0;
|
|
return settimeofday((const struct timeval *)&tm, 0);
|
|
#else
|
|
_ts_offset = ts - millis() / 1000;
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
/** Get the timestamp from the year, month, date, hour, minute,
|
|
* and second provided.
|
|
*
|
|
* @param year The year.
|
|
* @param mon The month from 1 to 12.
|
|
* @param date The dates.
|
|
* @param hour The hours.
|
|
* @param mins The minutes.
|
|
* @param sec The seconds.
|
|
* @return time_t The value of timestamp.
|
|
*/
|
|
time_t 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;
|
|
}
|
|
|
|
/** Get the timestamp from the RFC 2822 time string.
|
|
* e.g. Mon, 02 May 2022 00:30:00 +0000 or 02 May 2022 00:30:00 +0000
|
|
*
|
|
* @param gmt Return the GMT time.
|
|
* @return timestamp of time string.
|
|
*/
|
|
time_t getTimestamp(const char *timeString, bool gmt = false)
|
|
{
|
|
#if defined(MB_ARDUINO_ESP)
|
|
struct tm timeinfo;
|
|
|
|
if (strstr(timeString, ",") != NULL)
|
|
strptime(timeString, "%a, %d %b %Y %H:%M:%S %z", &timeinfo);
|
|
else
|
|
strptime(timeString, "%d %b %Y %H:%M:%S %z", &timeinfo);
|
|
|
|
time_t ts = mktime(&timeinfo);
|
|
return ts;
|
|
|
|
#else
|
|
|
|
time_t ts = 0;
|
|
_vectorImpl<MB_String> tk;
|
|
MB_String s1 = timeString;
|
|
|
|
splitToken(s1, tk, ' ');
|
|
int day = 0, mon = 0, year = 0, hr = 0, mins = 0, sec = 0, tz_h = 0, tz_m = 0;
|
|
|
|
int tkindex = tk.size() == 5 ? -1 : 0; // No week days?
|
|
|
|
// some response may include (UTC) and (ICT)
|
|
if (tk.size() >= 5)
|
|
{
|
|
day = atoi(tk[tkindex + 1].c_str());
|
|
for (size_t i = 0; i < 12; i++)
|
|
{
|
|
if (strcmp_P(mb_months[i], tk[tkindex + 2].c_str()) == 0)
|
|
mon = i;
|
|
}
|
|
|
|
// RFC 822 year to RFC 2822
|
|
if (tk[tkindex + 3].length() == 2)
|
|
tk[tkindex + 3].prepend("20");
|
|
|
|
year = atoi(tk[tkindex + 3].c_str());
|
|
|
|
_vectorImpl<MB_String> tk2;
|
|
splitToken(tk[tkindex + 4], tk2, ':');
|
|
if (tk2.size() == 3)
|
|
{
|
|
hr = atoi(tk2[0].c_str());
|
|
mins = atoi(tk2[1].c_str());
|
|
sec = atoi(tk2[2].c_str());
|
|
}
|
|
|
|
ts = getTimestamp(year, mon + 1, day, hr, mins, sec);
|
|
|
|
if (tk[tkindex + 5].length() == 5 && gmt)
|
|
{
|
|
char tmp[6];
|
|
memset(tmp, 0, 6);
|
|
strncpy(tmp, tk[tkindex + 5].c_str() + 1, 2);
|
|
tz_h = atoi(tmp);
|
|
|
|
memset(tmp, 0, 6);
|
|
strncpy(tmp, tk[tkindex + 5].c_str() + 3, 2);
|
|
tz_m = atoi(tmp);
|
|
|
|
time_t tz = tz_h * 60 * 60 + tz_m * 60;
|
|
if (tk[tkindex + 5][0] == '+')
|
|
ts -= tz; // remove time zone offset
|
|
else
|
|
ts += tz;
|
|
}
|
|
}
|
|
return ts;
|
|
#endif
|
|
}
|
|
|
|
/** Get the current timestamp.
|
|
*
|
|
* @return uint64_t The value of current timestamp.
|
|
*/
|
|
uint64_t getCurrentTimestamp()
|
|
{
|
|
getBaseTime();
|
|
return _base_ts;
|
|
}
|
|
|
|
/** Get the current rfc822 date time string that valid for Email.
|
|
* @return String The current date time string.
|
|
*/
|
|
String getDateTimeString()
|
|
{
|
|
getBaseTime();
|
|
|
|
char tbuf[40];
|
|
strftime(tbuf, 40, "%a, %d %b %Y %H:%M:%S %z", &_time_info);
|
|
MB_String tStr = tbuf, tzStr;
|
|
|
|
int p = _gmt_offset < 0 ? -1 : 1;
|
|
float dif = (p * (_gmt_offset * 10 - (int)_gmt_offset * 10)) * 60.0 / 10;
|
|
tzStr = (_gmt_offset < 0) ? '-' : '+';
|
|
|
|
if ((int)_gmt_offset < 10)
|
|
tzStr += 0;
|
|
|
|
tzStr += (int)_gmt_offset;
|
|
|
|
if (dif < 10)
|
|
tzStr += 0;
|
|
|
|
tzStr += (int)dif;
|
|
|
|
// replace for valid timezone
|
|
tStr.replaceAll("+0000", tzStr);
|
|
return tStr.c_str();
|
|
}
|
|
|
|
String getDateTimeString(time_t ts, const char *format)
|
|
{
|
|
char tbuf[100];
|
|
strftime(tbuf, 100, format, localtime(&ts));
|
|
return tbuf;
|
|
}
|
|
|
|
void begin(float gmtOffset, float daylightOffset, const char *servers)
|
|
{
|
|
if (timeReady() && _base_time_type == base_time_type_undefined)
|
|
_base_time_type = base_time_type_auto;
|
|
else if (_base_time_type == base_time_type_undefined)
|
|
_base_time_type = base_time_type_unset;
|
|
|
|
#if defined(ENABLE_NTP_TIME) && (defined(ESP32) || defined(ESP8266) || defined(ARDUINO_RASPBERRY_PI_PICO_W))
|
|
|
|
_vectorImpl<MB_String> tk;
|
|
MB_String sv = servers;
|
|
|
|
clear();
|
|
|
|
splitToken(sv, tk, ',');
|
|
|
|
if (tk.size() > 0)
|
|
_sv1 = tk[0];
|
|
if (tk.size() > 1)
|
|
_sv2 = tk[1];
|
|
if (tk.size() > 2)
|
|
_sv3 = tk[2];
|
|
|
|
#endif
|
|
|
|
if ((gmtOffset > ESP_TIME_NON_TS && _gmt_offset != gmtOffset) || (daylightOffset > ESP_TIME_NON_TS && _daylight_offset != daylightOffset))
|
|
{
|
|
// Reset system timestamp when config changed
|
|
_base_ts = 0;
|
|
if (gmtOffset > ESP_TIME_NON_TS)
|
|
_gmt_offset = gmtOffset;
|
|
if (daylightOffset > ESP_TIME_NON_TS)
|
|
_daylight_offset = daylightOffset;
|
|
|
|
if (_base_time_type == base_time_type_unset)
|
|
setTimestamp(millis(), _gmt_offset);
|
|
}
|
|
}
|
|
|
|
/** get the clock ready state
|
|
*/
|
|
bool timeReady()
|
|
{
|
|
getBaseTime();
|
|
return _time_ready;
|
|
}
|
|
|
|
private:
|
|
time_t _base_ts = 0;
|
|
struct tm _time_info;
|
|
float _gmt_offset = ESP_TIME_NON_TS;
|
|
float _daylight_offset = ESP_TIME_NON_TS;
|
|
void splitToken(MB_String &str, _vectorImpl<MB_String> &tk, char delim)
|
|
{
|
|
size_t current, previous = 0;
|
|
current = str.find(delim, previous);
|
|
MB_String s;
|
|
while (current != MB_String::npos)
|
|
{
|
|
|
|
s = str.substr(previous, current - previous);
|
|
s.trim();
|
|
|
|
if (s.length() > 0)
|
|
tk.push_back(s);
|
|
previous = current + 1;
|
|
current = str.find(delim, previous);
|
|
}
|
|
|
|
s = str.substr(previous, current - previous);
|
|
|
|
s.trim();
|
|
|
|
if (s.length() > 0)
|
|
tk.push_back(s);
|
|
|
|
s.clear();
|
|
}
|
|
|
|
// 9Safe) Get base timestamp
|
|
void getBaseTime()
|
|
{
|
|
|
|
#if defined(ESP32) || defined(ESP8266) || defined(MB_ARDUINO_PICO)
|
|
|
|
if (_base_ts < time(nullptr) && time(nullptr) > ESP_TIME_DEFAULT_TS)
|
|
_base_ts = time(nullptr);
|
|
|
|
#if defined(ESP32)
|
|
getLocalTime(&_time_info);
|
|
#elif defined(ESP8266) || defined(MB_ARDUINO_PICO)
|
|
localtime_r(&_base_ts, &_time_info);
|
|
#endif
|
|
|
|
#elif defined(ESP_MAIL_HAS_WIFI_TIME)
|
|
if (WiFI_CONNECTED)
|
|
_base_ts = WiFi.getTime() > ESP_TIME_DEFAULT_TS ? WiFi.getTime() : _base_ts;
|
|
#else
|
|
_base_ts = _ts_offset + millis() / 1000;
|
|
#endif
|
|
|
|
_time_ready = _base_ts > ESP_TIME_DEFAULT_TS;
|
|
if (_time_ready)
|
|
_ts_offset = _base_ts - millis() / 1000;
|
|
}
|
|
|
|
#if defined(ENABLE_NTP_TIME)
|
|
// in ESP8266 these NTP sever strings should be existed during configuring time.
|
|
MB_String _sv1, _sv2, _sv3;
|
|
#endif
|
|
|
|
uint32_t _ts_offset = 0;
|
|
bool _time_ready = false;
|
|
base_time_type_t _base_time_type = base_time_type_undefined;
|
|
};
|
|
|
|
#endif // MB_Time_H
|