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:
Dylan G
2021-06-17 01:43:50 +01:00
parent 3f2e4c03f3
commit 18cb5c616f
4 changed files with 145 additions and 68 deletions

View File

@@ -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); 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 remake_file(FS_Path path, FS_Archive archive, u32 size);
void save_zip_to_sd(char * filename, u32 size, char * buf, EntryMode mode);
#endif #endif

View File

@@ -391,14 +391,7 @@ bool init_qr(void)
if(mode != MODE_AMOUNT) if(mode != MODE_AMOUNT)
{ {
char path_to_file[0x107] = {0}; save_zip_to_sd(filename, zip_size, zip_buf, mode);
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);
success = true; success = true;
} }
else else

View File

@@ -27,6 +27,7 @@
#include <strings.h> #include <strings.h>
#include "fs.h" #include "fs.h"
#include "draw.h"
#include "unicode.h" #include "unicode.h"
#include <archive.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); buf_to_file(size, path, archive, buf);
free(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);
}

View File

@@ -24,6 +24,8 @@
* reasonable ways as different from the original version. * reasonable ways as different from the original version.
*/ */
#include <ctype.h>
#include "remote.h" #include "remote.h"
#include "loading.h" #include "loading.h"
#include "fs.h" #include "fs.h"
@@ -358,17 +360,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
} }
free(download_url); free(download_url);
char path_to_file[0x107] = { 0 }; save_zip_to_sd(filename, zip_size, zip_buf, mode);
sprintf(path_to_file, "%s%s", main_paths[mode], filename);
free(filename); 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); 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) SUCCESS, // 200/203 (203 indicates a successful request with a transformation applied by a proxy)
REDIRECT, // 301/302/307/308 REDIRECT, // 301/302/307/308
HTTPC_ERROR, HTTPC_ERROR,
ABORTED,
SERVER_IS_MISBEHAVING, SERVER_IS_MISBEHAVING,
SEE_OTHER = 303, // Theme Plaza returns these SEE_OTHER = 303, // Theme Plaza returns these
HTTP_UNAUTHORIZED = 401, 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); out->result_code = httpcGetResponseHeader(context, "Content-Disposition", content_buf, 1024);
if (R_FAILED(out->result_code)) if (R_FAILED(out->result_code))
{ {
if (out->result_code == 0xD8A0A028) if (out->result_code == (long)0xD8A0A028L)
present = 0; present = 0;
else else
{ {
@@ -867,55 +859,37 @@ static ParseResult parse_header(struct header * out, httpcContext * context, con
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ... // content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
if (!present) if (present)
{ {
const int max_chars = 250; char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
// needs to be heap allocated only because the call site is expected to free it // in the extreme fringe case that filename is missing:
*out->filename = malloc(max_chars + 5); // + .zip and the null term if (filename != NULL)
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); filename = strpbrk(filename, "=") + 1; // <filename>;?
return ABORTED; char * end = strpbrk(filename, ";");
if (end)
*end = '\0'; // <filename>
// 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++;
}
*out->filename = malloc(strlen(filename) + 1);
strcpy(*out->filename, filename);
}
else
{
*out->filename = NULL;
} }
strcat(*out->filename, ".zip");
return SUCCESS;
} }
else
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
filename = strpbrk(filename, "=") + 1; // <filename>;?
char * end = strpbrk(filename, ";");
if (end)
*end = '\0'; // <filename>
if (filename[0] == '"')
// safe to assume the filename is quoted
{ {
filename[strlen(filename) - 1] = '\0'; *out->filename = NULL;
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);
} }
return SUCCESS; return SUCCESS;
} }
@@ -967,11 +941,6 @@ redirect: // goto here if we need to redirect
{ {
case SUCCESS: case SUCCESS:
break; 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: case HTTPC_ERROR:
DEBUG("httpc error %lx\n", _header.result_code); 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); 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);