Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

206 lines
5.1 KiB

/*
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..."));
deferredReset(500, 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);
}
SecureClientConfig defaultSecureClientConfig {
"OTA",
[]() -> int {
return getSetting("otaScCheck", OTA_SECURE_CLIENT_CHECK);
},
[]() -> PGM_P {
return _ota_client_trusted_root_ca;
},
[]() -> String {
return getSetting("otaFP", OTA_FINGERPRINT);
},
[]() -> uint16_t {
return getSetting("otaScMFLN", OTA_SECURE_CLIENT_MFLN);
},
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"), [](const terminal::CommandContext& ctx) {
if (ctx.argc == 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