Correctly handle cases where files already exist on the filesystem.
Also: patched a bug wherein the filename filter was acting up, transforming `file.zip` to `file-zip.zip`, for example.
This commit is contained in:
@@ -44,5 +44,6 @@ u32 compress_lz_file_fast(FS_Path path, FS_Archive archive, char *in_buf, u32 si
|
||||
|
||||
Result buf_to_file(u32 size, FS_Path path, FS_Archive archive, char *buf);
|
||||
void remake_file(FS_Path path, FS_Archive archive, u32 size);
|
||||
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -391,14 +391,7 @@ bool init_qr(void)
|
||||
|
||||
if(mode != MODE_AMOUNT)
|
||||
{
|
||||
char path_to_file[0x107] = {0};
|
||||
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
|
||||
char * extension = strrchr(path_to_file, '.');
|
||||
if (extension == NULL || strcmp(extension, ".zip"))
|
||||
strcat(path_to_file, ".zip");
|
||||
|
||||
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
|
||||
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
|
||||
save_zip_to_sd(filename, zip_size, zip_buf, mode);
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
|
||||
114
source/fs.c
114
source/fs.c
@@ -27,6 +27,7 @@
|
||||
#include <strings.h>
|
||||
|
||||
#include "fs.h"
|
||||
#include "draw.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include <archive.h>
|
||||
@@ -351,3 +352,116 @@ void remake_file(FS_Path path, FS_Archive archive, u32 size)
|
||||
buf_to_file(size, path, archive, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
#define ILLEGAL_CHARS "><\"?;:/\\+,.|[=]"
|
||||
static SwkbdCallbackResult fat32filter(void *user, const char **ppMessage, const char *text, size_t textlen)
|
||||
{
|
||||
(void)textlen;
|
||||
(void)user;
|
||||
*ppMessage = "Input must not contain:\n" ILLEGAL_CHARS;
|
||||
if(strpbrk(text, ILLEGAL_CHARS))
|
||||
{
|
||||
DEBUG("illegal filename: %s\n", text);
|
||||
return SWKBD_CALLBACK_CONTINUE;
|
||||
}
|
||||
|
||||
return SWKBD_CALLBACK_OK;
|
||||
}
|
||||
|
||||
// assumes the input buffer is a ZIP. if it isn't, why are you calling this?
|
||||
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode)
|
||||
{
|
||||
static char path_to_file[32761]; // FAT32 paths can be quite long.
|
||||
const int max_chars = 250;
|
||||
char new_filename[max_chars + 5]; // .zip + \0
|
||||
renamed:
|
||||
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
|
||||
|
||||
// filter out characters illegal in FAT32 filenames
|
||||
char * curr_filename = strrchr(path_to_file, '/') + 1;
|
||||
char * illegal_char = curr_filename;
|
||||
while ((illegal_char = strpbrk(illegal_char, ILLEGAL_CHARS)))
|
||||
{
|
||||
DEBUG("Illegal char found in filename: %c\n", *illegal_char);
|
||||
if (*illegal_char == '.')
|
||||
{
|
||||
// skip initial . (this is allowed)
|
||||
if (illegal_char == curr_filename)
|
||||
continue;
|
||||
// skip extension delimiter
|
||||
if (strpbrk(illegal_char + 1, ".") == NULL)
|
||||
{
|
||||
illegal_char++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*illegal_char = '-';
|
||||
}
|
||||
|
||||
// ensure the extension is .zip
|
||||
char * extension = strrchr(path_to_file, '.');
|
||||
if (extension == NULL || strcmp(extension, ".zip"))
|
||||
strcat(path_to_file, ".zip");
|
||||
|
||||
DEBUG("path: %s\n", path_to_file);
|
||||
FS_Path path = fsMakePath(PATH_ASCII, path_to_file);
|
||||
|
||||
// check if file already exists, and if it does, prompt the user
|
||||
// to overwrite or change name (or exit)
|
||||
Result res = FSUSER_CreateFile(ArchiveSD, path, 0, size);
|
||||
if (R_FAILED(res))
|
||||
{
|
||||
if (res == (long)0xC82044BE)
|
||||
{
|
||||
DEBUG("File already exists\n");
|
||||
|
||||
SwkbdState swkbd;
|
||||
|
||||
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, max_chars / 2);
|
||||
swkbdSetHintText(&swkbd, "Choose a new filename or tap Overwrite");
|
||||
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
|
||||
|
||||
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
|
||||
swkbdSetButton(&swkbd, SWKBD_BUTTON_MIDDLE, "Overwrite", false);
|
||||
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Rename", true);
|
||||
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
|
||||
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
|
||||
|
||||
SwkbdButton button = swkbdInputText(&swkbd, new_filename, max_chars);
|
||||
|
||||
switch (button)
|
||||
{
|
||||
case SWKBD_BUTTON_RIGHT:
|
||||
DEBUG("Renaming to %s\n", new_filename);
|
||||
strcat(new_filename, ".zip");
|
||||
filename = new_filename;
|
||||
goto renamed;
|
||||
case SWKBD_BUTTON_MIDDLE:
|
||||
// we good
|
||||
DEBUG("Overwriting %s\n", filename);
|
||||
break;
|
||||
case SWKBD_BUTTON_LEFT:
|
||||
// do nothing
|
||||
DEBUG("File rename cancelled\n");
|
||||
return;
|
||||
case SWKBD_BUTTON_NONE:
|
||||
DEBUG("SWKBD broke wtf??? :- %x\n", swkbdGetResult(&swkbd));
|
||||
return throw_error("???\nTry a USB keyboard", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
}
|
||||
else if (res == (long)0xC86044D2)
|
||||
{
|
||||
DEBUG("SD card is full\n");
|
||||
return throw_error("SD card is full.\nDelete some themes to make space.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG("error: %lx\n", res);
|
||||
return throw_error("FS Error:\nGet a new SD card.", ERROR_LEVEL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG("Saving to SD: %s\n", path_to_file);
|
||||
remake_file(path, ArchiveSD, size);
|
||||
buf_to_file(size, path, ArchiveSD, buf);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
* reasonable ways as different from the original version.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "remote.h"
|
||||
#include "loading.h"
|
||||
#include "fs.h"
|
||||
@@ -358,17 +360,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
|
||||
}
|
||||
free(download_url);
|
||||
|
||||
char path_to_file[0x107] = { 0 };
|
||||
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
|
||||
save_zip_to_sd(filename, zip_size, zip_buf, mode);
|
||||
free(filename);
|
||||
|
||||
char * extension = strrchr(path_to_file, '.');
|
||||
if (extension == NULL || strcmp(extension, ".zip"))
|
||||
strcat(path_to_file, ".zip");
|
||||
|
||||
DEBUG("Saving to SD: %s\n", path_to_file);
|
||||
remake_file(fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_size);
|
||||
buf_to_file(zip_size, fsMakePath(PATH_ASCII, path_to_file), ArchiveSD, zip_buf);
|
||||
free(zip_buf);
|
||||
}
|
||||
|
||||
@@ -759,7 +752,6 @@ typedef enum ParseResult
|
||||
SUCCESS, // 200/203 (203 indicates a successful request with a transformation applied by a proxy)
|
||||
REDIRECT, // 301/302/307/308
|
||||
HTTPC_ERROR,
|
||||
ABORTED,
|
||||
SERVER_IS_MISBEHAVING,
|
||||
SEE_OTHER = 303, // Theme Plaza returns these
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
@@ -856,7 +848,7 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
|
||||
out->result_code = httpcGetResponseHeader(context, "Content-Disposition", content_buf, 1024);
|
||||
if (R_FAILED(out->result_code))
|
||||
{
|
||||
if (out->result_code == 0xD8A0A028)
|
||||
if (out->result_code == (long)0xD8A0A028L)
|
||||
present = 0;
|
||||
else
|
||||
{
|
||||
@@ -867,56 +859,38 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
|
||||
|
||||
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
|
||||
|
||||
if (!present)
|
||||
if (present)
|
||||
{
|
||||
const int max_chars = 250;
|
||||
// needs to be heap allocated only because the call site is expected to free it
|
||||
*out->filename = malloc(max_chars + 5); // + .zip and the null term
|
||||
|
||||
SwkbdState swkbd;
|
||||
|
||||
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 2, max_chars / 2);
|
||||
swkbdSetHintText(&swkbd, "Choose a filename");
|
||||
swkbdSetFeatures(&swkbd, SWKBD_PREDICTIVE_INPUT | SWKBD_DARKEN_TOP_SCREEN);
|
||||
|
||||
swkbdSetButton(&swkbd, SWKBD_BUTTON_LEFT, "Cancel", false);
|
||||
swkbdSetButton(&swkbd, SWKBD_BUTTON_RIGHT, "Download", true);
|
||||
swkbdSetValidation(&swkbd, SWKBD_NOTEMPTY_NOTBLANK, SWKBD_FILTER_CALLBACK, -1);
|
||||
swkbdSetFilterCallback(&swkbd, &fat32filter, NULL);
|
||||
|
||||
SwkbdButton button = swkbdInputText(&swkbd, *out->filename, max_chars);
|
||||
|
||||
if (button != SWKBD_BUTTON_CONFIRM)
|
||||
{
|
||||
out->result_code = swkbdGetResult(&swkbd);
|
||||
return ABORTED;
|
||||
}
|
||||
|
||||
strcat(*out->filename, ".zip");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
|
||||
// in the extreme fringe case that filename is missing:
|
||||
if (filename != NULL)
|
||||
{
|
||||
filename = strpbrk(filename, "=") + 1; // <filename>;?
|
||||
char * end = strpbrk(filename, ";");
|
||||
if (end)
|
||||
*end = '\0'; // <filename>
|
||||
|
||||
if (filename[0] == '"')
|
||||
// safe to assume the filename is quoted
|
||||
// (if it isn't, then we already have a null-terminated string <filename>)
|
||||
if (filename[0] == '"')
|
||||
{
|
||||
filename[strlen(filename) - 1] = '\0';
|
||||
filename++;
|
||||
}
|
||||
|
||||
char * illegal_char;
|
||||
// filter out characters illegal in FAT32 filenames
|
||||
while ((illegal_char = strpbrk(filename, "><\"?;:/\\+,.|[=]")))
|
||||
*illegal_char = '-';
|
||||
|
||||
*out->filename = malloc(strlen(filename) + 1);
|
||||
strcpy(*out->filename, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
*out->filename = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*out->filename = NULL;
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -967,11 +941,6 @@ redirect: // goto here if we need to redirect
|
||||
{
|
||||
case SUCCESS:
|
||||
break;
|
||||
case ABORTED:
|
||||
ret = httpcCloseContext(&context);
|
||||
if(R_FAILED(ret))
|
||||
return ret;
|
||||
return MAKERESULT(RL_SUCCESS, RS_CANCELED, RM_APPLICATION, RD_CANCEL_REQUESTED);
|
||||
case HTTPC_ERROR:
|
||||
DEBUG("httpc error %lx\n", _header.result_code);
|
||||
snprintf(err_buf, ERROR_BUFFER_SIZE, "Error in HTTPC sysmodule - 0x%08lx.\nIf you are seeing this, please contact an\nAnemone developer on the Theme Plaza Discord.", _header.result_code);
|
||||
|
||||
Reference in New Issue
Block a user