Networking rework (#240)
* Began refactoring http_get. Still a few cases to handle, but it works. * Introduced error enum; handle error 406; handle a misbehaving server; redirect_url leaked memory (L888) * Allowed calls to http_get to pass NULL as acceptable_mime_types * Removed status_code from header struct; eventually planning to move most of parse_header's logic back to http_get, so this makes sense * Moved some more logic back into http_get Note: currently behaves weirdly with some QR codes, including but probably not limited to ones pointing to TinyURL. * Handle redirects correctly; other rearrangements * Formatting * Handle HTTP 303 See Other correctly * Removed "Download failed" error in camera.c due to all related failures being handled in http_get * Fixed missing loading bar; started working on unchunked download * Added unchunked download * Reintroduced the download progress bar, rolled back to always doing chunked download * URL length caps at 2083 chars * Slightly more efficient * Correctly handle incorrect filesize header TODO: reorder clauses to remove the goto? * Fixed illegal characters, removed unnecessary logic (technically a regression, as we always have to realloc now) * Finally finished up status code handling. Fixed a memory leak when the server fails conneg.
This commit is contained in:
@@ -52,6 +52,6 @@
|
||||
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
|
||||
|
||||
bool themeplaza_browser(EntryMode mode);
|
||||
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type);
|
||||
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type, const char *mime_type);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -229,7 +229,7 @@ void update_qr(qr_data *data)
|
||||
draw_install(INSTALL_DOWNLOAD);
|
||||
char * zip_buf = NULL;
|
||||
char * filename = NULL;
|
||||
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD);
|
||||
u32 zip_size = http_get((char*)scan_data.payload, &filename, &zip_buf, INSTALL_DOWNLOAD, "application/zip");
|
||||
|
||||
if(zip_size != 0)
|
||||
{
|
||||
@@ -288,19 +288,17 @@ void update_qr(qr_data *data)
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle more elegantly
|
||||
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: marked for deletion
|
||||
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
free(zip_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_error("Download failed.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
}
|
||||
|
||||
422
source/remote.c
422
source/remote.c
@@ -128,7 +128,7 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
|
||||
smdh_buf = NULL;
|
||||
char * api_url = NULL;
|
||||
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id);
|
||||
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE);
|
||||
smdh_size = http_get(api_url, NULL, &smdh_buf, INSTALL_NONE, "application/octet-stream");
|
||||
free(api_url);
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
|
||||
char * page_json = NULL;
|
||||
char * api_url = NULL;
|
||||
asprintf(&api_url, THEMEPLAZA_PAGE_FORMAT, page, mode + 1, list->tp_search);
|
||||
u32 json_len = http_get(api_url, NULL, &page_json, INSTALL_NONE);
|
||||
u32 json_len = http_get(api_url, NULL, &page_json, INSTALL_NONE, "application/json");
|
||||
free(api_url);
|
||||
|
||||
if (json_len)
|
||||
@@ -228,7 +228,8 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
|
||||
list->tp_page_count = json_integer_value(value);
|
||||
else if (json_is_array(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_IDS))
|
||||
load_remote_entries(list, value, ignore_cache, loading_screen);
|
||||
else if(json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE) && !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND))
|
||||
else if (json_is_string(value) && !strcmp(key, THEMEPLAZA_JSON_ERROR_MESSAGE)
|
||||
&& !strcmp(json_string_value(value), THEMEPLAZA_JSON_ERROR_MESSAGE_NOT_FOUND))
|
||||
throw_error("No results for this search.", ERROR_LEVEL_WARNING);
|
||||
}
|
||||
}
|
||||
@@ -244,6 +245,7 @@ static void load_remote_list(Entry_List_s * list, json_int_t page, EntryMode mod
|
||||
}
|
||||
|
||||
static u16 previous_path_preview[0x106] = { 0 };
|
||||
|
||||
static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int * preview_offset)
|
||||
{
|
||||
bool not_cached = true;
|
||||
@@ -265,7 +267,7 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int *
|
||||
|
||||
draw_install(INSTALL_LOADING_REMOTE_PREVIEW);
|
||||
|
||||
preview_size = http_get(preview_url, NULL, &preview_png, INSTALL_LOADING_REMOTE_PREVIEW);
|
||||
preview_size = http_get(preview_url, NULL, &preview_png, INSTALL_LOADING_REMOTE_PREVIEW, "image/png");
|
||||
free(preview_url);
|
||||
}
|
||||
|
||||
@@ -292,6 +294,7 @@ static bool load_remote_preview(Entry_s * entry, C2D_Image* preview_image, int *
|
||||
}
|
||||
|
||||
static u16 previous_path_bgm[0x106] = { 0 };
|
||||
|
||||
static void load_remote_bgm(Entry_s * entry)
|
||||
{
|
||||
if (!memcmp(&previous_path_bgm, entry->path, 0x106 * sizeof(u16))) return;
|
||||
@@ -309,7 +312,7 @@ static void load_remote_bgm(Entry_s * entry)
|
||||
|
||||
draw_install(INSTALL_LOADING_REMOTE_BGM);
|
||||
|
||||
bgm_size = http_get(bgm_url, NULL, &bgm_ogg, INSTALL_LOADING_REMOTE_BGM);
|
||||
bgm_size = http_get(bgm_url, NULL, &bgm_ogg, INSTALL_LOADING_REMOTE_BGM, "application/ogg, audio/ogg");
|
||||
free(bgm_url);
|
||||
|
||||
u16 path[0x107] = { 0 };
|
||||
@@ -332,7 +335,7 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
|
||||
char * zip_buf = NULL;
|
||||
char * filename = NULL;
|
||||
draw_install(INSTALL_DOWNLOAD);
|
||||
u32 zip_size = http_get(download_url, &filename, &zip_buf, INSTALL_DOWNLOAD);
|
||||
u32 zip_size = http_get(download_url, &filename, &zip_buf, INSTALL_DOWNLOAD, "application/zip");
|
||||
free(download_url);
|
||||
|
||||
char path_to_file[0x107] = { 0 };
|
||||
@@ -349,7 +352,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
|
||||
free(zip_buf);
|
||||
}
|
||||
|
||||
static SwkbdCallbackResult jump_menu_callback(void* page_number, const char** ppMessage, const char* text, size_t textlen)
|
||||
static SwkbdCallbackResult
|
||||
jump_menu_callback(void * page_number, const char ** ppMessage, const char * text, size_t textlen)
|
||||
{
|
||||
(void)textlen;
|
||||
int typed_value = atoi(text);
|
||||
@@ -374,11 +378,13 @@ static void jump_menu(Entry_List_s * list)
|
||||
|
||||
SwkbdState swkbd;
|
||||
|
||||
sprintf(numbuf, "%" JSON_INTEGER_FORMAT, list->tp_page_count);
|
||||
sprintf(numbuf, "%"
|
||||
JSON_INTEGER_FORMAT, list->tp_page_count);
|
||||
int max_chars = strlen(numbuf);
|
||||
swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 2, max_chars);
|
||||
|
||||
sprintf(numbuf, "%" JSON_INTEGER_FORMAT, list->tp_current_page);
|
||||
sprintf(numbuf, "%"
|
||||
JSON_INTEGER_FORMAT, list->tp_current_page);
|
||||
swkbdSetInitialText(&swkbd, numbuf);
|
||||
|
||||
sprintf(numbuf, "Which page do you want to jump to?");
|
||||
@@ -720,171 +726,327 @@ bool themeplaza_browser(EntryMode mode)
|
||||
return downloaded;
|
||||
}
|
||||
|
||||
u32 http_get(const char *url, char ** filename, char ** buf, InstallType install_type)
|
||||
typedef struct header
|
||||
{
|
||||
char * filename; // allocated in parse_header; if NULL, this is user-provided
|
||||
u32 file_size; // if == 0, fall back to chunked read
|
||||
} header;
|
||||
|
||||
typedef enum ParseResult
|
||||
{
|
||||
SUCCESS, // 200/203 (203 indicates a successful request with a transformation applied by a proxy)
|
||||
REDIRECT, // 301/302/303/307/308
|
||||
HTTPC_ERROR,
|
||||
SERVER_IS_MISBEHAVING,
|
||||
NO_FILENAME, // provisional
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_UNACCEPTABLE = 406, // like 204, usually doesn't happen
|
||||
HTTP_PROXY_UNAUTHORIZED = 407,
|
||||
HTTP_GONE = 410,
|
||||
HTTP_URI_TOO_LONG = 414,
|
||||
HTTP_IM_A_TEAPOT = 418, // Note that a combined coffee/tea pot that is temporarily out of coffee should instead return 503.
|
||||
HTTP_UPGRADE_REQUIRED = 426, // the 3DS doesn't support HTTP/2, so we can't upgrade - inform and return
|
||||
HTTP_LEGAL_REASONS = 451,
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_BAD_GATEWAY = 502,
|
||||
HTTP_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_GATEWAY_TIMEOUT = 504,
|
||||
} ParseResult;
|
||||
|
||||
// the good paths for this function return SUCCESS or REDIRECT;
|
||||
// all other paths are failures
|
||||
static ParseResult parse_header(struct header * out, httpcContext * context, bool get_filename, const char * mime)
|
||||
{
|
||||
// status code
|
||||
u32 status_code;
|
||||
|
||||
if (httpcGetResponseStatusCode(context, &status_code))
|
||||
{
|
||||
DEBUG("httpcGetResponseStatusCode\n");
|
||||
return HTTPC_ERROR;
|
||||
}
|
||||
|
||||
DEBUG("HTTP %lu\n", status_code);
|
||||
switch (status_code)
|
||||
{
|
||||
case 301:
|
||||
case 302:
|
||||
case 307:
|
||||
case 308:
|
||||
return REDIRECT;
|
||||
case 200:
|
||||
case 203:
|
||||
break;
|
||||
default:
|
||||
return (ParseResult)status_code;
|
||||
}
|
||||
|
||||
char * content_buf = calloc(1024, sizeof(char));
|
||||
|
||||
// Content-Type
|
||||
|
||||
if (mime)
|
||||
{
|
||||
httpcGetResponseHeader(context, "Content-Type", content_buf, 1024);
|
||||
if (!strstr(mime, content_buf))
|
||||
{
|
||||
free(content_buf);
|
||||
return SERVER_IS_MISBEHAVING;
|
||||
}
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
|
||||
if (httpcGetDownloadSizeState(context, NULL, &out->file_size))
|
||||
{
|
||||
DEBUG("httpcGetDownloadSizeState\n");
|
||||
return HTTPC_ERROR; // no need to free, program dies anyway
|
||||
}
|
||||
|
||||
// Content-Disposition
|
||||
|
||||
if (get_filename)
|
||||
{
|
||||
if (httpcGetResponseHeader(context, "Content-Disposition", content_buf, 1024))
|
||||
{
|
||||
free(content_buf);
|
||||
DEBUG("httpcGetResponseHeader\n");
|
||||
return HTTPC_ERROR;
|
||||
}
|
||||
|
||||
// content_buf: Content-Disposition: attachment; ... filename=<filename>;? ...
|
||||
|
||||
char * filename = strstr(content_buf, "filename="); // filename=<filename>;? ...
|
||||
if (!filename)
|
||||
{
|
||||
free(content_buf);
|
||||
return NO_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';
|
||||
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);
|
||||
DEBUG("%s\n", out->filename);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#define ZIP_NOT_AVAILABLE "ZIP not found at this URL\nIf you believe this is an error, please\ncontact the site administrator"
|
||||
|
||||
/*
|
||||
* call example: written = http_get("url", &filename, &buffer_to_download_to, INSTALL_DOWNLOAD, "application/json");
|
||||
*/
|
||||
u32 http_get(const char * url, char ** filename, char ** buf, InstallType install_type, const char * acceptable_mime_types)
|
||||
{
|
||||
Result ret;
|
||||
httpcContext context;
|
||||
char *new_url = NULL;
|
||||
u32 status_code;
|
||||
u32 content_size = 0;
|
||||
u32 read_size = 0;
|
||||
u32 size = 0;
|
||||
char *last_buf;
|
||||
char redirect_url[0x824] = {0};
|
||||
char new_url[0x824] = {0};
|
||||
|
||||
do {
|
||||
struct header _header = {};
|
||||
|
||||
DEBUG("Original URL: %s\n", url);
|
||||
|
||||
redirect: // goto here if we need to redirect
|
||||
ret = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1);
|
||||
if (ret != 0)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
if (new_url != NULL) free(new_url);
|
||||
DEBUG("httpcOpenContext %.8lx\n", ret);
|
||||
return 0;
|
||||
}
|
||||
ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
|
||||
ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED);
|
||||
ret = httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT);
|
||||
ret = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
|
||||
|
||||
httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
|
||||
httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED);
|
||||
httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT);
|
||||
httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
|
||||
if (acceptable_mime_types)
|
||||
httpcAddRequestHeaderField(&context, "Accept", acceptable_mime_types);
|
||||
|
||||
ret = httpcBeginRequest(&context);
|
||||
if (ret != 0)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
if (new_url != NULL) free(new_url);
|
||||
DEBUG("httpcBeginRequest %.8lx\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = httpcGetResponseStatusCode(&context, &status_code);
|
||||
if(ret!=0){
|
||||
httpcCloseContext(&context);
|
||||
if(new_url!=NULL) free(new_url);
|
||||
DEBUG("httpcGetResponseStatusCode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308))
|
||||
ParseResult parse = parse_header(&_header, &context, (bool)filename, acceptable_mime_types);
|
||||
switch (parse)
|
||||
{
|
||||
if (new_url == NULL) new_url = malloc(0x1000);
|
||||
ret = httpcGetResponseHeader(&context, "Location", new_url, 0x1000);
|
||||
case NO_FILENAME:
|
||||
case SUCCESS:
|
||||
break;
|
||||
case REDIRECT:
|
||||
httpcGetResponseHeader(&context, "Location", redirect_url, 0x824);
|
||||
httpcCloseContext(&context);
|
||||
if (*redirect_url == '/') // if relative URL
|
||||
{
|
||||
strcpy(new_url, url);
|
||||
// this is good code, i promise
|
||||
*(strchr(strchr(strchr(new_url, '/') + 1, '/') + 1, '/')) = '\0';
|
||||
strncat(new_url, redirect_url, 0x824 - strlen(new_url));
|
||||
url = new_url;
|
||||
httpcCloseContext(&context);
|
||||
}
|
||||
} while ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308));
|
||||
|
||||
if (status_code != 200)
|
||||
else
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
if (new_url != NULL) free(new_url);
|
||||
DEBUG("status_code, %lu\n", status_code);
|
||||
return 0;
|
||||
url = redirect_url;
|
||||
}
|
||||
|
||||
ret = httpcGetDownloadSizeState(&context, NULL, &content_size);
|
||||
if (ret != 0)
|
||||
DEBUG("HTTP Redirect: %s %s\n", new_url, *redirect_url == '/' ? "relative" : "absolute");
|
||||
goto redirect;
|
||||
case HTTP_UNACCEPTABLE:
|
||||
DEBUG("HTTP 406 Unacceptable; Accept: %s\n", acceptable_mime_types);
|
||||
throw_error(ZIP_NOT_AVAILABLE, ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case SERVER_IS_MISBEHAVING:
|
||||
DEBUG("Server is misbehaving (provided resource with incorrect MIME)\n");
|
||||
throw_error(ZIP_NOT_AVAILABLE, ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTPC_ERROR:
|
||||
DEBUG("httpc error\n");
|
||||
throw_error("Error in HTTPC sysmodule.\nIf you are seeing this, please contact an Anemone developer\non the ThemePlaza Discord.", ERROR_LEVEL_ERROR);
|
||||
quit = true;
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_NOT_FOUND:
|
||||
case HTTP_GONE:
|
||||
// TODO: check if we're looking at a TP URL, if we are, suggest that it might be missing
|
||||
const char * http_error = parse == HTTP_NOT_FOUND ? "404 Not Found" : "410 Gone";
|
||||
DEBUG("HTTP %s; URL: %s\n", http_error, url);
|
||||
char err_buf[0x69];
|
||||
snprintf(err_buf, 0x69, "HTTP %s\nCheck that the URL is correct.", http_error);
|
||||
throw_error(err_buf, ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_UNAUTHORIZED:
|
||||
case HTTP_FORBIDDEN:
|
||||
case HTTP_PROXY_UNAUTHORIZED:
|
||||
DEBUG("HTTP %u: device not authenticated\n", parse);
|
||||
char err_buf[0x69];
|
||||
snprintf(err_buf, 0x69, "HTTP %s\nContact the site administrator.", parse == HTTP_UNAUTHORIZED
|
||||
? "401 Unauthorized"
|
||||
: parse == HTTP_FORBIDDEN
|
||||
? "403 Forbidden"
|
||||
: "407 Proxy Authentication Required");
|
||||
throw_error(err_buf, ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_URI_TOO_LONG:
|
||||
DEBUG("HTTP 414; URL is too long, maybe too many redirects?\n");
|
||||
throw_error("HTTP 414 URI Too Long\nThe QR code points to a really long URL.\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_IM_A_TEAPOT:
|
||||
DEBUG("HTTP 418 I'm a teapot\n");
|
||||
throw_error("HTTP 418 I'm a teapot\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_UPGRADE_REQUIRED:
|
||||
DEBUG("HTTP 426; HTTP/2 required\n");
|
||||
throw_error("HTTP 426 Upgrade Required\nThe 3DS does not support this website.\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_LEGAL_REASONS:
|
||||
DEBUG("HTTP 451; URL: %s\n", url);
|
||||
throw_error("HTTP 451 Unavailable for Legal Reasons\nSome entity is preventing access\nto the host server for legal reasons.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_INTERNAL_SERVER_ERROR:
|
||||
DEBUG("HTTP 500\n");
|
||||
throw_error("HTTP 500 Internal Server Error\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_BAD_GATEWAY:
|
||||
DEBUG("HTTP 502\n");
|
||||
throw_error("HTTP 502 Bad Gateway\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_SERVICE_UNAVAILABLE:
|
||||
DEBUG("HTTP 503\n");
|
||||
throw_error("HTTP 503 Service Unavailable\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
case HTTP_GATEWAY_TIMEOUT:
|
||||
DEBUG("HTTP 504\n");
|
||||
throw_error("HTTP 504 Gateway Timeout\nContact the site administrator.", ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
default:
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
if (new_url != NULL) free(new_url);
|
||||
DEBUG("httpcGetDownloadSizeState\n");
|
||||
return 0;
|
||||
DEBUG("HTTP %u\n", parse);
|
||||
char err_buf[0x69];
|
||||
snprintf(err_buf, 0x69, "HTTP %u\nIf you believe this is unexpected, please\ncontact the site administrator.", parse);
|
||||
throw_error(err_buf, ERROR_LEVEL_WARNING);
|
||||
return httpcCloseContext(&context);
|
||||
}
|
||||
|
||||
*buf = malloc(0x1000);
|
||||
if (*buf == NULL)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
free(new_url);
|
||||
DEBUG("malloc\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename)
|
||||
{
|
||||
char *content_disposition = calloc(1024, sizeof(char));
|
||||
ret = httpcGetResponseHeader(&context, "Content-Disposition", content_disposition, 1024);
|
||||
if (ret != 0)
|
||||
if (parse != NO_FILENAME)
|
||||
*filename = _header.filename;
|
||||
else
|
||||
{
|
||||
free(content_disposition);
|
||||
free(new_url);
|
||||
// TODO: swkbd stuff
|
||||
// needs to be heap allocated only because the call site is expected to free it
|
||||
*filename = malloc(0x20);
|
||||
sprintf(*filename, "tobesupplied.zip");
|
||||
}
|
||||
}
|
||||
|
||||
u32 chunk_size;
|
||||
if (_header.file_size)
|
||||
// the only reason we chunk this at all is for the download bar;
|
||||
// in terms of efficiency, allocating the full size
|
||||
// would avoid 3 reallocs whenever the server isn't lying
|
||||
chunk_size = _header.file_size / 4;
|
||||
else
|
||||
chunk_size = 0x80000;
|
||||
|
||||
*buf = NULL;
|
||||
char * new_buf;
|
||||
u32 size = 0;
|
||||
u32 read_size = 0;
|
||||
|
||||
do
|
||||
{
|
||||
new_buf = realloc(*buf, size + chunk_size);
|
||||
if (new_buf == NULL)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
free(*buf);
|
||||
DEBUG("httpcGetResponseHeader\n");
|
||||
DEBUG("realloc failed in http_get - file possibly too large?\n"); // TODO: report this?
|
||||
return 0;
|
||||
}
|
||||
*buf = new_buf;
|
||||
|
||||
char * tok = strstr(content_disposition, "filename=");
|
||||
|
||||
if(!(tok))
|
||||
{
|
||||
free(content_disposition);
|
||||
free(new_url);
|
||||
free(*buf);
|
||||
throw_error("Target is not valid!", ERROR_LEVEL_WARNING);
|
||||
DEBUG("filename\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tok += sizeof("filename=") - 1;
|
||||
if(ispunct((int)*tok))
|
||||
{
|
||||
tok++;
|
||||
}
|
||||
char* last_char = tok + strlen(tok) - 1;
|
||||
if(ispunct((int)*last_char))
|
||||
{
|
||||
*last_char = '\0';
|
||||
}
|
||||
|
||||
char *illegal_characters = "\"?;:/\\+";
|
||||
for (size_t i = 0; i < strlen(tok); i++)
|
||||
{
|
||||
for (size_t n = 0; n < strlen(illegal_characters); n++)
|
||||
{
|
||||
if ((tok)[i] == illegal_characters[n])
|
||||
{
|
||||
(tok)[i] = '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*filename = calloc(1024, sizeof(char));
|
||||
strcpy(*filename, tok);
|
||||
free(content_disposition);
|
||||
}
|
||||
|
||||
do {
|
||||
ret = httpcDownloadData(&context, (*(u8**)buf) + size, 0x1000, &read_size);
|
||||
// download exactly chunk_size bytes and toss them into buf.
|
||||
// size contains the current offset into buf.
|
||||
ret = httpcDownloadData(&context, (u8*)(*buf) + size, chunk_size, &read_size);
|
||||
size += read_size;
|
||||
|
||||
if(content_size && install_type != INSTALL_NONE)
|
||||
draw_loading_bar(size, content_size, install_type);
|
||||
|
||||
if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING)
|
||||
{
|
||||
last_buf = *buf;
|
||||
*buf = realloc(*buf, size + 0x1000);
|
||||
if (*buf == NULL)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
free(new_url);
|
||||
free(last_buf);
|
||||
DEBUG("NULL\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (_header.file_size && install_type != INSTALL_NONE)
|
||||
draw_loading_bar(size, _header.file_size, install_type);
|
||||
} while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING);
|
||||
httpcCloseContext(&context);
|
||||
|
||||
last_buf = *buf;
|
||||
*buf = realloc(*buf, size);
|
||||
if (*buf == NULL)
|
||||
// shrink to size
|
||||
new_buf = realloc(*buf, size);
|
||||
if (new_buf == NULL)
|
||||
{
|
||||
httpcCloseContext(&context);
|
||||
free(new_url);
|
||||
free(last_buf);
|
||||
DEBUG("realloc\n");
|
||||
free(*buf);
|
||||
DEBUG("shrinking realloc failed\n"); // 何?
|
||||
return 0;
|
||||
}
|
||||
|
||||
httpcCloseContext(&context);
|
||||
free(new_url);
|
||||
*buf = new_buf;
|
||||
|
||||
DEBUG("size: %lu\n", size);
|
||||
return size;
|
||||
|
||||
Reference in New Issue
Block a user