From e3e01da80bb3dd704e41884f4df9a89b08e20f98 Mon Sep 17 00:00:00 2001 From: anschrammh Date: Tue, 27 Sep 2022 08:05:35 +0200 Subject: [PATCH] Added the OTAUpdater class which wraps the ESP8266httpUpdate to be compatible with my OTA service php file --- src/app/OTAUpdater.cpp | 231 +++++++++++++++++++++++++++++++++++++++++ src/app/OTAUpdater.h | 103 ++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 src/app/OTAUpdater.cpp create mode 100644 src/app/OTAUpdater.h diff --git a/src/app/OTAUpdater.cpp b/src/app/OTAUpdater.cpp new file mode 100644 index 0000000..ea42ee6 --- /dev/null +++ b/src/app/OTAUpdater.cpp @@ -0,0 +1,231 @@ +#include "OTAUpdater.h" +#include "HttpClient.h" + +//#define DEBUG_OTA_UPDATER + +OTAUpdater::OTAUpdater(const char *serverAddress, const char *path, const char *ota_auth_key, uint16_t port) : _port(port) +{ + if(serverAddress) + _serverAddress = strdup(serverAddress); + + if(path) + _path = strdup(path); + + if(ota_auth_key) + _ota_auth_key = strdup(ota_auth_key); +} + +OTAUpdater::OTAUpdater(int httpClientTimeout,const char *serverAddress, const char *path, const char *ota_auth_key, uint16_t port) : ESP8266HTTPUpdate(httpClientTimeout), _port(port) +{ + if(serverAddress) + _serverAddress = strdup(serverAddress); + + if(path) + _path = strdup(path); + + if(ota_auth_key) + _ota_auth_key = strdup(ota_auth_key); +} + +OTAUpdater::~OTAUpdater() +{ + if(_publicKey != nullptr) + delete _publicKey; + + if(_hash != nullptr) + delete _hash; + + if(_signingVerifier != nullptr) + delete _signingVerifier; + + free(_serverAddress); + free(_path); + free(_ota_auth_key); +} + +//Enables the binary signing verification +void OTAUpdater::enableSHA256UpdateVerification(const char *publicKey) +{ + if(_publicKey != nullptr) + delete _publicKey; + + if(_hash != nullptr) + delete _hash; + + if(_signingVerifier != nullptr) + delete _signingVerifier; + + _publicKey = new BearSSL::PublicKey(publicKey); + _hash = new BearSSL::HashSHA256(); + _signingVerifier = new BearSSL::SigningVerifier(_publicKey); + + Update.installSignature(_hash, _signingVerifier); +} + +void OTAUpdater::setServerAddress(const char *serverAddress) +{ + if(!serverAddress) + return; + + free(_serverAddress); + _serverAddress = strdup(serverAddress); +} + +void OTAUpdater::setPath(const char *path) +{ + //The path can be empty + if(!path) + { + free(_path); + _path = nullptr; + } + else + { + free(_path); + _path = strdup(path); + } + +} + +void OTAUpdater::setOtaAuthKey(const char *ota_auth_key) +{ + //The authKey can be empty + if(!ota_auth_key) + { + free(_ota_auth_key); + _ota_auth_key = nullptr; + } + else + { + free(_ota_auth_key); + _ota_auth_key = strdup(ota_auth_key); + } +} + +void OTAUpdater::setPort(uint16_t port) +{ + _port = port; +} + +OTAUpdater::UpdateInfo OTAUpdater::fetchUpdateInfo(const char *softwareVersion, uint32_t timeout) +{ + HttpClient httpClient(_serverAddress, _path, _port, timeout); + UpdateInfo ui; + + if(!softwareVersion) + { + ui.info = OTAUpdater::UpdateInfo::HTTP_NO_UPDATE; + return ui; + } + + Dictionary getData; + getData.add("authKey", DictionaryHelper::StringEntity(_ota_auth_key)); + getData.add("checkonly", NULL); + + Dictionary headerData; + headerData.add("x-ESP8266-version",DictionaryHelper::StringEntity(softwareVersion)); + headerData.add("x-ESP8266-STA-MAC",DictionaryHelper::StringEntity(WiFi.macAddress().c_str())); + headerData.add("User-Agent",DictionaryHelper::StringEntity("ESP8266-http-Update")); + + if(httpClient.sendHttpQuery(HttpClient::HttpRequestMethod::GET, &getData, NULL, &headerData) == 0) + { + HttpConstants::HTTP_CODE result = httpClient.isReplyAvailable(1000); + switch(result) + { + case HttpConstants::HTTP_CODE::HTTP_CODE_OK: + { + char buffer[100]; + httpClient.readHttpBody((uint8_t *)buffer, 100); + #ifdef DEBUG_OTA_UPDATER + Serial.printf("Response from update service : %s\n", buffer); + #endif + + char *p = strstr_P(buffer, PSTR("bin_version")); + if(p) + { + p+=14; + char *end = strchr(p, '"'); + if(end) + { + *end = '\0'; + + ui.version = (char *)malloc(strlen(p) * sizeof(char) + 1); + if(ui.version) + strcpy(ui.version, p); + } + } + ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_AVAILABLE; + } + break; + case HttpConstants::HTTP_CODE::HTTP_CODE_NOT_MODIFIED: + ui.info = OTAUpdater::UpdateInfo::HTTP_NO_UPDATE; + break; + case HttpConstants::HTTP_CODE::HTTP_CODE_FORBIDDEN: + ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_AUTH_ERROR; + break; + case HttpConstants::HTTP_CODE::HTTP_CODE_NOT_FOUND: + ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR; + break; + default: + #ifdef DEBUG_OTA_UPDATER + Serial.printf("HTTP ERROR CODE IS : %u\n", result); + #endif + ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_UNDEFINED_ERROR; + } + + httpClient.stop(); + } + else + ui.info = OTAUpdater::UpdateInfo::HTTP_UPDATE_REACH_ERROR; + + return ui; +} + +#define DEBUG_OTA_UPDATER + +HTTPUpdateResult OTAUpdater::update(const char *softwareVersion) +{ + if(!softwareVersion) + { + #ifdef DEBUG_OTA_UPDATER + Serial.printf("Missing softwareVersion !\n"); + #endif + return HTTPUpdateResult::HTTP_UPDATE_NO_UPDATES; + } + + if(!_serverAddress) + { + #ifdef DEBUG_OTA_UPDATER + Serial.printf("Missing server address !\n"); + #endif + return HTTPUpdateResult::HTTP_UPDATE_FAILED; + } + + //Depending on the presence or not of the path and the authKey, we compute the size we must allocate for the full path + uint16_t length = 1 /*for the \0*/; + length += _path ? strlen(_path) : 0; + length += _ota_auth_key ? strlen(_ota_auth_key) + 9/*for : ?authKey=*/ : 0; + + char *fullpath = (char *)malloc(length * sizeof(char)); + if(!fullpath)return HTTP_UPDATE_FAILED; + + fullpath[0] = '\0'; + + WiFiClient client; + + if(_path)strcpy(fullpath, _path); + if(_ota_auth_key) + { + strcat(fullpath, "?authKey="); + strcat(fullpath, _ota_auth_key); + } + + #ifdef DEBUG_OTA_UPDATER + Serial.printf("The fullpath is : %s\n", fullpath); + #endif + + HTTPUpdateResult result = ESP8266HTTPUpdate::update(client, _serverAddress, _port, fullpath, softwareVersion); + + free(fullpath); + return result; +} diff --git a/src/app/OTAUpdater.h b/src/app/OTAUpdater.h new file mode 100644 index 0000000..9acd9e6 --- /dev/null +++ b/src/app/OTAUpdater.h @@ -0,0 +1,103 @@ +#ifndef OTAUPDATER_H +#define OTAUPDATER_H +#include + +#define OTAUPDATER_STRING //PSTR + +class OTAUpdater : public ESP8266HTTPUpdate +{ + public: + struct UpdateInfo + { + enum HTTPUpdateInfo { + HTTP_NO_UPDATE, //There is no new update available + HTTP_UPDATE_AVAILABLE, //There is an update ready to be applied + HTTP_UPDATE_AUTH_ERROR, //There was an error during the authentification process by the update service + HTTP_UPDATE_REACH_ERROR, //Unable to reach the update service + HTTP_UPDATE_UNDEFINED_ERROR + }; + + UpdateInfo(){} + UpdateInfo(const UpdateInfo &object) + { + info = object.info; + if(object.version) + { + version = (char *) malloc((strlen(object.version) + 1) * sizeof(char)); + if(version != NULL) + strcpy(version, object.version); + } + } + ~UpdateInfo(){ dispose(); } + UpdateInfo& operator=(const UpdateInfo &object) + { + info = object.info; + if(version) + { + free(version);version = NULL; + } + + if(object.version) + { + version = (char *) malloc((strlen(object.version) + 1) * sizeof(char)); + if(version != NULL) + strcpy(version, object.version); + } + return *this; + } + void dispose() + { + if(version) + { + free(version);version = NULL; + } + } + const char *HTTPUpdateInfoToString(void) + { + switch(info) + { + case HTTP_NO_UPDATE: + return OTAUPDATER_STRING("HTTP_NO_UPDATE"); + break; + case HTTP_UPDATE_AVAILABLE: + return OTAUPDATER_STRING("HTTP_UPDATE_AVAILABLE"); + break; + case HTTP_UPDATE_AUTH_ERROR: + return OTAUPDATER_STRING("HTTP_UPDATE_AUTH_ERROR"); + break; + case HTTP_UPDATE_REACH_ERROR: + return OTAUPDATER_STRING("HTTP_UPDATE_REACH_ERROR"); + break; + default: + return OTAUPDATER_STRING("HTTP_UPDATE_UNDEFINED_ERROR"); + break; + } + } + HTTPUpdateInfo info = HTTP_NO_UPDATE; + char *version = NULL; + }; + OTAUpdater(const char *serverAddress = NULL, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80); + OTAUpdater(int httpClientTimeout, const char *serverAddress, const char *path = NULL, const char *ota_auth_key = NULL, uint16_t port = 80); + + ~OTAUpdater(); + //Enables the binary signing verification + void enableSHA256UpdateVerification(const char *publicKey); + void setServerAddress(const char *serverAddress); + void setPath(const char *path = NULL); + void setOtaAuthKey(const char *ota_auth_key = NULL); + void setPort(uint16_t port = 80); + UpdateInfo fetchUpdateInfo(const char *softwareVersion, uint32_t timeout = 500); + HTTPUpdateResult update(const char *softwareVersion); + protected: + BearSSL::PublicKey *_publicKey = nullptr; + BearSSL::HashSHA256 *_hash = nullptr; + BearSSL::SigningVerifier *_signingVerifier = nullptr; + + char *_serverAddress = nullptr; + char *_path = nullptr; + char *_ota_auth_key = nullptr; + uint16_t _port; + private: +}; + +#endif //OTAUPDATER_H