mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
md5 check for upload bin files #637
This commit is contained in:
@@ -35,7 +35,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
|
|||||||
onDrop,
|
onDrop,
|
||||||
accept: {
|
accept: {
|
||||||
'application/octet-stream': ['.bin'],
|
'application/octet-stream': ['.bin'],
|
||||||
'application/json': ['.json']
|
'application/json': ['.json'],
|
||||||
|
'text/plain': ['.md5']
|
||||||
},
|
},
|
||||||
disabled: uploading,
|
disabled: uploading,
|
||||||
multiple: false
|
multiple: false
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
const [uploading, setUploading] = useState<boolean>(false);
|
const [uploading, setUploading] = useState<boolean>(false);
|
||||||
|
const [md5, setMd5] = useState<string>('');
|
||||||
const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
|
const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
|
||||||
const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
|
const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
setUploading(false);
|
setUploading(false);
|
||||||
setUploadProgress(undefined);
|
setUploadProgress(undefined);
|
||||||
setUploadCancelToken(undefined);
|
setUploadCancelToken(undefined);
|
||||||
|
setMd5('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelUpload = useCallback(() => {
|
const cancelUpload = useCallback(() => {
|
||||||
@@ -41,12 +43,17 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
const cancelToken = axios.CancelToken.source();
|
const cancelToken = axios.CancelToken.source();
|
||||||
setUploadCancelToken(cancelToken);
|
setUploadCancelToken(cancelToken);
|
||||||
setUploading(true);
|
setUploading(true);
|
||||||
await upload(images[0], {
|
const response = await upload(images[0], {
|
||||||
onUploadProgress: setUploadProgress,
|
onUploadProgress: setUploadProgress,
|
||||||
cancelToken: cancelToken.token
|
cancelToken: cancelToken.token
|
||||||
});
|
});
|
||||||
resetUploadingStates();
|
resetUploadingStates();
|
||||||
|
if (response.status === 200) {
|
||||||
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
|
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
|
||||||
|
} else if (response.status === 201) {
|
||||||
|
setMd5((String)(response.data));
|
||||||
|
enqueueSnackbar(LL.UPLOAD() + ' MD5 ' + LL.SUCCESSFUL(), { variant: 'success' });
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isCancel(error)) {
|
if (axios.isCancel(error)) {
|
||||||
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.ABORTED(), { variant: 'warning' });
|
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.ABORTED(), { variant: 'warning' });
|
||||||
@@ -57,7 +64,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [uploadFile, cancelUpload, uploading, uploadProgress] as const;
|
return [uploadFile, cancelUpload, uploading, uploadProgress, md5] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useFileUpload;
|
export default useFileUpload;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AxiosPromise } from 'axios';
|
|||||||
import { Typography, Button, Box } from '@mui/material';
|
import { Typography, Button, Box } from '@mui/material';
|
||||||
|
|
||||||
import { FileUploadConfig } from '../../api/endpoints';
|
import { FileUploadConfig } from '../../api/endpoints';
|
||||||
|
|
||||||
import { SingleUpload, useFileUpload } from '../../components';
|
import { SingleUpload, useFileUpload } from '../../components';
|
||||||
|
|
||||||
import DownloadIcon from '@mui/icons-material/GetApp';
|
import DownloadIcon from '@mui/icons-material/GetApp';
|
||||||
@@ -21,7 +22,8 @@ interface UploadFileProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
||||||
const [uploadFile, cancelUpload, uploading, uploadProgress] = useFileUpload({ upload: uploadGeneralFile });
|
|
||||||
|
const [uploadFile, cancelUpload, uploading, uploadProgress, md5] = useFileUpload({ upload: uploadGeneralFile });
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
@@ -80,6 +82,11 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{md5 !== '' && (
|
||||||
|
<Box mb={2}>
|
||||||
|
<Typography variant="body2">{'MD5: ' + md5}</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} uploading={uploading} progress={uploadProgress} />
|
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} uploading={uploading} progress={uploadProgress} />
|
||||||
|
|
||||||
{!uploading && (
|
{!uploading && (
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ const UploadFileForm: FC = () => {
|
|||||||
|
|
||||||
const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => {
|
const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => {
|
||||||
const response = await SystemApi.uploadFile(file, config);
|
const response = await SystemApi.uploadFile(file, config);
|
||||||
|
if (response.status === 200) {
|
||||||
setRestarting(true);
|
setRestarting(true);
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const extractErrorMessage = (error: any, defaultMessage: string) => {
|
export const extractErrorMessage = (error: any, defaultMessage: string) => {
|
||||||
if (error.request) {
|
if (error.request) {
|
||||||
return defaultMessage + ' (' + error.request.statusText + ')';
|
return defaultMessage + ' (' + error.request.status + ': ' + error.request.statusText + ')';
|
||||||
} else if (error instanceof Error) {
|
} else if (error instanceof Error) {
|
||||||
return defaultMessage + ' (' + error.message + ')';
|
return defaultMessage + ' (' + error.message + ')';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using namespace std::placeholders; // for `_1` etc
|
using namespace std::placeholders; // for `_1` etc
|
||||||
|
|
||||||
static bool is_firmware = false;
|
static bool is_firmware = false;
|
||||||
|
static char md5[33] = "\0";
|
||||||
|
|
||||||
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||||
: _securityManager(securityManager) {
|
: _securityManager(securityManager) {
|
||||||
@@ -33,45 +34,53 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
is_firmware = false;
|
||||||
if ((extension == "bin") && (fsize > 1500000)) {
|
if ((extension == "bin") && (fsize > 1500000)) {
|
||||||
is_firmware = true;
|
is_firmware = true;
|
||||||
} else if (extension == "json") {
|
} else if (extension == "json") {
|
||||||
is_firmware = false;
|
md5[0] = '\0'; // clear md5
|
||||||
|
} else if (extension == "md5") {
|
||||||
|
if (len == 32) {
|
||||||
|
memcpy(md5, data, 32);
|
||||||
|
md5[32] = '\0';
|
||||||
|
}
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
is_firmware = false;
|
md5[0] = '\0';
|
||||||
return; // not support file type
|
return; // not support file type
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_firmware) {
|
if (is_firmware) {
|
||||||
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
|
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
|
||||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||||
bool isC3 = (fname.find("C3") != std::string::npos);
|
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
|
||||||
bool isS2 = (fname.find("S2") != std::string::npos);
|
|
||||||
if (isC3 || isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 0))) {
|
|
||||||
handleError(request, 503); // service unavailable
|
handleError(request, 503); // service unavailable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
bool isS2 = (fname.find("S2") != std::string::npos);
|
if (len > 12 && (data[0] != 0xE9 || data[12] != 2)) {
|
||||||
if (!isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 2))) {
|
|
||||||
handleError(request, 503); // service unavailable
|
handleError(request, 503); // service unavailable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
bool isC3 = (fname.find("C3") != std::string::npos);
|
if (len > 12 && (data[0] != 0xE9 || data[12] != 5)) {
|
||||||
if (!isC3 || (len > 12 && (data[0] != 0xE9 || data[12] != 5))) {
|
|
||||||
handleError(request, 503); // service unavailable
|
handleError(request, 503); // service unavailable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// it's firmware - initialize the ArduinoOTA updater
|
// it's firmware - initialize the ArduinoOTA updater
|
||||||
if (Update.begin(fsize)) {
|
if (Update.begin(fsize)) {
|
||||||
|
if (strlen(md5) == 32) {
|
||||||
|
Update.setMD5(md5);
|
||||||
|
md5[0] = '\0';
|
||||||
|
}
|
||||||
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
|
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
|
||||||
} else {
|
} else {
|
||||||
#if defined(EMSESP_USE_SERIAL)
|
#if defined(EMSESP_USE_SERIAL)
|
||||||
Update.printError(Serial);
|
Update.printError(Serial);
|
||||||
#endif
|
#endif
|
||||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// its a normal file, open a new temp file to write the contents too
|
// its a normal file, open a new temp file to write the contents too
|
||||||
@@ -83,7 +92,6 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
|||||||
if (len) {
|
if (len) {
|
||||||
request->_tempFile.write(data, len); // stream the incoming chunk to the opened file
|
request->_tempFile.write(data, len); // stream the incoming chunk to the opened file
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// if we haven't delt with an error, continue with the firmware update
|
// if we haven't delt with an error, continue with the firmware update
|
||||||
if (!request->_tempObject) {
|
if (!request->_tempObject) {
|
||||||
@@ -123,6 +131,11 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (strlen(md5) == 32) {
|
||||||
|
AsyncWebServerResponse * response = request->beginResponse(201, "text/plain", md5); // created
|
||||||
|
request->send(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handleError(request, 403); // send the forbidden response
|
handleError(request, 403); // send the forbidden response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import shutil
|
|||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
Import("env")
|
Import("env")
|
||||||
|
import hashlib
|
||||||
|
|
||||||
OUTPUT_DIR = "build{}".format(os.path.sep)
|
OUTPUT_DIR = "build{}".format(os.path.sep)
|
||||||
|
|
||||||
@@ -18,7 +19,6 @@ def bin_copy(source, target, env):
|
|||||||
bag[var] = m.group(1)
|
bag[var] = m.group(1)
|
||||||
|
|
||||||
app_version = bag.get('app_version')
|
app_version = bag.get('app_version')
|
||||||
|
|
||||||
platform = "ESP32"
|
platform = "ESP32"
|
||||||
chip_target = env.get('PIOENV').upper()
|
chip_target = env.get('PIOENV').upper()
|
||||||
|
|
||||||
@@ -33,14 +33,13 @@ def bin_copy(source, target, env):
|
|||||||
|
|
||||||
# alternatively take platform from the pio target
|
# alternatively take platform from the pio target
|
||||||
# platform = str(target[0]).split(os.path.sep)[2]
|
# platform = str(target[0]).split(os.path.sep)[2]
|
||||||
chip_target = env.get('PIOENV').upper()
|
|
||||||
|
|
||||||
print("app version: " + app_version)
|
print("app version: " + app_version)
|
||||||
print("platform: " + platform)
|
print("platform: " + platform)
|
||||||
print("chip_target: " + chip_target)
|
print("chip_target: " + chip_target)
|
||||||
|
|
||||||
# convert . to _ so Windows doesn't complain
|
# convert . to _ so Windows doesn't complain
|
||||||
variant = "EMS-ESP-" + chip_target + "-" + app_version.replace(".", "_")
|
variant = "EMS-ESP-" + app_version.replace(".", "_") + "-" + chip_target.replace("CI","ESP32")
|
||||||
|
|
||||||
# check if output directories exist and create if necessary
|
# check if output directories exist and create if necessary
|
||||||
if not os.path.isdir(OUTPUT_DIR):
|
if not os.path.isdir(OUTPUT_DIR):
|
||||||
@@ -52,15 +51,29 @@ def bin_copy(source, target, env):
|
|||||||
|
|
||||||
# create string with location and file names based on variant
|
# create string with location and file names based on variant
|
||||||
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
|
||||||
|
md5_file = "{}firmware{}{}.md5".format(OUTPUT_DIR, os.path.sep, variant)
|
||||||
|
|
||||||
# check if new target files exist and remove if necessary
|
# check if new target files exist and remove if necessary
|
||||||
for f in [bin_file]:
|
for f in [bin_file]:
|
||||||
if os.path.isfile(f):
|
if os.path.isfile(f):
|
||||||
os.remove(f)
|
os.remove(f)
|
||||||
|
|
||||||
|
# check if new target files exist and remove if necessary
|
||||||
|
for f in [md5_file]:
|
||||||
|
if os.path.isfile(f):
|
||||||
|
os.remove(f)
|
||||||
|
|
||||||
print("Renaming file to "+bin_file)
|
print("Renaming file to "+bin_file)
|
||||||
|
|
||||||
# copy firmware.bin to firmware/<variant>.bin
|
# copy firmware.bin to firmware/<variant>.bin
|
||||||
shutil.copy(str(target[0]), bin_file)
|
shutil.copy(str(target[0]), bin_file)
|
||||||
|
|
||||||
|
with open(bin_file,"rb") as f:
|
||||||
|
result = hashlib.md5(f.read())
|
||||||
|
print("Calculating MD5: "+result.hexdigest())
|
||||||
|
file1 = open(md5_file, 'w')
|
||||||
|
file1.write(result.hexdigest())
|
||||||
|
file1.close()
|
||||||
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_copy])
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_copy])
|
||||||
|
env.AddPostAction("$BUILD_DIR/${PROGNAME}.md5", [bin_copy])
|
||||||
|
|||||||
Reference in New Issue
Block a user