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:
Dylan G
2020-12-22 02:11:11 +00:00
committed by GitHub
parent 9cb13f6be1
commit 1c2e562dd6
3 changed files with 425 additions and 265 deletions

View File

@@ -52,6 +52,6 @@
#define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT #define CACHE_PATH_FORMAT "/3ds/" APP_TITLE "/cache/%" JSON_INTEGER_FORMAT
bool themeplaza_browser(EntryMode mode); 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 #endif

View File

@@ -229,7 +229,7 @@ void update_qr(qr_data *data)
draw_install(INSTALL_DOWNLOAD); draw_install(INSTALL_DOWNLOAD);
char * zip_buf = NULL; char * zip_buf = NULL;
char * filename = 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) if(zip_size != 0)
{ {
@@ -288,19 +288,17 @@ void update_qr(qr_data *data)
} }
else else
{ {
// TODO: handle more elegantly
throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING); throw_error("Zip downloaded is neither\na splash nor a theme.", ERROR_LEVEL_WARNING);
} }
} }
else else
{ {
// TODO: marked for deletion
throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING); throw_error("File downloaded isn't a zip.", ERROR_LEVEL_WARNING);
} }
free(zip_buf); free(zip_buf);
} }
else
{
throw_error("Download failed.", ERROR_LEVEL_WARNING);
}
free(filename); free(filename);
} }

View File

@@ -128,7 +128,7 @@ static C2D_Image * load_remote_smdh(Entry_s * entry, bool ignore_cache)
smdh_buf = NULL; smdh_buf = NULL;
char * api_url = NULL; char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_SMDH_FORMAT, entry->tp_download_id); 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); 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 * page_json = NULL;
char * api_url = NULL; char * api_url = NULL;
asprintf(&api_url, THEMEPLAZA_PAGE_FORMAT, page, mode + 1, list->tp_search); 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); free(api_url);
if (json_len) 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); list->tp_page_count = json_integer_value(value);
else if (json_is_array(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_IDS)) else if (json_is_array(value) && !strcmp(key, THEMEPLAZA_JSON_PAGE_IDS))
load_remote_entries(list, value, ignore_cache, loading_screen); 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); 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 u16 previous_path_preview[0x106] = { 0 };
static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int * preview_offset) static bool load_remote_preview(Entry_s * entry, C2D_Image * preview_image, int * preview_offset)
{ {
bool not_cached = true; 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); 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); 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 u16 previous_path_bgm[0x106] = { 0 };
static void load_remote_bgm(Entry_s * entry) static void load_remote_bgm(Entry_s * entry)
{ {
if (!memcmp(&previous_path_bgm, entry->path, 0x106 * sizeof(u16))) return; 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); 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); free(bgm_url);
u16 path[0x107] = { 0 }; u16 path[0x107] = { 0 };
@@ -332,7 +335,7 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
char * zip_buf = NULL; char * zip_buf = NULL;
char * filename = NULL; char * filename = NULL;
draw_install(INSTALL_DOWNLOAD); 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); free(download_url);
char path_to_file[0x107] = { 0 }; char path_to_file[0x107] = { 0 };
@@ -349,7 +352,8 @@ static void download_remote_entry(Entry_s * entry, EntryMode mode)
free(zip_buf); 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; (void)textlen;
int typed_value = atoi(text); int typed_value = atoi(text);
@@ -374,11 +378,13 @@ static void jump_menu(Entry_List_s * list)
SwkbdState swkbd; 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); int max_chars = strlen(numbuf);
swkbdInit(&swkbd, SWKBD_TYPE_NUMPAD, 2, max_chars); 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); swkbdSetInitialText(&swkbd, numbuf);
sprintf(numbuf, "Which page do you want to jump to?"); sprintf(numbuf, "Which page do you want to jump to?");
@@ -720,171 +726,327 @@ bool themeplaza_browser(EntryMode mode)
return downloaded; 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; Result ret;
httpcContext context; httpcContext context;
char *new_url = NULL; char redirect_url[0x824] = {0};
u32 status_code; char new_url[0x824] = {0};
u32 content_size = 0;
u32 read_size = 0;
u32 size = 0;
char *last_buf;
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); ret = httpcOpenContext(&context, HTTPC_METHOD_GET, url, 1);
if (ret != 0) if (ret != 0)
{ {
httpcCloseContext(&context); httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("httpcOpenContext %.8lx\n", ret); DEBUG("httpcOpenContext %.8lx\n", ret);
return 0; return 0;
} }
ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED); httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); // should let us do https
ret = httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT); httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED);
ret = httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive"); httpcAddRequestHeaderField(&context, "User-Agent", USER_AGENT);
httpcAddRequestHeaderField(&context, "Connection", "Keep-Alive");
if (acceptable_mime_types)
httpcAddRequestHeaderField(&context, "Accept", acceptable_mime_types);
ret = httpcBeginRequest(&context); ret = httpcBeginRequest(&context);
if (ret != 0) if (ret != 0)
{ {
httpcCloseContext(&context); httpcCloseContext(&context);
if (new_url != NULL) free(new_url);
DEBUG("httpcBeginRequest %.8lx\n", ret); DEBUG("httpcBeginRequest %.8lx\n", ret);
return 0; return 0;
} }
ret = httpcGetResponseStatusCode(&context, &status_code); ParseResult parse = parse_header(&_header, &context, (bool)filename, acceptable_mime_types);
if(ret!=0){ switch (parse)
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))
{ {
if (new_url == NULL) new_url = malloc(0x1000); case NO_FILENAME:
ret = httpcGetResponseHeader(&context, "Location", new_url, 0x1000); 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; url = new_url;
httpcCloseContext(&context);
} }
} while ((status_code >= 301 && status_code <= 303) || (status_code >= 307 && status_code <= 308)); else
if (status_code != 200)
{ {
httpcCloseContext(&context); url = redirect_url;
if (new_url != NULL) free(new_url);
DEBUG("status_code, %lu\n", status_code);
return 0;
} }
DEBUG("HTTP Redirect: %s %s\n", new_url, *redirect_url == '/' ? "relative" : "absolute");
ret = httpcGetDownloadSizeState(&context, NULL, &content_size); goto redirect;
if (ret != 0) 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); DEBUG("HTTP %u\n", parse);
if (new_url != NULL) free(new_url); char err_buf[0x69];
DEBUG("httpcGetDownloadSizeState\n"); snprintf(err_buf, 0x69, "HTTP %u\nIf you believe this is unexpected, please\ncontact the site administrator.", parse);
return 0; 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) if (filename)
{ {
char *content_disposition = calloc(1024, sizeof(char)); if (parse != NO_FILENAME)
ret = httpcGetResponseHeader(&context, "Content-Disposition", content_disposition, 1024); *filename = _header.filename;
if (ret != 0) else
{ {
free(content_disposition); // TODO: swkbd stuff
free(new_url); // 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); free(*buf);
DEBUG("httpcGetResponseHeader\n"); DEBUG("realloc failed in http_get - file possibly too large?\n"); // TODO: report this?
return 0; return 0;
} }
*buf = new_buf;
char * tok = strstr(content_disposition, "filename="); // download exactly chunk_size bytes and toss them into buf.
// size contains the current offset into buf.
if(!(tok)) ret = httpcDownloadData(&context, (u8*)(*buf) + size, chunk_size, &read_size);
{
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);
size += read_size; size += read_size;
if(content_size && install_type != INSTALL_NONE) if (_header.file_size && install_type != INSTALL_NONE)
draw_loading_bar(size, content_size, install_type); draw_loading_bar(size, _header.file_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;
}
}
} while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING); } while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING);
httpcCloseContext(&context);
last_buf = *buf; // shrink to size
*buf = realloc(*buf, size); new_buf = realloc(*buf, size);
if (*buf == NULL) if (new_buf == NULL)
{ {
httpcCloseContext(&context); httpcCloseContext(&context);
free(new_url); free(*buf);
free(last_buf); DEBUG("shrinking realloc failed\n"); // 何?
DEBUG("realloc\n");
return 0; return 0;
} }
*buf = new_buf;
httpcCloseContext(&context);
free(new_url);
DEBUG("size: %lu\n", size); DEBUG("size: %lu\n", size);
return size; return size;