mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 14:47:37 +03:00
avoid overwriting on filename conflicts
This commit is contained in:
parent
687b0b5ac0
commit
6473689e3e
@ -4,6 +4,7 @@
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "fontIds.h"
|
||||
#include "util/StringUtils.h"
|
||||
|
||||
#define DEVICE_NAME "EPaper"
|
||||
#define SERVICE_UUID "4ae29d01-499a-480a-8c41-a82192105125"
|
||||
@ -11,6 +12,7 @@
|
||||
#define RESPONSE_CHARACTERISTIC_UUID "0c656023-dee6-47c5-9afb-e601dfbdaa1d"
|
||||
|
||||
#define OUTPUT_DIRECTORY "/bt"
|
||||
#define MAX_FILENAME_LENGTH 200
|
||||
|
||||
#define PROTOCOL_ASSERT(cond, fmt, ...) \
|
||||
do { \
|
||||
@ -181,7 +183,7 @@ void BluetoothActivity::render() const {
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, 75, stateText.c_str());
|
||||
|
||||
if (state == STATE_OFFERED || state == STATE_RECEIVING || state == STATE_DONE) {
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 110, filename);
|
||||
renderer.drawCenteredText(UI_12_FONT_ID, 110, filename.c_str());
|
||||
} else if (state == STATE_ERROR) {
|
||||
renderer.drawCenteredText(UI_10_FONT_ID, 110, errorMessage);
|
||||
}
|
||||
@ -249,25 +251,30 @@ void BluetoothActivity::onRequest(lfbt_message* msg, size_t msg_len) {
|
||||
|
||||
totalBytes = msg->body.clientOffer.bodyLength;
|
||||
|
||||
size_t filenameLen = msg_len - 8 - sizeof(lfbt_msg_client_offer);
|
||||
if (filenameLen > MAX_FILENAME) {
|
||||
filenameLen = MAX_FILENAME;
|
||||
}
|
||||
|
||||
memcpy(filename, msg->body.clientOffer.name, filenameLen);
|
||||
filename[filenameLen] = 0;
|
||||
|
||||
// sanitize filename
|
||||
for (char *p = filename; *p; p++) {
|
||||
if (*p == '/' || *p == '\\' || *p == ':') {
|
||||
*p = '_';
|
||||
}
|
||||
}
|
||||
size_t filenameLength = msg_len - 8 - sizeof(lfbt_msg_client_offer);
|
||||
std::string originalFilename = StringUtils::sanitizeFilename(
|
||||
std::string(msg->body.clientOffer.name, filenameLength),
|
||||
MAX_FILENAME_LENGTH
|
||||
);
|
||||
|
||||
PROTOCOL_ASSERT(SdMan.ensureDirectoryExists(OUTPUT_DIRECTORY), "Couldn't create output directory %s", OUTPUT_DIRECTORY);
|
||||
char filepath[MAX_FILENAME + strlen(OUTPUT_DIRECTORY) + 2];
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", OUTPUT_DIRECTORY, filename);
|
||||
// TODO: we could check if file already exists and append a number to filename to avoid overwriting
|
||||
|
||||
// generate unique filepath
|
||||
auto splitName = StringUtils::splitFileName(originalFilename);
|
||||
filename = originalFilename;
|
||||
std::string filepath = OUTPUT_DIRECTORY "/" + filename;
|
||||
uint32_t duplicateIndex = 0;
|
||||
while (SdMan.exists(filepath.c_str())) {
|
||||
duplicateIndex++;
|
||||
if (splitName.second.empty()) {
|
||||
// no extension
|
||||
filename = splitName.first + "-" + std::to_string(duplicateIndex);
|
||||
} else {
|
||||
filename = splitName.first + "-" + std::to_string(duplicateIndex) + splitName.second;
|
||||
}
|
||||
filepath = OUTPUT_DIRECTORY "/" + filename;
|
||||
}
|
||||
|
||||
PROTOCOL_ASSERT(SdMan.openFileForWrite("BT", filepath, file), "Couldn't open file %s for writing", filepath);
|
||||
// TODO: would be neat to check if we have enough space, but SDCardManager doesn't seem to expose that info currently
|
||||
|
||||
|
||||
@ -13,8 +13,6 @@
|
||||
|
||||
#include "../Activity.h"
|
||||
|
||||
#define MAX_FILENAME 200
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t version;
|
||||
uint32_t bodyLength;
|
||||
@ -104,7 +102,7 @@ class BluetoothActivity final : public Activity {
|
||||
} State;
|
||||
|
||||
State state = STATE_INITIALIZING;
|
||||
char filename[MAX_FILENAME + 1];
|
||||
std::string filename;
|
||||
FsFile file;
|
||||
size_t receivedBytes = 0;
|
||||
size_t totalBytes = 0;
|
||||
|
||||
@ -61,6 +61,14 @@ bool checkFileExtension(const String& fileName, const char* extension) {
|
||||
return localFile.endsWith(localExtension);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> splitFileName(const std::string& name) {
|
||||
size_t lastDot = name.find_last_of('.');
|
||||
if (lastDot == std::string::npos) {
|
||||
return std::make_pair(name, "");
|
||||
}
|
||||
return std::make_pair(name.substr(0, lastDot), name.substr(lastDot));
|
||||
}
|
||||
|
||||
size_t utf8RemoveLastChar(std::string& str) {
|
||||
if (str.empty()) return 0;
|
||||
size_t pos = str.size() - 1;
|
||||
|
||||
@ -19,6 +19,12 @@ std::string sanitizeFilename(const std::string& name, size_t maxLength = 100);
|
||||
bool checkFileExtension(const std::string& fileName, const char* extension);
|
||||
bool checkFileExtension(const String& fileName, const char* extension);
|
||||
|
||||
/**
|
||||
* Split a filename into base name and extension.
|
||||
* If there is no extension, the second element of the pair will be an empty string.
|
||||
*/
|
||||
std::pair<std::string, std::string> splitFileName(const std::string& name);
|
||||
|
||||
// UTF-8 safe string truncation - removes one character from the end
|
||||
// Returns the new size after removing one UTF-8 character
|
||||
size_t utf8RemoveLastChar(std::string& str);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user