Fork of the espurna firmware for `mhsw` switches
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.

149 lines
4.2 KiB

  1. /*
  2. Part of the WEBSERVER module
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  5. */
  6. #include "ota.h"
  7. #include "settings.h"
  8. #include "storage_eeprom.h"
  9. #include "utils.h"
  10. #include "web.h"
  11. #include "ws.h"
  12. #if WEB_SUPPORT && OTA_WEB_SUPPORT
  13. void _onUpgradeResponse(AsyncWebServerRequest *request, int code, const String& payload = "") {
  14. auto *response = request->beginResponseStream("text/plain", 256);
  15. response->addHeader("Connection", "close");
  16. response->addHeader("X-XSS-Protection", "1; mode=block");
  17. response->addHeader("X-Content-Type-Options", "nosniff");
  18. response->addHeader("X-Frame-Options", "deny");
  19. response->setCode(code);
  20. if (payload.length()) {
  21. response->printf("%s", payload.c_str());
  22. } else {
  23. if (!Update.hasError()) {
  24. response->print("OK");
  25. } else {
  26. #if defined(ARDUINO_ESP8266_RELEASE_2_3_0)
  27. Update.printError(reinterpret_cast<Stream&>(response));
  28. #else
  29. Update.printError(*response);
  30. #endif
  31. }
  32. }
  33. request->send(response);
  34. }
  35. void _onUpgradeStatusSet(AsyncWebServerRequest *request, int code, const String& payload = "") {
  36. _onUpgradeResponse(request, code, payload);
  37. request->_tempObject = malloc(sizeof(bool));
  38. }
  39. void _onUpgrade(AsyncWebServerRequest *request) {
  40. webLog(request);
  41. if (!webAuthenticate(request)) {
  42. return request->requestAuthentication(getSetting("hostname").c_str());
  43. }
  44. if (request->_tempObject) {
  45. return;
  46. }
  47. _onUpgradeResponse(request, 200);
  48. }
  49. void _onUpgradeFile(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  50. if (!webAuthenticate(request)) {
  51. return request->requestAuthentication(getSetting("hostname").c_str());
  52. }
  53. // We set this after we are done with the request
  54. // It is still possible to re-enter this callback even after connection is already closed
  55. // 1.14.2: TODO: see https://github.com/me-no-dev/ESPAsyncWebServer/pull/660
  56. // remote close or request sending some data before finishing parsing of the body will leak 1460 bytes
  57. // waiting a bit for upstream. fork and point to the fixed version if not resolved before 1.14.2
  58. if (request->_tempObject) {
  59. return;
  60. }
  61. if (!index) {
  62. // TODO: stop network activity completely when handling Update through ArduinoOTA or `ota` command?
  63. if (Update.isRunning()) {
  64. _onUpgradeStatusSet(request, 400, F("ERROR: Upgrade in progress"));
  65. return;
  66. }
  67. // Check that header is correct and there is more data before anything is written to the flash
  68. if (final || !len) {
  69. _onUpgradeStatusSet(request, 400, F("ERROR: Invalid request"));
  70. return;
  71. }
  72. if (!otaVerifyHeader(data, len)) {
  73. _onUpgradeStatusSet(request, 400, F("ERROR: No magic byte / invalid flash config"));
  74. return;
  75. }
  76. // Disabling EEPROM rotation to prevent writing to EEPROM after the upgrade
  77. eepromRotate(false);
  78. DEBUG_MSG_P(PSTR("[UPGRADE] Start: %s\n"), filename.c_str());
  79. Update.runAsync(true);
  80. // Note: cannot use request->contentLength() for multipart/form-data
  81. if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
  82. _onUpgradeStatusSet(request, 500);
  83. eepromRotate(true);
  84. return;
  85. }
  86. }
  87. if (request->_tempObject) {
  88. return;
  89. }
  90. // Any error will cancel the update, but request may still be alive
  91. if (!Update.isRunning()) {
  92. return;
  93. }
  94. if (Update.write(data, len) != len) {
  95. _onUpgradeStatusSet(request, 500);
  96. Update.end();
  97. eepromRotate(true);
  98. return;
  99. }
  100. if (final) {
  101. otaFinalize(index + len, CUSTOM_RESET_UPGRADE, true);
  102. } else {
  103. otaProgress(index + len);
  104. }
  105. }
  106. void otaWebSetup() {
  107. webServer()->on("/upgrade", HTTP_POST, _onUpgrade, _onUpgradeFile);
  108. wsRegister().
  109. onVisible([](JsonObject& root) {
  110. root["otaVisible"] = 1;
  111. });
  112. }
  113. #endif // OTA_WEB_SUPPORT