mirror of
https://github.com/xoseperez/espurna.git
synced 2026-03-14 12:17:11 +01:00
schedule immediately with module functions, don't wait more flash-strings in the module and utility functions support rare condition when body cannot be sent on connect, use poll to send it don't wait for header line in body, and bail out when done parsing make sure data is copied into the async variant, and remains there for the duration of the connection general async api is... more complicated that it needs to be until wolfssl / brssl port is here, though, there is no other choice secureclient config becomes a simple struct, pending further rework
207 lines
5.2 KiB
C++
207 lines
5.2 KiB
C++
/*
|
|
|
|
HTTP(s) OTA MODULE
|
|
|
|
Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
|
|
|
|
*/
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// OTA by using Core's HTTP(s) updater
|
|
// -----------------------------------------------------------------------------
|
|
|
|
#include "espurna.h"
|
|
|
|
#if OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
|
|
|
|
#include "mqtt.h"
|
|
#include "ota.h"
|
|
#include "system.h"
|
|
#include "terminal.h"
|
|
|
|
#include <ESP8266HTTPClient.h>
|
|
#include <ESP8266httpUpdate.h>
|
|
|
|
#include "libs/URL.h"
|
|
#include "libs/TypeChecks.h"
|
|
#include "libs/SecureClientHelpers.h"
|
|
|
|
#if SECURE_CLIENT != SECURE_CLIENT_NONE
|
|
#include <WiFiClientSecure.h>
|
|
|
|
namespace {
|
|
|
|
#if OTA_SECURE_CLIENT_INCLUDE_CA
|
|
#include "static/ota_client_trusted_root_ca.h"
|
|
#else
|
|
#include "static/digicert_evroot_pem.h"
|
|
#define _ota_client_trusted_root_ca _ssl_digicert_ev_root_ca
|
|
#endif
|
|
|
|
#endif // SECURE_CLIENT != SECURE_CLIENT_NONE
|
|
|
|
} // namespace
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
namespace ota {
|
|
namespace httpupdate {
|
|
namespace {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Generic update methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void run(WiFiClient* client, const String& url) {
|
|
// Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
|
|
// Must happen right now, since HTTP updater will block until it's done
|
|
eepromRotate(false);
|
|
|
|
DEBUG_MSG_P(PSTR("[OTA] Downloading %s ...\n"), url.c_str());
|
|
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
|
ESPhttpUpdate.rebootOnUpdate(false);
|
|
|
|
t_httpUpdate_return result = HTTP_UPDATE_NO_UPDATES;
|
|
result = ESPhttpUpdate.update(*client, url);
|
|
|
|
switch (result) {
|
|
case HTTP_UPDATE_FAILED:
|
|
DEBUG_MSG_P(PSTR("[OTA] Update failed (error %d): %s\n"), ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
|
|
break;
|
|
case HTTP_UPDATE_NO_UPDATES:
|
|
DEBUG_MSG_P(PSTR("[OTA] No updates"));
|
|
break;
|
|
case HTTP_UPDATE_OK:
|
|
DEBUG_MSG_P(PSTR("[OTA] Done, restarting..."));
|
|
prepareReset(CustomResetReason::Ota);
|
|
return;
|
|
}
|
|
|
|
eepromRotate(true);
|
|
}
|
|
|
|
void clientFromHttp(const String& url) {
|
|
auto client = std::make_unique<WiFiClient>();
|
|
run(client.get(), url);
|
|
}
|
|
|
|
#if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
|
|
|
|
void clientFromHttps(const String& url, SecureClientConfig& config) {
|
|
// Check for NTP early to avoid constructing SecureClient prematurely
|
|
const int check = config.on_check();
|
|
if (!ntpSynced() && (check == SECURE_CLIENT_CHECK_CA)) {
|
|
DEBUG_MSG_P(PSTR("[OTA] Time not synced! Cannot use CA validation\n"));
|
|
return;
|
|
}
|
|
|
|
auto client = std::make_unique<SecureClient>(config);
|
|
if (!client->beforeConnected()) {
|
|
return;
|
|
}
|
|
|
|
run(&client->get(), url);
|
|
}
|
|
|
|
static SecureClientConfig defaultSecureClientConfig {
|
|
.tag = "OTA",
|
|
.on_check = []() -> int {
|
|
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK);
|
|
},
|
|
.on_certificate = []() -> PGM_P {
|
|
return _ota_client_trusted_root_ca;
|
|
},
|
|
.on_fingerprint = []() -> String {
|
|
return getSetting("otaFP", OTA_FINGERPRINT);
|
|
},
|
|
.on_mfln = []() -> uint16_t {
|
|
return getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN);
|
|
},
|
|
.debug = true,
|
|
};
|
|
|
|
void clientFromHttps(const String& url) {
|
|
clientFromHttps(url, defaultSecureClientConfig);
|
|
}
|
|
|
|
#endif // SECURE_CLIENT_BEARSSL
|
|
|
|
void clientFromUrl(const String& url) {
|
|
if (url.startsWith("http://")) {
|
|
clientFromHttp(url);
|
|
return;
|
|
}
|
|
#if SECURE_CLIENT != SECURE_CLIENT_NONE
|
|
else if (url.startsWith("https://")) {
|
|
clientFromHttps(url);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
DEBUG_MSG_P(PSTR("[OTA] Incorrect URL specified\n"));
|
|
}
|
|
|
|
#if TERMINAL_SUPPORT
|
|
|
|
void terminalCommands() {
|
|
terminalRegisterCommand(F("OTA"), [](::terminal::CommandContext&& ctx) {
|
|
if (ctx.argv.size() == 2) {
|
|
clientFromUrl(ctx.argv[1]);
|
|
terminalOK(ctx);
|
|
return;
|
|
}
|
|
|
|
terminalError(ctx, F("OTA <url>"));
|
|
});
|
|
}
|
|
|
|
#endif // TERMINAL_SUPPORT
|
|
|
|
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
|
|
|
|
void mqttCallback(unsigned int type, const char* topic, char* payload) {
|
|
if (type == MQTT_CONNECT_EVENT) {
|
|
mqttSubscribe(MQTT_TOPIC_OTA);
|
|
return;
|
|
}
|
|
|
|
if (type == MQTT_MESSAGE_EVENT) {
|
|
const String t = mqttMagnitude(topic);
|
|
static bool busy { false };
|
|
|
|
if (!busy && t.equals(MQTT_TOPIC_OTA)) {
|
|
DEBUG_MSG_P(PSTR("[OTA] Queuing from URL: %s\n"), payload);
|
|
|
|
const String url(payload);
|
|
schedule_function([url]() {
|
|
clientFromUrl(url);
|
|
busy = false;
|
|
});
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
#endif // MQTT_SUPPORT
|
|
|
|
} // namespace
|
|
} // namespace httpupdate
|
|
} // namespace ota
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void otaClientSetup() {
|
|
moveSetting("otafp", "otaFP");
|
|
|
|
#if TERMINAL_SUPPORT
|
|
ota::httpupdate::terminalCommands();
|
|
#endif
|
|
|
|
#if (MQTT_SUPPORT && OTA_MQTT_SUPPORT)
|
|
mqttRegister(ota::httpupdate::mqttCallback);
|
|
#endif
|
|
}
|
|
|
|
#endif // OTA_CLIENT == OTA_CLIENT_HTTPUPDATE
|