md5 check for upload bin files #637

This commit is contained in:
MichaelDvP
2022-10-31 11:08:05 +01:00
parent c65005e5a6
commit e0e07a9deb
7 changed files with 63 additions and 20 deletions

View File

@@ -35,7 +35,8 @@ const SingleUpload: FC<SingleUploadProps> = ({ onDrop, onCancel, uploading, prog
onDrop,
accept: {
'application/octet-stream': ['.bin'],
'application/json': ['.json']
'application/json': ['.json'],
'text/plain': ['.md5']
},
disabled: uploading,
multiple: false

View File

@@ -16,6 +16,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
const { enqueueSnackbar } = useSnackbar();
const [uploading, setUploading] = useState<boolean>(false);
const [md5, setMd5] = useState<string>('');
const [uploadProgress, setUploadProgress] = useState<AxiosProgressEvent>();
const [uploadCancelToken, setUploadCancelToken] = useState<CancelTokenSource>();
@@ -23,6 +24,7 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
setUploading(false);
setUploadProgress(undefined);
setUploadCancelToken(undefined);
setMd5('');
};
const cancelUpload = useCallback(() => {
@@ -41,12 +43,17 @@ const useFileUpload = ({ upload }: MediaUploadOptions) => {
const cancelToken = axios.CancelToken.source();
setUploadCancelToken(cancelToken);
setUploading(true);
await upload(images[0], {
const response = await upload(images[0], {
onUploadProgress: setUploadProgress,
cancelToken: cancelToken.token
});
resetUploadingStates();
enqueueSnackbar(LL.UPLOAD() + ' ' + LL.SUCCESSFUL(), { variant: 'success' });
if (response.status === 200) {
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) {
if (axios.isCancel(error)) {
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;

View File

@@ -4,6 +4,7 @@ import { AxiosPromise } from 'axios';
import { Typography, Button, Box } from '@mui/material';
import { FileUploadConfig } from '../../api/endpoints';
import { SingleUpload, useFileUpload } from '../../components';
import DownloadIcon from '@mui/icons-material/GetApp';
@@ -21,7 +22,8 @@ interface UploadFileProps {
}
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();
@@ -80,6 +82,11 @@ const GeneralFileUpload: FC<UploadFileProps> = ({ uploadGeneralFile }) => {
</Box>
</>
)}
{md5 !== '' && (
<Box mb={2}>
<Typography variant="body2">{'MD5: ' + md5}</Typography>
</Box>
)}
<SingleUpload onDrop={uploadFile} onCancel={cancelUpload} uploading={uploading} progress={uploadProgress} />
{!uploading && (

View File

@@ -16,7 +16,9 @@ const UploadFileForm: FC = () => {
const uploadFile = useRef(async (file: File, config?: FileUploadConfig) => {
const response = await SystemApi.uploadFile(file, config);
setRestarting(true);
if (response.status === 200) {
setRestarting(true);
}
return response;
});

View File

@@ -1,6 +1,6 @@
export const extractErrorMessage = (error: any, defaultMessage: string) => {
if (error.request) {
return defaultMessage + ' (' + error.request.statusText + ')';
return defaultMessage + ' (' + error.request.status + ': ' + error.request.statusText + ')';
} else if (error instanceof Error) {
return defaultMessage + ' (' + error.message + ')';
}

View File

@@ -3,6 +3,7 @@
using namespace std::placeholders; // for `_1` etc
static bool is_firmware = false;
static char md5[33] = "\0";
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
: _securityManager(securityManager) {
@@ -33,45 +34,53 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
Serial.println();
#endif
is_firmware = false;
if ((extension == "bin") && (fsize > 1500000)) {
is_firmware = true;
} 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 {
is_firmware = false;
md5[0] = '\0';
return; // not support file type
}
if (is_firmware) {
// 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
bool isC3 = (fname.find("C3") != std::string::npos);
bool isS2 = (fname.find("S2") != std::string::npos);
if (isC3 || isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 0))) {
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
handleError(request, 503); // service unavailable
return;
}
#elif CONFIG_IDF_TARGET_ESP32S2
bool isS2 = (fname.find("S2") != std::string::npos);
if (!isS2 || (len > 12 && (data[0] != 0xE9 || data[12] != 2))) {
if (len > 12 && (data[0] != 0xE9 || data[12] != 2)) {
handleError(request, 503); // service unavailable
return;
}
#elif CONFIG_IDF_TARGET_ESP32C3
bool isC3 = (fname.find("C3") != std::string::npos);
if (!isC3 || (len > 12 && (data[0] != 0xE9 || data[12] != 5))) {
if (len > 12 && (data[0] != 0xE9 || data[12] != 5)) {
handleError(request, 503); // service unavailable
return;
}
#endif
// it's firmware - initialize the ArduinoOTA updater
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
} else {
#if defined(EMSESP_USE_SERIAL)
Update.printError(Serial);
#endif
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
return;
}
} else {
// 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) {
request->_tempFile.write(data, len); // stream the incoming chunk to the opened file
}
} else {
// if we haven't delt with an error, continue with the firmware update
if (!request->_tempObject) {
@@ -123,6 +131,11 @@ void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
request->send(response);
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
}

View File

@@ -2,6 +2,7 @@ import shutil
import re
import os
Import("env")
import hashlib
OUTPUT_DIR = "build{}".format(os.path.sep)
@@ -18,7 +19,6 @@ def bin_copy(source, target, env):
bag[var] = m.group(1)
app_version = bag.get('app_version')
platform = "ESP32"
chip_target = env.get('PIOENV').upper()
@@ -33,14 +33,13 @@ def bin_copy(source, target, env):
# alternatively take platform from the pio target
# platform = str(target[0]).split(os.path.sep)[2]
chip_target = env.get('PIOENV').upper()
print("app version: " + app_version)
print("platform: " + platform)
print("chip_target: " + chip_target)
# 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
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
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
for f in [bin_file]:
if os.path.isfile(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)
# copy firmware.bin to firmware/<variant>.bin
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}.md5", [bin_copy])