Skip to content

Commit fb8d6d6

Browse files
authored
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 60d2c77. * Revert "auto numbering of HTTPUpdateError enum" This reverts commit 61785b2. * Revert "added getAvailableVersion(), moved _httpClientTimeout and _followRedirects to protected, added enum HTTPUpdateError" This reverts commit cec84ed. * corrected incorrect merge with master
1 parent 30c6df4 commit fb8d6d6

File tree

3 files changed

+188
-45
lines changed

3 files changed

+188
-45
lines changed

doc/ota_updates/readme.rst

+62-32
Original file line numberDiff line numberDiff line change
@@ -592,34 +592,42 @@ With this information the script now can check if an update is needed. It is als
592592
593593
<?PHP
594594
595-
header('Content-type: text/plain; charset=utf8', true);
596-
597595
function check_header($name, $value = false) {
598-
if(!isset($_SERVER[$name])) {
596+
global $headers;
597+
if (!isset($headers[$name])) {
599598
return false;
600599
}
601-
if($value && $_SERVER[$name] != $value) {
600+
if ($value && $headers[$name] != $value) {
602601
return false;
603602
}
604603
return true;
605604
}
606-
607-
function sendFile($path) {
605+
606+
function sendFile($path, $version) {
608607
header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200);
609608
header('Content-Type: application/octet-stream', true);
610609
header('Content-Disposition: attachment; filename='.basename($path));
611610
header('Content-Length: '.filesize($path), true);
612611
header('x-MD5: '.md5_file($path), true);
612+
header('x-version: '.$version, true);
613613
readfile($path);
614614
}
615-
616-
if(!check_header('User-Agent', 'ESP8266-http-Update')) {
615+
616+
617+
$headers = getallheaders();
618+
619+
header('Content-type: text/plain; charset=utf8', true);
620+
621+
//if (!check_header('HTTP_USER_AGENT', 'ESP8266-http-Update')) {
622+
if (!check_header('User-Agent', 'ESP8266-http-Update')) {
617623
header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);
618-
echo "only for ESP8266 updater!\n";
624+
echo "Only for ESP8266 updater!\n";
625+
echo "User-Agent: ".$headers['User-Agent']." != ESP8266-http-Update\n";
619626
exit();
620627
}
621-
622-
if(
628+
629+
if (
630+
!check_header('x-ESP8266-mode') ||
623631
!check_header('x-ESP8266-STA-MAC') ||
624632
!check_header('x-ESP8266-AP-MAC') ||
625633
!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
629637
!check_header('x-ESP8266-sdk-version')
630638
) {
631639
header($_SERVER["SERVER_PROTOCOL"].' 403 Forbidden', true, 403);
632-
echo "only for ESP8266 updater! (header)\n";
640+
echo "Only for ESP8266 updater! (header missing)\n";
633641
exit();
634642
}
635-
636-
$db = array(
637-
"18:FE:AA:AA:AA:AA" => "DOOR-7-g14f53a19",
638-
"18:FE:AA:AA:AA:BB" => "TEMP-1.0.0"
639-
);
640-
641-
if(!isset($db[$_SERVER['x-ESP8266-STA-MAC']])) {
642-
header($_SERVER["SERVER_PROTOCOL"].' 500 ESP MAC not configured for updates', true, 500);
643+
644+
$db_string = '{
645+
"18:FE:AA:AA:AA:AA": {"file": "DOOR-7-g14f53a19.bin", "version": 1},
646+
"18:FE:AA:AA:AA:BB": {"file": "TEMP-1.0.0".bin", "version": 1}}';
647+
// $db_string = file_get_contents("arduino-db.json");
648+
$db = json_decode($db_string, true);
649+
$mode = $headers['x-ESP8266-mode'];
650+
$mac = $headers['x-ESP8266-STA-MAC'];
651+
652+
if (!isset($db[$mac])) {
653+
header($_SERVER["SERVER_PROTOCOL"].' 404 ESP MAC not configured for updates', true, 404);
654+
echo "MAC ".$mac." not configured for updates\n";
655+
exit();
643656
}
644-
645-
$localBinary = "./bin/".$db[$_SERVER['x-ESP8266-STA-MAC']].".bin";
646-
647-
// Check if version has been set and does not match, if not, check if
648-
// MD5 hash between local binary and ESP8266 binary do not match if not.
649-
// then no update has been found.
650-
if((!check_header('x-ESP8266-sdk-version') && $db[$_SERVER['x-ESP8266-STA-MAC']] != $_SERVER['x-ESP8266-version'])
651-
|| $_SERVER["x-ESP8266-sketch-md5"] != md5_file($localBinary)) {
652-
sendFile($localBinary);
657+
658+
$localBinary = $db[$mac]['file'];
659+
$localVersion = $db[$mac]['version'];
660+
661+
if (!is_readable($localBinary)) {
662+
header($_SERVER["SERVER_PROTOCOL"].' 404 File not found', true, 404);
663+
echo "File ".$localBinary." not found\n";
664+
exit();
665+
}
666+
667+
if ($mode == 'sketch') {
668+
// Check if version has been set and does not match, if not, check if
669+
// MD5 hash between local binary and ESP8266 binary do not match if not.
670+
// then no update has been found.
671+
if ((check_header('x-ESP8266-version') && $headers['x-ESP8266-version'] != $localVersion)) {
672+
// || $headers["x-ESP8266-sketch-md5"] != md5_file($localBinary)) {
673+
sendFile($localBinary, $localVersion);
674+
} else {
675+
header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304);
676+
echo "File ".$localBinary." not modified\n";
677+
}
678+
} else if ($mode == 'version') {
679+
header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200);
680+
header('x-MD5: '.md5_file($localBinary), true);
681+
header('x-version: '.$localVersion, true);
653682
} else {
654-
header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304);
683+
header($_SERVER["SERVER_PROTOCOL"].' 404 Mode not supported', true, 404);
684+
echo "mode: ".$mode." not supported\n";
685+
exit();
655686
}
656-
657-
header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500);
687+
?>
658688
659689
Stream Interface
660690
----------------

libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String&
235235
DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n");
236236
DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace());
237237
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize());
238+
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str());
238239

239240
if(currentVersion && currentVersion[0] != 0x00) {
240241
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,
440441
return true;
441442
}
442443

444+
/**
445+
* @brief Get avialable firmware version from update server
446+
* @author Holger Mueller
447+
* @date 2023-08-03
448+
*
449+
* @param client WiFiClient to use (see HTTPClient::begin)
450+
* @param host Update host name or IP (see HTTPClient::begin)
451+
* @param port Port on host (see HTTPClient::begin)
452+
* @param uri Update URI on server (see HTTPClient::begin)
453+
* @param current_version Current firmware version
454+
* @param available_version Firmware version available on update server
455+
* @return ESP8266HTTPUpdate::HTTPUpdateResult, HTTP_UPDATE_OK in case of success
456+
*/
457+
HTTPUpdateResult ESP8266HTTPUpdate::getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version) {
458+
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
459+
HTTPClient http;
460+
http.begin(client, host, port, uri);
461+
462+
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
463+
http.useHTTP10(true);
464+
http.setTimeout(_httpClientTimeout);
465+
http.setFollowRedirects(_followRedirects);
466+
http.setUserAgent(F("ESP8266-http-Update"));
467+
http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId()));
468+
http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
469+
http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
470+
http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
471+
http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize()));
472+
http.addHeader(F("x-ESP8266-sketch-md5"), String(ESP.getSketchMD5()));
473+
http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize()));
474+
http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion());
475+
476+
http.addHeader(F("x-ESP8266-mode"), F("version"));
477+
478+
if (current_version && current_version[0] != 0x00) {
479+
http.addHeader(F("x-ESP8266-version"), current_version);
480+
}
481+
482+
if (!_user.isEmpty() && !_password.isEmpty()) {
483+
http.setAuthorization(_user.c_str(), _password.c_str());
484+
}
485+
486+
if (!_auth.isEmpty()) {
487+
http.setAuthorization(_auth.c_str());
488+
}
489+
490+
const char* headerkeys[] = {"x-MD5", "x-version"};
491+
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
492+
493+
// track these headers
494+
http.collectHeaders(headerkeys, headerkeyssize);
495+
496+
int code = http.GET();
497+
498+
if (code <= 0) {
499+
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
500+
_setLastError(code);
501+
http.end();
502+
return HTTP_UPDATE_FAILED;
503+
}
504+
505+
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
506+
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
507+
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
508+
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", http.getSize());
509+
if (code != HTTP_CODE_OK) {
510+
DEBUG_HTTP_UPDATE("[httpUpdate] - payload: %s\n", http.getString().c_str());
511+
}
512+
513+
switch (code) {
514+
case HTTP_CODE_OK: ///< OK (check for version)
515+
if (http.hasHeader("x-version")) {
516+
available_version = http.header("x-version");
517+
ret = HTTP_UPDATE_OK;
518+
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version.c_str());
519+
DEBUG_HTTP_UPDATE("[httpUpdate] - server version: %s\n", available_version.c_str());
520+
} else {
521+
_setLastError(HTTP_UE_SERVER_NOT_REPORT_VERSION);
522+
ret = HTTP_UPDATE_FAILED;
523+
DEBUG_HTTP_UPDATE("[httpUpdate] Server did not respond with a firmware version\n");
524+
}
525+
if (http.hasHeader("x-MD5")) {
526+
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str());
527+
DEBUG_HTTP_UPDATE("[httpUpdate] - server Sketch MD5: %s\n", http.header("x-MD5").c_str());
528+
}
529+
break;
530+
case HTTP_CODE_NOT_FOUND:
531+
_setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND);
532+
ret = HTTP_UPDATE_FAILED;
533+
break;
534+
case HTTP_CODE_FORBIDDEN:
535+
_setLastError(HTTP_UE_SERVER_FORBIDDEN);
536+
ret = HTTP_UPDATE_FAILED;
537+
break;
538+
case HTTP_CODE_UNAUTHORIZED:
539+
_setLastError(HTTP_UE_SERVER_UNAUTHORIZED);
540+
ret = HTTP_UPDATE_FAILED;
541+
break;
542+
default:
543+
_setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE);
544+
ret = HTTP_UPDATE_FAILED;
545+
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
546+
break;
547+
}
548+
549+
http.end();
550+
return ret;
551+
}
552+
443553
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
444554
ESP8266HTTPUpdate ESPhttpUpdate;
445555
#endif

libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h

+16-13
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,18 @@
4343
#endif
4444

4545
/// note we use HTTP client errors too so we start at 100
46-
//TODO - in v3.0.0 make this an enum
47-
constexpr int HTTP_UE_TOO_LESS_SPACE = (-100);
48-
constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101);
49-
constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102);
50-
constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103);
51-
constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104);
52-
constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105);
53-
constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106);
54-
constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107);
55-
constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108);
46+
enum HTTPUpdateError {
47+
HTTP_UE_SERVER_NOT_REPORT_VERSION = -109, // server did not respond with a firmware version
48+
HTTP_UE_SERVER_UNAUTHORIZED, // -108
49+
HTTP_UE_BIN_FOR_WRONG_FLASH, // -107
50+
HTTP_UE_BIN_VERIFY_HEADER_FAILED, // -106
51+
HTTP_UE_SERVER_FAULTY_MD5, // -105
52+
HTTP_UE_SERVER_WRONG_HTTP_CODE, // -104
53+
HTTP_UE_SERVER_FORBIDDEN, // -103
54+
HTTP_UE_SERVER_FILE_NOT_FOUND, // -102
55+
HTTP_UE_SERVER_NOT_REPORT_SIZE, // -101
56+
HTTP_UE_TOO_LESS_SPACE // -100
57+
};
5658

5759
enum HTTPUpdateResult {
5860
HTTP_UPDATE_FAILED,
@@ -122,7 +124,9 @@ class ESP8266HTTPUpdate
122124
t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = "");
123125
t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = "");
124126
t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = "");
125-
127+
128+
t_httpUpdate_return getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version);
129+
126130
// Notification callbacks
127131
void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; }
128132
void onEnd(HTTPUpdateEndCB cbOnEnd) { _cbEnd = cbOnEnd; }
@@ -153,10 +157,9 @@ class ESP8266HTTPUpdate
153157
String _password;
154158
String _auth;
155159
String _md5Sum;
156-
private:
157160
int _httpClientTimeout;
158161
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
159-
162+
private:
160163
// Callbacks
161164
HTTPUpdateStartCB _cbStart;
162165
HTTPUpdateEndCB _cbEnd;

0 commit comments

Comments
 (0)