1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

ESP8266HTTPUpdate: Get available firmware version from update server (#8968)

* added getAvailableVersion(), moved _httpClientTimeout and _followRedirects to protected, added enum HTTPUpdateError

* auto numbering of HTTPUpdateError enum

* added getAvailableVersion(), debug output current current Sketch MD5

* updated advanced updater php script

* switch case indention corrected

* Revert "added getAvailableVersion(), debug output current current Sketch MD5"

This reverts commit 60d2c7762e7fb1fed7fae37fa99be149e12f125c.

* Revert "auto numbering of HTTPUpdateError enum"

This reverts commit 61785b27da3f2d42f8f95316d78ce22e5b00103a.

* Revert "added getAvailableVersion(), moved _httpClientTimeout and _followRedirects to protected, added enum HTTPUpdateError"

This reverts commit cec84ed17ab149d3e48082293f9e2723246b7d0b.

* corrected incorrect merge with master
This commit is contained in:
Holger Müller 2023-11-05 00:01:49 +01:00 committed by GitHub
parent 30c6df4639
commit fb8d6d668d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 188 additions and 45 deletions

View File

@ -592,34 +592,42 @@ With this information the script now can check if an update is needed. It is als
<?PHP <?PHP
header('Content-type: text/plain; charset=utf8', true);
function check_header($name, $value = false) { function check_header($name, $value = false) {
if(!isset($_SERVER[$name])) { global $headers;
if (!isset($headers[$name])) {
return false; return false;
} }
if($value && $_SERVER[$name] != $value) { if ($value && $headers[$name] != $value) {
return false; return false;
} }
return true; return true;
} }
function sendFile($path) { function sendFile($path, $version) {
header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200); header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200);
header('Content-Type: application/octet-stream', true); header('Content-Type: application/octet-stream', true);
header('Content-Disposition: attachment; filename='.basename($path)); header('Content-Disposition: attachment; filename='.basename($path));
header('Content-Length: '.filesize($path), true); header('Content-Length: '.filesize($path), true);
header('x-MD5: '.md5_file($path), true); header('x-MD5: '.md5_file($path), true);
header('x-version: '.$version, true);
readfile($path); readfile($path);
} }
if(!check_header('User-Agent', 'ESP8266-http-Update')) {
$headers = getallheaders();
header('Content-type: text/plain; charset=utf8', true);
//if (!check_header('HTTP_USER_AGENT', 'ESP8266-http-Update')) {
if (!check_header('User-Agent', 'ESP8266-http-Update')) {
header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403); header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);
echo "only for ESP8266 updater!\n"; echo "Only for ESP8266 updater!\n";
echo "User-Agent: ".$headers['User-Agent']." != ESP8266-http-Update\n";
exit(); exit();
} }
if( if (
!check_header('x-ESP8266-mode') ||
!check_header('x-ESP8266-STA-MAC') || !check_header('x-ESP8266-STA-MAC') ||
!check_header('x-ESP8266-AP-MAC') || !check_header('x-ESP8266-AP-MAC') ||
!check_header('x-ESP8266-free-space') || !check_header('x-ESP8266-free-space') ||
@ -629,32 +637,54 @@ With this information the script now can check if an update is needed. It is als
!check_header('x-ESP8266-sdk-version') !check_header('x-ESP8266-sdk-version')
) { ) {
header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403); header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);
echo "only for ESP8266 updater! (header)\n"; echo "Only for ESP8266 updater! (header missing)\n";
exit(); exit();
} }
$db = array( $db_string = '{
"18:FE:AA:AA:AA:AA" => "DOOR-7-g14f53a19", "18:FE:AA:AA:AA:AA": {"file": "DOOR-7-g14f53a19.bin", "version": 1},
"18:FE:AA:AA:AA:BB" => "TEMP-1.0.0" "18:FE:AA:AA:AA:BB": {"file": "TEMP-1.0.0".bin", "version": 1}}';
); // $db_string = file_get_contents("arduino-db.json");
$db = json_decode($db_string, true);
if(!isset($db[$_SERVER['x-ESP8266-STA-MAC']])) { $mode = $headers['x-ESP8266-mode'];
header($_SERVER["SERVER_PROTOCOL"].' 500 ESP MAC not configured for updates', true, 500); $mac = $headers['x-ESP8266-STA-MAC'];
if (!isset($db[$mac])) {
header($_SERVER["SERVER_PROTOCOL"].' 404 ESP MAC not configured for updates', true, 404);
echo "MAC ".$mac." not configured for updates\n";
exit();
} }
$localBinary = "./bin/".$db[$_SERVER['x-ESP8266-STA-MAC']].".bin"; $localBinary = $db[$mac]['file'];
$localVersion = $db[$mac]['version'];
// Check if version has been set and does not match, if not, check if
// MD5 hash between local binary and ESP8266 binary do not match if not. if (!is_readable($localBinary)) {
// then no update has been found. header($_SERVER["SERVER_PROTOCOL"].' 404 File not found', true, 404);
if((!check_header('x-ESP8266-sdk-version') && $db[$_SERVER['x-ESP8266-STA-MAC']] != $_SERVER['x-ESP8266-version']) echo "File ".$localBinary." not found\n";
|| $_SERVER["x-ESP8266-sketch-md5"] != md5_file($localBinary)) { exit();
sendFile($localBinary); }
if ($mode == 'sketch') {
// Check if version has been set and does not match, if not, check if
// MD5 hash between local binary and ESP8266 binary do not match if not.
// then no update has been found.
if ((check_header('x-ESP8266-version') && $headers['x-ESP8266-version'] != $localVersion)) {
// || $headers["x-ESP8266-sketch-md5"] != md5_file($localBinary)) {
sendFile($localBinary, $localVersion);
} else {
header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304);
echo "File ".$localBinary." not modified\n";
}
} else if ($mode == 'version') {
header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200);
header('x-MD5: '.md5_file($localBinary), true);
header('x-version: '.$localVersion, true);
} else { } else {
header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304); header($_SERVER["SERVER_PROTOCOL"].' 404 Mode not supported', true, 404);
echo "mode: ".$mode." not supported\n";
exit();
} }
?>
header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500);
Stream Interface Stream Interface
---------------- ----------------

View File

@ -235,6 +235,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n");
DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace());
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize());
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str());
if(currentVersion && currentVersion[0] != 0x00) { if(currentVersion && currentVersion[0] != 0x00) {
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() ); DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() );
@ -440,6 +441,115 @@ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, const String& md5,
return true; return true;
} }
/**
* @brief Get avialable firmware version from update server
* @author Holger Mueller
* @date 2023-08-03
*
* @param client WiFiClient to use (see HTTPClient::begin)
* @param host Update host name or IP (see HTTPClient::begin)
* @param port Port on host (see HTTPClient::begin)
* @param uri Update URI on server (see HTTPClient::begin)
* @param current_version Current firmware version
* @param available_version Firmware version available on update server
* @return ESP8266HTTPUpdate::HTTPUpdateResult, HTTP_UPDATE_OK in case of success
*/
HTTPUpdateResult ESP8266HTTPUpdate::getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version) {
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
HTTPClient http;
http.begin(client, host, port, uri);
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
http.useHTTP10(true);
http.setTimeout(_httpClientTimeout);
http.setFollowRedirects(_followRedirects);
http.setUserAgent(F("ESP8266-http-Update"));
http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId()));
http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize()));
http.addHeader(F("x-ESP8266-sketch-md5"), String(ESP.getSketchMD5()));
http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize()));
http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion());
http.addHeader(F("x-ESP8266-mode"), F("version"));
if (current_version && current_version[0] != 0x00) {
http.addHeader(F("x-ESP8266-version"), current_version);
}
if (!_user.isEmpty() && !_password.isEmpty()) {
http.setAuthorization(_user.c_str(), _password.c_str());
}
if (!_auth.isEmpty()) {
http.setAuthorization(_auth.c_str());
}
const char* headerkeys[] = {"x-MD5", "x-version"};
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
// track these headers
http.collectHeaders(headerkeys, headerkeyssize);
int code = http.GET();
if (code <= 0) {
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
_setLastError(code);
http.end();
return HTTP_UPDATE_FAILED;
}
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", http.getSize());
if (code != HTTP_CODE_OK) {
DEBUG_HTTP_UPDATE("[httpUpdate] - payload: %s\n", http.getString().c_str());
}
switch (code) {
case HTTP_CODE_OK: ///< OK (check for version)
if (http.hasHeader("x-version")) {
available_version = http.header("x-version");
ret = HTTP_UPDATE_OK;
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version.c_str());
DEBUG_HTTP_UPDATE("[httpUpdate] - server version: %s\n", available_version.c_str());
} else {
_setLastError(HTTP_UE_SERVER_NOT_REPORT_VERSION);
ret = HTTP_UPDATE_FAILED;
DEBUG_HTTP_UPDATE("[httpUpdate] Server did not respond with a firmware version\n");
}
if (http.hasHeader("x-MD5")) {
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str());
DEBUG_HTTP_UPDATE("[httpUpdate] - server Sketch MD5: %s\n", http.header("x-MD5").c_str());
}
break;
case HTTP_CODE_NOT_FOUND:
_setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND);
ret = HTTP_UPDATE_FAILED;
break;
case HTTP_CODE_FORBIDDEN:
_setLastError(HTTP_UE_SERVER_FORBIDDEN);
ret = HTTP_UPDATE_FAILED;
break;
case HTTP_CODE_UNAUTHORIZED:
_setLastError(HTTP_UE_SERVER_UNAUTHORIZED);
ret = HTTP_UPDATE_FAILED;
break;
default:
_setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE);
ret = HTTP_UPDATE_FAILED;
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
break;
}
http.end();
return ret;
}
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE) #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
ESP8266HTTPUpdate ESPhttpUpdate; ESP8266HTTPUpdate ESPhttpUpdate;
#endif #endif

View File

@ -43,16 +43,18 @@
#endif #endif
/// note we use HTTP client errors too so we start at 100 /// note we use HTTP client errors too so we start at 100
//TODO - in v3.0.0 make this an enum enum HTTPUpdateError {
constexpr int HTTP_UE_TOO_LESS_SPACE = (-100); HTTP_UE_SERVER_NOT_REPORT_VERSION = -109, // server did not respond with a firmware version
constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101); HTTP_UE_SERVER_UNAUTHORIZED, // -108
constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102); HTTP_UE_BIN_FOR_WRONG_FLASH, // -107
constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103); HTTP_UE_BIN_VERIFY_HEADER_FAILED, // -106
constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104); HTTP_UE_SERVER_FAULTY_MD5, // -105
constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105); HTTP_UE_SERVER_WRONG_HTTP_CODE, // -104
constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106); HTTP_UE_SERVER_FORBIDDEN, // -103
constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107); HTTP_UE_SERVER_FILE_NOT_FOUND, // -102
constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108); HTTP_UE_SERVER_NOT_REPORT_SIZE, // -101
HTTP_UE_TOO_LESS_SPACE // -100
};
enum HTTPUpdateResult { enum HTTPUpdateResult {
HTTP_UPDATE_FAILED, HTTP_UPDATE_FAILED,
@ -122,7 +124,9 @@ public:
t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = ""); t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = "");
t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = ""); t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = "");
t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = ""); t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = "");
t_httpUpdate_return getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version);
// Notification callbacks // Notification callbacks
void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; } void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; }
void onEnd(HTTPUpdateEndCB cbOnEnd) { _cbEnd = cbOnEnd; } void onEnd(HTTPUpdateEndCB cbOnEnd) { _cbEnd = cbOnEnd; }
@ -153,10 +157,9 @@ protected:
String _password; String _password;
String _auth; String _auth;
String _md5Sum; String _md5Sum;
private:
int _httpClientTimeout; int _httpClientTimeout;
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
private:
// Callbacks // Callbacks
HTTPUpdateStartCB _cbStart; HTTPUpdateStartCB _cbStart;
HTTPUpdateEndCB _cbEnd; HTTPUpdateEndCB _cbEnd;