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);
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
114
source/fs.c
114
source/fs.c
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,56 +859,38 @@ 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;
|
|
||||||
// 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>;? ...
|
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>;?
|
filename = strpbrk(filename, "=") + 1; // <filename>;?
|
||||||
char * end = strpbrk(filename, ";");
|
char * end = strpbrk(filename, ";");
|
||||||
if (end)
|
if (end)
|
||||||
*end = '\0'; // <filename>
|
*end = '\0'; // <filename>
|
||||||
|
|
||||||
if (filename[0] == '"')
|
|
||||||
// safe to assume the filename is quoted
|
// 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[strlen(filename) - 1] = '\0';
|
||||||
filename++;
|
filename++;
|
||||||
}
|
}
|
||||||
|
|
||||||
char * illegal_char;
|
|
||||||
// filter out characters illegal in FAT32 filenames
|
|
||||||
while ((illegal_char = strpbrk(filename, "><\"?;:/\\+,.|[=]")))
|
|
||||||
*illegal_char = '-';
|
|
||||||
|
|
||||||
*out->filename = malloc(strlen(filename) + 1);
|
*out->filename = malloc(strlen(filename) + 1);
|
||||||
strcpy(*out->filename, filename);
|
strcpy(*out->filename, filename);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out->filename = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out->filename = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
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);
|
||||||
|
|||||||
Reference in New Issue
Block a user