Downloading works. Still to come: cancelling downloads and extraction.

This commit is contained in:
Matthew Bowra-Dean
2010-12-03 05:21:24 +13:00
committed by Chris Forbes
parent 5991ecdcf5
commit 2e309b46e8
3 changed files with 345 additions and 12 deletions

View File

@@ -14,6 +14,7 @@
#include <glib.h>
#include "main.h"
#include "utility.h"
#define JS_STR(str) JSStringCreateWithUTF8CString(str)
#define JS_FUNC(ctx, callback) JSObjectMakeFunctionWithCallback(ctx, NULL, \
@@ -101,6 +102,8 @@ JSValueRef js_exists_in_mod(JSContextRef ctx, JSObjectRef func,
return_value = JSValueMakeNumber(ctx, 1);
}
g_message("JS ExistsInMod: Not found");
return return_value;
}
@@ -175,6 +178,287 @@ JSValueRef js_launch_mod(JSContextRef ctx, JSObjectRef func,
return return_value;
}
typedef struct download_t
{
char key[32];
char url[128];
char dest[128];
int current_bytes;
int total_bytes;
JSContextGroupRef ctx_group;
JSObjectRef download_progressed_func;
JSValueRef status;
JSValueRef error;
GIOChannel * output_channel;
GPid pid;
} download_t;
#define MAX_DOWNLOADS 16
static download_t downloads[MAX_DOWNLOADS];
static int num_downloads = 0;
download_t * find_download(char const * key)
{
int i;
for (i = 0; i < num_downloads; i++)
{
if (0 == strcmp(downloads[i].key, key))
return downloads + i;
}
return NULL;
}
JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key, * url, * filename;
size_t key_size, url_size, filename_size;
download_t * download;
JSValueRef o;
if (!js_check_num_args(ctx, "registerDownload", argc, 3, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
g_message("JS RegisterDownload: Registering %s", key);
if (NULL == (download = find_download(key)))
{
download = downloads + num_downloads++;
if (num_downloads >= MAX_DOWNLOADS)
{
num_downloads = MAX_DOWNLOADS - 1;
return JSValueMakeNull(ctx);
}
}
memset(download, 0, sizeof(download_t));
download->ctx_group = JSContextGetGroup(ctx);
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("downloadProgressed"), NULL);
download->download_progressed_func = JSValueToObject(ctx, o, NULL);
strncpy(download->key, key, 31);
download->key[31] = '\0';
free(key);
url = js_get_cstr_from_val(ctx, argv[1], &url_size);
strncpy(download->url, url, 127);
download->url[127] = '\0';
free(url);
filename = js_get_cstr_from_val(ctx, argv[2], &filename_size);
strncpy(download->dest, filename, 127);
download->dest[127] = '\0';
free(filename);
download->status = JSValueMakeString(ctx, JS_STR("AVAILABLE"));
return JSValueMakeNull(ctx);
}
gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpointer data)
{
download_t * download = (download_t *)data;
gchar * line;
gsize line_length;
GIOStatus io_status;
JSValueRef args[1];
JSContextRef ctx;
ctx = JSGlobalContextCreateInGroup(download->ctx_group, NULL);
switch(condition)
{
case G_IO_IN:
io_status = g_io_channel_read_line(source, &line, &line_length, NULL, NULL);
if (G_IO_STATUS_NORMAL == io_status)
{
if (0 == memcmp(line, "Error:", 6))
{
download->status = JSValueMakeString(ctx, JS_STR("ERROR"));
download->error = JSValueMakeString(ctx, JS_STR(line + 7));
}
else
{
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
GRegex * pattern = g_regex_new("(\\d{1,3})% (\\d+)/(\\d+) bytes", 0, 0, NULL);
GMatchInfo * match;
if (g_regex_match(pattern, line, 0, &match))
{
gchar * current = g_match_info_fetch(match, 2), * total = g_match_info_fetch(match, 3);
download->current_bytes = atoi(current);
download->total_bytes = atoi(total);
g_free(current);
g_free(total);
}
g_free(match);
}
}
g_free(line);
break;
case G_IO_HUP:
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADED"));
g_io_channel_shutdown(source, FALSE, NULL);
break;
default:
break;
}
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
JSObjectCallAsFunction(ctx, download->download_progressed_func, NULL, 1, args, NULL);
JSGlobalContextRelease((JSGlobalContextRef)ctx);
return TRUE;
}
JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key;
size_t key_size;
download_t * download;
int fd;
GPid pid;
if (!js_check_num_args(ctx, "startDownload", argc, 1, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
if (NULL == (download = find_download(key)))
{
free(key);
return JSValueMakeBoolean(ctx, 0);
}
free(key);
g_message("Starting download %s", download->key);
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
fd = util_do_download(download->url, download->dest, &pid);
if (!fd)
return JSValueMakeBoolean(ctx, 0);
download->pid = pid;
download->output_channel = g_io_channel_unix_new(fd);
g_io_add_watch(download->output_channel, G_IO_IN | G_IO_HUP, update_download_stats, download);
return JSValueMakeBoolean(ctx, 1);
}
JSValueRef js_cancel_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
return JSValueMakeNull(ctx);
}
JSValueRef js_download_status(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key;
size_t key_size;
download_t * download;
if (!js_check_num_args(ctx, "downloadStatus", argc, 1, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
if (NULL == (download = find_download(key)))
{
free(key);
return JSValueMakeString(ctx, JS_STR("NOT_REGISTERED"));
}
free(key);
return download->status;
}
JSValueRef js_download_error(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key;
size_t key_size;
download_t * download;
if (!js_check_num_args(ctx, "downloadError", argc, 1, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
g_message("JS DownloadError: Retrieving error message for %s", key);
if (NULL == (download = find_download(key)))
{
free(key);
return JSValueMakeString(ctx, JS_STR(""));
}
free(key);
return download->error;
}
JSValueRef js_bytes_completed(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key;
size_t key_size;
download_t * download;
if (!js_check_num_args(ctx, "bytesCompleted", argc, 1, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
if (NULL == (download = find_download(key)))
{
free(key);
return JSValueMakeNumber(ctx, 0);
}
free(key);
return JSValueMakeNumber(ctx, download->current_bytes);
}
JSValueRef js_bytes_total(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
char * key;
size_t key_size;
download_t * download;
if (!js_check_num_args(ctx, "bytesTotal", argc, 1, exception))
return JSValueMakeNull(ctx);
key = js_get_cstr_from_val(ctx, argv[0], &key_size);
if (NULL == (download = find_download(key)))
{
free(key);
return JSValueMakeNumber(ctx, 0);
}
free(key);
return JSValueMakeNumber(ctx, download->total_bytes);
}
JSValueRef js_extract_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
size_t argc, const JSValueRef argv[], JSValueRef * exception)
{
return JSValueMakeNull(ctx);
}
void js_add_functions(JSGlobalContextRef ctx, JSObjectRef target, char ** names,
JSObjectCallAsFunctionCallback * callbacks, size_t count)
{
@@ -193,9 +477,16 @@ void bind_js_bridge(WebKitWebView * view, WebKitWebFrame * frame,
JSGlobalContextRef js_ctx;
JSObjectRef window_obj, external_obj;
int func_count = 3;
char * names[] = { "log", "existsInMod", "launchMod" };
JSObjectCallAsFunctionCallback callbacks[] = { js_log, js_exists_in_mod, js_launch_mod };
int func_count = 11;
char * names[] = { "log", "existsInMod", "launchMod", "registerDownload",
"startDownload", "cancelDownload", "downloadStatus",
"downloadError", "bytesCompleted", "bytesTotal",
"extractDownload"};
JSObjectCallAsFunctionCallback callbacks[] = { js_log, js_exists_in_mod, js_launch_mod,
js_register_download, js_start_download,
js_cancel_download, js_download_status,
js_download_error, js_bytes_completed,
js_bytes_total, js_extract_download };
js_ctx = (JSGlobalContextRef)context;

View File

@@ -12,14 +12,16 @@
#include <glib.h>
#include <sys/wait.h>
int util_get_mod_list (GChildWatchFunc callback)
int util_do_command_async(char * command, GChildWatchFunc callback)
{
GPid child_pid;
gint * out_fd = (gint *)malloc(sizeof(gint));
char * spawn_args[] = { "mono", "OpenRA.Utility.exe", "-l", NULL };
gboolean result = g_spawn_async_with_pipes(NULL, spawn_args, NULL,
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &child_pid, NULL, out_fd, NULL, NULL);
char * spawn_args[] = { "mono", "OpenRA.Utility.exe", command, NULL };
gboolean result;
result = g_spawn_async_with_pipes(NULL, spawn_args, NULL,
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &child_pid, NULL, out_fd, NULL, NULL);
if (!result)
{
@@ -31,6 +33,11 @@ int util_get_mod_list (GChildWatchFunc callback)
return TRUE;
}
int util_get_mod_list (GChildWatchFunc callback)
{
return util_do_command_async("-l", callback);
}
int util_do_command_blocking(char * command, GChildWatchFunc callback)
{
GPid child_pid;
@@ -58,16 +65,50 @@ int util_do_command_blocking(char * command, GChildWatchFunc callback)
int util_get_mod_metadata(char const * mod, GChildWatchFunc callback)
{
char util_args[32];
char * util_args;
int return_val;
util_args = (char *)malloc(strlen(mod) + strlen("-i=") + 1);
sprintf(util_args, "-i=%s", mod);
return util_do_command_blocking(util_args, callback);
return_val = util_do_command_blocking(util_args, callback);
free(util_args);
return return_val;
}
int util_get_setting(const char * setting, GChildWatchFunc callback)
{
char command[64];
char * command;
int return_val;
command = (char *)malloc(strlen(setting) + strlen("--settings-value=~/.openra,") + 1);
sprintf(command, "--settings-value=~/.openra,%s", setting);
return util_do_command_blocking(command, callback);
return_val = util_do_command_blocking(command, callback);
free(command);
return return_val;
}
int util_do_download(const char * url, const char * dest, GPid * pid)
{
char * command;
int out_fd;
gboolean result;
char * launch_args[] = { "mono", "OpenRA.Utility.exe", NULL, NULL };
command = (char *)malloc(strlen(url) + strlen(dest) + strlen("--download-url=") + 2);
sprintf(command, "--download-url=%s,%s", url, dest);
launch_args[2] = command;
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
free(command);
if (!result)
{
return 0;
}
return out_fd;
}
char * util_get_output(int fd, int * output_len)

View File

@@ -9,4 +9,5 @@
int util_get_mod_list (GChildWatchFunc);
int util_get_mod_metadata(char const *, GChildWatchFunc);
int util_get_setting(const char *, GChildWatchFunc);
int util_do_download(const char *, const char *, GPid *);
char * util_get_output(int, int *);