summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.c692
1 files changed, 389 insertions, 303 deletions
diff --git a/src/main.c b/src/main.c
index 6a6bf30..6903d8f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -34,6 +34,7 @@ typedef enum
typedef struct
{
+ const gchar* hash;
state_t state;
gchar* title;
gfloat downloaded; /* percent */
@@ -49,9 +50,8 @@ typedef struct
GtkTreeIter iter;
} torrent_t;
-static void torrent_update(const gchar* hash,
- torrent_t* torrent, torrent_data_t* data);
-static void torrent_data_init(torrent_data_t* data);
+static void torrent_update(torrent_t* torrent, const torrent_data_t* data);
+static void torrent_data_init(torrent_data_t* data, const gchar* hash);
static void torrent_data_free(torrent_data_t* data);
typedef struct
@@ -79,8 +79,6 @@ typedef struct
guint config_try;
GHashTable* torrents;
- guint64 last_sync_time;
- guint last_sync_count;
guint sync_timeout;
const gchar* item_lock;
@@ -118,18 +116,20 @@ typedef enum
/* Tell worker to update the list of torrents.
* Will generate a MSG_SYNCLIST response */
MSG_UPDATELIST,
- /* Worker responding to a MSG_UPDATELIST with added and removed torrents */
+ /* Worker responding to a MSG_UPDATELIST with added and removed torrents
+ * and torrent data for all (except removed) */
MSG_SYNCLIST,
- /* Tell worker to update a torrent.
- * Will generate a MSG_SYNC response */
- MSG_UPDATE,
- /* Worker responding to a MSG_UPDATE with uptodate torrent data */
- MSG_SYNC,
- /* Tell worker to send a start command for torrent */
+ /* Worker responding to a MSG_START/MSG_STOP/MSG_REHASH with uptodate
+ * torrent state */
+ MSG_SYNCSTATE,
+ /* Tell worker to send a start command for torrent.
+ * Generates a MSG_SYNCSTATE as response if no error */
MSG_START,
- /* Tell worker to send a stop command for torrent */
+ /* Tell worker to send a stop command for torrent.
+ * Generates a MSG_SYNCSTATE as response if no error */
MSG_STOP,
- /* Tell worker to send a rehash command for torrent */
+ /* Tell worker to send a rehash command for torrent.
+ * Generates a MSG_SYNCSTATE as response if no error */
MSG_REHASH,
} msg_type_t;
@@ -184,54 +184,45 @@ typedef struct
typedef struct
{
msg_t base;
- const gchar** added; /* NULL terminated list */
- const gchar** removed; /* NULL terminated list */
+ const torrent_data_t* data; /* .hash NULL terminated list */
+ const torrent_data_t* added; /* .hash NULL terminated list */
+ const gchar** removed; /* NULL terminated list */
} msg_synclist_t;
typedef struct
{
msg_t base;
const gchar* hash;
- torrent_t torrent;
-} msg_update_t;
-
-typedef struct
-{
- msg_t base;
- const gchar* hash;
- torrent_t torrent;
- torrent_data_t data;
-} msg_sync_t;
+ state_t state;
+} msg_syncstate_t;
typedef struct
{
msg_t base;
gchar* hash;
- torrent_t torrent;
} msg_start_t;
typedef struct
{
msg_t base;
gchar* hash;
- torrent_t torrent;
} msg_stop_t;
typedef struct
{
msg_t base;
gchar* hash;
- torrent_t torrent;
} msg_rehash_t;
typedef struct _hashlist_t
{
- gchar** data;
+ torrent_data_t* data;
gboolean* mark;
gsize size, fill;
- const gchar** added;
+ torrent_data_t* added;
const gchar** removed;
+ gsize removed_size;
} hashlist_t;
static void msg_free(msg_t* msg);
@@ -245,19 +236,12 @@ static msg_t* msg_status(const gchar* format, ...);
static msg_t* msg_disconnect(void);
static msg_t* msg_quit(void);
static msg_t* msg_updatelist(void);
-static msg_t* msg_synclist(const gchar** added, const gchar** removed);
-static msg_t* msg_update(const gchar* hash, torrent_t* torrent);
-static msg_t* msg_sync(const gchar* hash, torrent_t* torrent,
- torrent_data_t* data);
-static msg_t* msg_start(const gchar* hash, torrent_t* torrent);
-static msg_t* msg_stop(const gchar* hash, torrent_t* torrent);
-static msg_t* msg_rehash(const gchar* hash, torrent_t* torrent);
-
-static void hashlist_init(hashlist_t* hlist);
-static void hashlist_clear(hashlist_t* hlist);
-static void hashlist_free(hashlist_t* hlist);
-static gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env,
- const xmlrpc_value * const result, gsize count);
+static msg_t* msg_synclist(torrent_data_t* data, torrent_data_t* added,
+ const gchar** removed);
+static msg_t* msg_syncstate(const gchar* hash, state_t state);
+static msg_t* msg_start(const gchar* hash);
+static msg_t* msg_stop(const gchar* hash);
+static msg_t* msg_rehash(const gchar* hash);
static void do_connect(GtkAction* action, gpointer data);
static void do_disconnect(GtkAction* action, gpointer data);
@@ -565,15 +549,6 @@ gboolean sync_config(gpointer _data)
return FALSE;
}
-static void update_torrent(gpointer _hash, gpointer _torrent, gpointer _master)
-{
- master_t* master = _master;
- const gchar* hash = _hash;
- torrent_t* torrent = _torrent;
-
- g_async_queue_push(master->queue, msg_update(hash, torrent));
-}
-
static gboolean update_list(gpointer _master)
{
master_t* master = _master;
@@ -650,78 +625,65 @@ gboolean incoming_msg(gpointer data)
case MSG_SYNCLIST:
{
msg_synclist_t* m = (msg_synclist_t*)msg;
- const gchar** x = m->removed;
- for (; *x != NULL; ++x)
+ const torrent_data_t* data;
+ const gchar** del = m->removed;
+ for (; *del != NULL; ++del)
{
- if (msg->master->item_lock == *x)
+ if (msg->master->item_lock == *del)
{
msg->master->item_lock = NULL;
listselection_changed(msg->master->listselection, msg->master);
}
- g_hash_table_remove(msg->master->torrents, *x);
+ g_hash_table_remove(msg->master->torrents, *del);
}
- x = m->added;
- for (; *x != NULL; ++x)
+ data = m->data;
+ for (; data->hash != NULL; ++data)
+ {
+ torrent_t* t = g_hash_table_lookup(msg->master->torrents,
+ data->hash);
+ if (t != NULL)
+ {
+ torrent_update(t, data);
+ }
+ if (msg->master->item_lock == data->hash)
+ {
+ msg->master->item_lock = NULL;
+ listselection_changed(msg->master->listselection, msg->master);
+ }
+ }
+ data = m->added;
+ for (; data->hash != NULL; ++data)
{
torrent_t* t = g_slice_new(torrent_t);
t->store = msg->master->liststore;
gtk_list_store_append(t->store, &t->iter);
- g_hash_table_insert(msg->master->torrents, (gpointer)*x, t);
+ g_hash_table_insert(msg->master->torrents, (gpointer)data->hash, t);
+ torrent_update(t, data);
}
- msg->master->last_sync_time = g_get_monotonic_time();
- msg->master->last_sync_count =
- g_hash_table_size(msg->master->torrents);
- g_hash_table_foreach(msg->master->torrents,
- update_torrent, msg->master);
+ msg->master->sync_timeout = g_timeout_add(UPDATE_INTERVAL_MS,
+ update_list,
+ msg->master);
break;
}
- case MSG_SYNC:
+ case MSG_SYNCSTATE:
{
- msg_sync_t* m = (msg_sync_t*)msg;
- g_assert(msg->master->last_sync_count > 0);
-
- torrent_update(m->hash, &m->torrent, &m->data);
+ msg_syncstate_t* m = (msg_syncstate_t*)msg;
+ torrent_t* t = g_hash_table_lookup(msg->master->torrents, m->hash);
+ if (t != NULL)
+ {
+ gtk_list_store_set(t->store, &t->iter, COLUMN_STATE, m->state, -1);
+ }
if (msg->master->item_lock == m->hash)
{
msg->master->item_lock = NULL;
listselection_changed(msg->master->listselection, msg->master);
}
-
- if (--msg->master->last_sync_count == 0)
- {
- if (msg->master->sync_timeout == 0)
- {
- guint64 now = g_get_monotonic_time();
- msg->master->last_sync_time += UPDATE_INTERVAL_MS * 1000;
- if (now >= msg->master->last_sync_time)
- {
- g_async_queue_push(msg->master->queue, msg_updatelist());
- }
- else
- {
- now = msg->master->last_sync_time - now;
- if (now >= UPDATE_INTERVAL_MS * 1000)
- {
- g_async_queue_push(msg->master->queue,
- msg_updatelist());
- }
- else
- {
- msg->master->sync_timeout =
- g_timeout_add((guint)(now / 1000),
- update_list,
- msg->master);
- }
- }
- }
- }
break;
}
case MSG_CONNECT:
case MSG_DISCONNECT:
case MSG_QUIT:
- case MSG_UPDATE:
case MSG_UPDATELIST:
case MSG_START:
case MSG_STOP:
@@ -817,38 +779,35 @@ void do_disconnect(GtkAction* action, gpointer data)
listselection_changed(master->listselection, master);
}
-static gboolean get_selected_torrent(master_t* master, const gchar** hash,
- torrent_t* torrent)
+static const gchar* get_selected_torrent(master_t* master)
{
GValue value = G_VALUE_INIT;
GtkTreeModel* model;
+ GtkTreeIter iter;
+ const gchar* hash;
if (!master->connected || master->item_lock)
{
return FALSE;
}
- if (!gtk_tree_selection_get_selected(master->listselection, &model,
- &torrent->iter))
+ if (!gtk_tree_selection_get_selected(master->listselection, &model, &iter))
{
return FALSE;
}
- torrent->store = master->liststore;
- gtk_tree_model_get_value(model, &torrent->iter,
- COLUMN_HASH, &value);
- *hash = g_value_get_pointer(&value);
+ gtk_tree_model_get_value(model, &iter, COLUMN_HASH, &value);
+ hash = g_value_get_pointer(&value);
g_value_unset(&value);
- return TRUE;
+ return hash;
}
void do_start(GtkAction* action, gpointer data)
{
master_t* master = data;
- torrent_t torrent;
const gchar* hash;
- if (!get_selected_torrent(master, &hash, &torrent))
+ if ((hash = get_selected_torrent(master)) == NULL)
{
return;
}
- g_async_queue_push(master->queue, msg_start(hash, &torrent));
+ g_async_queue_push(master->queue, msg_start(hash));
master->item_lock = hash;
gtk_action_set_sensitive(master->startaction, FALSE);
gtk_action_set_sensitive(master->stopaction, FALSE);
@@ -858,13 +817,12 @@ void do_start(GtkAction* action, gpointer data)
void do_stop(GtkAction* action, gpointer data)
{
master_t* master = data;
- torrent_t torrent;
const gchar* hash;
- if (!get_selected_torrent(master, &hash, &torrent))
+ if ((hash = get_selected_torrent(master)) == NULL)
{
return;
}
- g_async_queue_push(master->queue, msg_stop(hash, &torrent));
+ g_async_queue_push(master->queue, msg_stop(hash));
master->item_lock = hash;
gtk_action_set_sensitive(master->startaction, FALSE);
gtk_action_set_sensitive(master->stopaction, FALSE);
@@ -874,13 +832,12 @@ void do_stop(GtkAction* action, gpointer data)
void do_rehash(GtkAction* action, gpointer data)
{
master_t* master = data;
- torrent_t torrent;
const gchar* hash;
- if (!get_selected_torrent(master, &hash, &torrent))
+ if ((hash = get_selected_torrent(master)) == NULL)
{
return;
}
- g_async_queue_push(master->queue, msg_rehash(hash, &torrent));
+ g_async_queue_push(master->queue, msg_rehash(hash));
master->item_lock = hash;
gtk_action_set_sensitive(master->startaction, FALSE);
gtk_action_set_sensitive(master->stopaction, FALSE);
@@ -909,7 +866,7 @@ void status(master_t* master, const char* format, ...)
g_free(tmp);
}
-void torrent_update(const gchar* hash, torrent_t* torrent, torrent_data_t* data)
+void torrent_update(torrent_t* torrent, const torrent_data_t* data)
{
gtk_list_store_set(torrent->store, &torrent->iter,
COLUMN_STATE, data->state,
@@ -922,7 +879,7 @@ void torrent_update(const gchar* hash, torrent_t* torrent, torrent_data_t* data)
COLUMN_PROGRESSSORT,
data->downloaded < 100.0f ? data->downloaded :
1000.0f + data->seeded,
- COLUMN_HASH, hash,
+ COLUMN_HASH, data->hash,
-1);
}
@@ -931,9 +888,17 @@ static void worker_respond(worker_data_t* data, msg_t* msg);
static gint64 get_i64_xmlrpc(xmlrpc_env* env, xmlrpc_value* value);
static gboolean get_bool_xmlrpc(xmlrpc_env* env, xmlrpc_value* value);
-static void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
- xmlrpc_client* client, xmlrpc_server_info* server,
- const gchar* hash, torrent_t* torrent);
+static void sync_torrent_state(worker_data_t* data, xmlrpc_env* env,
+ xmlrpc_client* client,
+ xmlrpc_server_info* server,
+ const gchar* hash);
+
+static void hashlist_init(hashlist_t* hlist);
+static void hashlist_clear(hashlist_t* hlist);
+static void hashlist_free(hashlist_t* hlist);
+static gboolean hashlist_sync(hashlist_t* hlist, worker_data_t* data,
+ xmlrpc_env * const env,
+ const xmlrpc_value * const result, gsize count);
gpointer worker_main(gpointer _data)
{
@@ -1053,18 +1018,51 @@ gpointer worker_main(gpointer _data)
}
case MSG_UPDATELIST:
{
- xmlrpc_value* params, * result = NULL;
+ xmlrpc_value* params, * item, * result = NULL;
int count;
params = xmlrpc_array_new(&env);
- xmlrpc_client_call2(&env, client, server, "download_list",
+ item = xmlrpc_string_new(&env, ""); /* default view */
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.hash=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.is_active=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.hashing_failed=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.is_hash_checking=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.name=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.size_bytes=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.bytes_done=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.ratio=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.down.rate=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ item = xmlrpc_string_new(&env, "d.up.rate=");
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+ xmlrpc_client_call2(&env, client, server, "d.multicall",
params, &result);
+ xmlrpc_DECREF(params);
if (!env.fault_occurred)
{
count = xmlrpc_array_size(&env, result);
}
if (env.fault_occurred)
{
- xmlrpc_DECREF(params);
if (result) xmlrpc_DECREF(result);
worker_respond(data,
msg_status("Failed to update list (%d): %s",
@@ -1073,10 +1071,9 @@ gpointer worker_main(gpointer _data)
msg_free(msg);
continue;
}
- if (!hashlist_sync(&hashlist, &env,
+ if (!hashlist_sync(&hashlist, data, &env,
result, count > 0 ? (gsize)count : 0))
{
- xmlrpc_DECREF(params);
xmlrpc_DECREF(result);
if (env.fault_occurred)
{
@@ -1094,9 +1091,11 @@ gpointer worker_main(gpointer _data)
continue;
}
xmlrpc_DECREF(result);
- worker_respond(data, msg_synclist(hashlist.added,
+ worker_respond(data, msg_synclist(hashlist.data,
+ hashlist.added,
hashlist.removed));
+ params = xmlrpc_array_new(&env);
xmlrpc_client_call2(&env, client, server, "ratio.min",
params, &result);
data->ratio_min = get_i64_xmlrpc(&env, result) * 10;
@@ -1123,14 +1122,6 @@ gpointer worker_main(gpointer _data)
msg_free(msg);
break;
}
- case MSG_UPDATE:
- {
- msg_update_t* m = (msg_update_t*)msg;
- sync_torrent_data(data, &env, client, server, m->hash,
- &m->torrent);
- msg_free(msg);
- break;
- }
case MSG_QUIT:
quit = TRUE;
msg_free(msg);
@@ -1156,8 +1147,7 @@ gpointer worker_main(gpointer _data)
}
else
{
- sync_torrent_data(data, &env, client, server, m->hash,
- &m->torrent);
+ sync_torrent_state(data, &env, client, server, m->hash);
}
msg_free(msg);
break;
@@ -1198,8 +1188,7 @@ gpointer worker_main(gpointer _data)
}
else
{
- sync_torrent_data(data, &env, client, server, m->hash,
- &m->torrent);
+ sync_torrent_state(data, &env, client, server, m->hash);
}
}
msg_free(msg);
@@ -1226,8 +1215,7 @@ gpointer worker_main(gpointer _data)
}
else
{
- sync_torrent_data(data, &env, client, server, m->hash,
- &m->torrent);
+ sync_torrent_state(data, &env, client, server, m->hash);
}
msg_free(msg);
break;
@@ -1236,7 +1224,7 @@ gpointer worker_main(gpointer _data)
case MSG_ERROR:
case MSG_STATUS:
case MSG_SYNCLIST:
- case MSG_SYNC:
+ case MSG_SYNCSTATE:
g_assert(FALSE);
break;
}
@@ -1321,17 +1309,10 @@ void msg_free(msg_t* msg)
g_slice_free(msg_synclist_t, m);
break;
}
- case MSG_UPDATE:
- {
- msg_update_t* m = (msg_update_t*)msg;
- g_slice_free(msg_update_t, m);
- break;
- }
- case MSG_SYNC:
+ case MSG_SYNCSTATE:
{
- msg_sync_t* m = (msg_sync_t*)msg;
- torrent_data_free(&m->data);
- g_slice_free(msg_sync_t, m);
+ msg_syncstate_t* m = (msg_syncstate_t*)msg;
+ g_slice_free(msg_syncstate_t, m);
break;
}
case MSG_START:
@@ -1433,58 +1414,47 @@ msg_t* msg_updatelist(void)
return &msg->base;
}
-msg_t* msg_synclist(const gchar** added, const gchar** removed)
+msg_t* msg_synclist(torrent_data_t* data, torrent_data_t* added,
+ const gchar** removed)
{
msg_synclist_t* msg = g_slice_new(msg_synclist_t);
msg->base.type = MSG_SYNCLIST;
+ msg->data = data;
msg->added = added;
msg->removed = removed;
return &msg->base;
}
-msg_t* msg_update(const gchar* hash, torrent_t* torrent)
+msg_t* msg_syncstate(const gchar* hash, state_t state)
{
- msg_update_t* msg = g_slice_new(msg_update_t);
- msg->base.type = MSG_UPDATE;
+ msg_syncstate_t* msg = g_slice_new(msg_syncstate_t);
+ msg->base.type = MSG_SYNCSTATE;
msg->hash = hash;
- msg->torrent = *torrent;
+ msg->state = state;
return &msg->base;
}
-msg_t* msg_sync(const gchar* hash, torrent_t* torrent, torrent_data_t* data)
-{
- msg_sync_t* msg = g_slice_new(msg_sync_t);
- msg->base.type = MSG_SYNC;
- msg->hash = hash;
- msg->torrent = *torrent;
- msg->data = *data;
- return &msg->base;
-}
-
-msg_t* msg_start(const gchar* hash, torrent_t* torrent)
+msg_t* msg_start(const gchar* hash)
{
msg_start_t* msg = g_slice_new(msg_start_t);
msg->base.type = MSG_START;
msg->hash = g_strdup(hash);
- msg->torrent = *torrent;
return &msg->base;
}
-msg_t* msg_stop(const gchar* hash, torrent_t* torrent)
+msg_t* msg_stop(const gchar* hash)
{
msg_stop_t* msg = g_slice_new(msg_stop_t);
msg->base.type = MSG_STOP;
msg->hash = g_strdup(hash);
- msg->torrent = *torrent;
return &msg->base;
}
-msg_t* msg_rehash(const gchar* hash, torrent_t* torrent)
+msg_t* msg_rehash(const gchar* hash)
{
msg_rehash_t* msg = g_slice_new(msg_rehash_t);
msg->base.type = MSG_REHASH;
msg->hash = g_strdup(hash);
- msg->torrent = *torrent;
return &msg->base;
}
@@ -1492,29 +1462,30 @@ void hashlist_init(hashlist_t* hlist)
{
hlist->fill = 0;
hlist->size = 10;
- hlist->data = g_new0(gchar*, hlist->size);
+ hlist->data = g_new0(torrent_data_t, hlist->size);
hlist->mark = g_new0(gboolean, hlist->size);
- hlist->added = (const gchar**)hlist->data;
- hlist->removed = (const gchar**)hlist->data + hlist->size - 1;
+ hlist->added = hlist->data;
+ hlist->removed_size = 5;
+ hlist->removed = g_new0(const gchar*, hlist->removed_size);
}
void hashlist_clear(hashlist_t* hlist)
{
- gsize i, a, r;
- for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a);
- r = hlist->removed - (const gchar**)hlist->data;
- for (i = 0; i < a; ++i)
+ torrent_data_t* d = hlist->data;
+ const gchar** r;
+ for (; d->hash != NULL; d++)
{
- g_free(hlist->data[i]);
+ g_free((gchar*)d->hash);
+ torrent_data_free(d);
}
- for (i = r; hlist->data[i]; ++i)
+ for (r = hlist->removed; *r != NULL; r++)
{
- g_free(hlist->data[i]);
+ g_free((gchar*)*r);
}
- hlist->data[0] = NULL;
+ hlist->data[0].hash = NULL;
hlist->fill = 0;
- hlist->added = (const gchar**)hlist->data;
- hlist->removed = (const gchar**)hlist->data + hlist->size - 1;
+ hlist->added = hlist->data;
+ hlist->removed[0] = NULL;
}
void hashlist_free(hashlist_t* hlist)
@@ -1522,97 +1493,120 @@ void hashlist_free(hashlist_t* hlist)
hashlist_clear(hlist);
g_free(hlist->data);
g_free(hlist->mark);
+ g_free(hlist->removed);
}
-gboolean hashlist_resize(hashlist_t* hlist, G_GNUC_UNUSED gsize* a, gsize* r)
+static gboolean hashlist_data_resize(hashlist_t* hlist)
{
gsize ns = hlist->size * 2;
- gchar** tmp1;
+ torrent_data_t* tmp1;
gboolean* tmp2;
- g_assert(*r == hlist->size - 1);
if (ns < 10) ns = 10;
- tmp1 = realloc(hlist->data, ns * sizeof(gchar**));
+ tmp1 = realloc(hlist->data, ns * sizeof(torrent_data_t));
tmp2 = realloc(hlist->mark, ns * sizeof(gboolean));
if (tmp1 == NULL || tmp2 == NULL)
{
if (tmp1) hlist->data = tmp1;
if (tmp2) hlist->mark = tmp2;
- hlist->added = (const gchar**)hlist->data + hlist->fill;
- hlist->removed = (const gchar**)hlist->data + *r;
+ hlist->added = hlist->data + hlist->fill;
return FALSE;
}
- memset(tmp1 + hlist->size, 0, (ns - hlist->size) * sizeof(gchar*));
hlist->data = tmp1;
hlist->mark = tmp2;
hlist->size = ns;
- *r = hlist->size - 1;
return TRUE;
}
-gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env,
+static gboolean hashlist_removed_resize(hashlist_t* hlist, gsize need)
+{
+ gsize ns;
+ const gchar** tmp;
+ if (hlist->removed_size > need)
+ {
+ return TRUE;
+ }
+ ns = hlist->removed_size * 2;
+ if (ns < need + 1) ns = need + 1;
+ tmp = realloc(hlist->data, ns * sizeof(const gchar*));
+ if (tmp == NULL)
+ {
+ hlist->added = hlist->data + hlist->fill;
+ return FALSE;
+ }
+ hlist->removed = tmp;
+ hlist->removed_size = ns;
+ return TRUE;
+}
+
+static gboolean hashlist_item_sync(worker_data_t* data,
+ xmlrpc_env * const env,
+ const xmlrpc_value * const line,
+ torrent_data_t* torrent_data);
+
+gboolean hashlist_sync(hashlist_t* hlist, worker_data_t* data,
+ xmlrpc_env * const env,
const xmlrpc_value * const result, gsize count)
{
gsize i, j, a, r;
gsize found = 0;
- g_assert((const gchar**)hlist->data + hlist->fill == hlist->added);
- for (a = hlist->added - (const gchar**)hlist->data; hlist->data[a]; ++a);
+ g_assert(hlist->data + hlist->fill == hlist->added);
+ for (a = hlist->added - hlist->data; hlist->data[a].hash; ++a);
hlist->fill = a;
- hlist->added = (const gchar**)hlist->data + a;
- for (r = hlist->removed - (const gchar**)hlist->data; hlist->data[r]; ++r)
+ hlist->added = hlist->data + a;
+ for (r = 0; hlist->removed[r]; ++r)
{
- g_free(hlist->data[r]);
- }
- g_assert(r == hlist->size - 1);
- if (a + 5 > r)
- {
- gsize ns = a + 10;
- gchar** tmp1 = realloc(hlist->data, ns * sizeof(gchar*));
- if (tmp1 != NULL)
- {
- gboolean* tmp2 = realloc(hlist->mark, ns * sizeof(gboolean));
- hlist->data = tmp1;
- if (tmp2 != NULL)
- {
- hlist->mark = tmp2;
- memset(hlist->data + hlist->size, 0,
- (ns - hlist->size) * sizeof(gchar*));
- hlist->size = ns;
- r = hlist->size - 1;
- }
- }
+ g_free((gchar*)hlist->removed[r]);
}
+ hlist->removed[0] = NULL;
memset(hlist->mark, 0, hlist->fill * sizeof(gboolean));
for (i = 0; i < count; ++i)
{
- xmlrpc_value* item;
+ xmlrpc_value* line, *item;
const gchar* hash;
- xmlrpc_array_read_item(env, result, i, &item);
+ xmlrpc_array_read_item(env, result, i, &line);
+ xmlrpc_array_read_item(env, line, 0, &item);
xmlrpc_read_string(env, item, &hash);
- xmlrpc_DECREF(item);
+ if (item) xmlrpc_DECREF(item);
if (env->fault_occurred)
{
- hlist->added = (const gchar**)hlist->data + hlist->fill;
- hlist->removed = (const gchar**)hlist->data + r;
+ hlist->added = hlist->data + hlist->fill;
+ if (line) xmlrpc_DECREF(line);
return FALSE;
}
if (found == hlist->fill)
{
- if (a + 1 == r &&
- !hashlist_resize(hlist, &a, &r))
+ if (a == hlist->size && !hashlist_data_resize(hlist))
{
free((void*)hash);
+ xmlrpc_DECREF(line);
return FALSE;
}
- hlist->data[a++] = (gchar*)hash;
+ torrent_data_init(hlist->data + a, hash);
+ if (!hashlist_item_sync(data, env, line, hlist->data + a++))
+ {
+ hlist->added = hlist->data + hlist->fill;
+ hlist->data[a].hash = NULL;
+ xmlrpc_DECREF(line);
+ return FALSE;
+ }
+ hlist->data[a].hash = NULL;
+ xmlrpc_DECREF(line);
continue;
}
/* Quick path */
if (i < hlist->fill && !hlist->mark[i] &&
- strcmp(hlist->data[i], hash) == 0)
+ strcmp(hlist->data[i].hash, hash) == 0)
{
+ if (!hashlist_item_sync(data, env, line, hlist->data + i))
+ {
+ hlist->added = hlist->data + hlist->fill;
+ xmlrpc_DECREF(line);
+ return FALSE;
+ }
hlist->mark[i] = TRUE;
found++;
free((void*)hash);
+ xmlrpc_DECREF(line);
continue;
}
for (j = i + 1; j != i; ++j)
@@ -1629,13 +1623,19 @@ gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env,
{
continue;
}
- if (strcmp(hlist->data[j], hash) == 0)
+ if (strcmp(hlist->data[j].hash, hash) == 0)
{
break;
}
}
if (j != i)
{
+ if (!hashlist_item_sync(data, env, line, hlist->data + j))
+ {
+ hlist->added = hlist->data + hlist->fill;
+ xmlrpc_DECREF(line);
+ return FALSE;
+ }
hlist->mark[j] = TRUE;
found++;
free((void*)hash);
@@ -1643,34 +1643,49 @@ gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env,
else
{
/* New */
- if (a + 1 == r &&
- !hashlist_resize(hlist, &a, &r))
+ if (a == hlist->size && !hashlist_data_resize(hlist))
{
free((void*)hash);
+ xmlrpc_DECREF(line);
return FALSE;
}
- hlist->data[a++] = (gchar*)hash;
+ torrent_data_init(hlist->data + a, hash);
+ if (!hashlist_item_sync(data, env, line, hlist->data + a++))
+ {
+ hlist->data[a].hash = NULL;
+ hlist->added = hlist->data + hlist->fill;
+ xmlrpc_DECREF(line);
+ return FALSE;
+ }
+ hlist->data[a].hash = NULL;
}
+ xmlrpc_DECREF(line);
}
if (found < hlist->fill)
{
gsize n = hlist->fill - found;
- for (j = hlist->fill - 1; n > 0; --j)
+ if (!hashlist_removed_resize(hlist, n))
+ {
+ hlist->added = hlist->data + hlist->fill;
+ return FALSE;
+ }
+ r = 0;
+ for (j = hlist->fill - 1; r < n; --j)
{
if (!hlist->mark[j])
{
- hlist->data[--r] = hlist->data[j];
+ hlist->removed[r++] = hlist->data[j].hash;
+ torrent_data_free(hlist->data + j);
a--;
hlist->fill--;
memmove(hlist->data + j, hlist->data + j + 1,
- (a - j) * sizeof(gchar*));
- hlist->data[a] = NULL;
- n--;
+ (a - j) * sizeof(torrent_data_t));
+ hlist->data[a].hash = NULL;
}
}
+ hlist->removed[r] = NULL;
}
- hlist->added = (const gchar**)hlist->data + hlist->fill;
- hlist->removed = (const gchar**)hlist->data + r;
+ hlist->added = hlist->data + hlist->fill;
return TRUE;
}
@@ -1685,9 +1700,10 @@ void torrent_destroy(gpointer value)
g_slice_free(torrent_t, torrent);
}
-void torrent_data_init(torrent_data_t* data)
+void torrent_data_init(torrent_data_t* data, const gchar* hash)
{
memset(data, 0, sizeof(torrent_data_t));
+ data->hash = hash;
data->left = -1;
}
@@ -1851,145 +1867,142 @@ gint liststore_default_compare_func(GtkTreeModel* model,
}
}
-void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
- xmlrpc_client* client, xmlrpc_server_info* server,
- const gchar* hash, torrent_t* torrent)
+gboolean hashlist_item_sync(worker_data_t* data, xmlrpc_env * const env,
+ const xmlrpc_value * const line,
+ torrent_data_t* torrent_data)
{
- xmlrpc_value* params;
- torrent_data_t torrent_data;
do
{
- xmlrpc_value* result = NULL, * item;
+ xmlrpc_value* item;
const gchar* tmpstr;
gint64 tmpi64;
gboolean tmpb;
guint64 done, up_rate, down_rate, ratio;
- torrent_data_init(&torrent_data);
- params = xmlrpc_array_new(env);
- item = xmlrpc_string_new(env, hash);
- xmlrpc_array_append_item(env, params, item);
- xmlrpc_DECREF(item);
-
- xmlrpc_client_call2(env, client, server, "d.is_active", params,
- &result);
- tmpb = get_bool_xmlrpc(env, result);
+ g_assert(torrent_data->hash != NULL);
+ /* d.active */
+ xmlrpc_array_read_item(env, line, 1, &item);
+ tmpb = get_bool_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
if (!tmpb)
{
- xmlrpc_client_call2(env, client, server, "d.hashing_failed",
- params, &result);
- tmpb = get_bool_xmlrpc(env, result);
+ /* d.hashing_failed */
+ xmlrpc_array_read_item(env, line, 2, &item);
+ tmpb = get_bool_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
if (tmpb)
{
- torrent_data.state = STATE_HASHFAILED;
+ torrent_data->state = STATE_HASHFAILED;
}
else
{
- torrent_data.state = STATE_DEAD;
+ torrent_data->state = STATE_DEAD;
}
}
else
{
- xmlrpc_client_call2(env, client, server, "d.is_hash_checking",
- params, &result);
- tmpb = get_bool_xmlrpc(env, result);
+ /* d.is_hash_checking */
+ xmlrpc_array_read_item(env, line, 3, &item);
+ tmpb = get_bool_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
if (tmpb)
{
- torrent_data.state = STATE_REHASH;
+ torrent_data->state = STATE_REHASH;
}
else
{
- torrent_data.state = STATE_ACTIVE;
+ torrent_data->state = STATE_ACTIVE;
}
}
- xmlrpc_client_call2(env, client, server, "d.name", params, &result);
+ /* d.name */
+ xmlrpc_array_read_item(env, line, 4, &item);
if (!env->fault_occurred)
{
- xmlrpc_read_string(env, result, &tmpstr);
+ xmlrpc_read_string(env, item, &tmpstr);
}
- if (result) xmlrpc_DECREF(result);
+ if (item) xmlrpc_DECREF(item);
if (env->fault_occurred)
{
break;
}
- torrent_data.title = (gchar*)tmpstr;
+ g_free(torrent_data->title);
+ torrent_data->title = (gchar*)tmpstr;
- xmlrpc_client_call2(env, client, server, "d.size_bytes", params,
- &result);
- tmpi64 = get_i64_xmlrpc(env, result);
+ /* d.size_bytes */
+ xmlrpc_array_read_item(env, line, 5, &item);
+ tmpi64 = get_i64_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
- torrent_data.size = (guint64)tmpi64;
+ torrent_data->size = (guint64)tmpi64;
- xmlrpc_client_call2(env, client, server, "d.bytes_done", params,
- &result);
- tmpi64 = get_i64_xmlrpc(env, result);
+ /* d.bytes_done */
+ xmlrpc_array_read_item(env, line, 6, &item);
+ tmpi64 = get_i64_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
done = (guint64)tmpi64;
- if (done >= torrent_data.size)
+ if (done >= torrent_data->size)
{
- torrent_data.downloaded = 100.0f;
+ torrent_data->downloaded = 100.0f;
}
- else if (torrent_data.size > 0)
+ else if (torrent_data->size > 0)
{
- torrent_data.downloaded = ((gfloat)done * 100.0f) /
- (gfloat)torrent_data.size;
+ torrent_data->downloaded = ((gfloat)done * 100.0f) /
+ (gfloat)torrent_data->size;
}
else
{
- torrent_data.downloaded = 0.0f;
+ torrent_data->downloaded = 0.0f;
}
- xmlrpc_client_call2(env, client, server, "d.ratio", params, &result);
- tmpi64 = get_i64_xmlrpc(env, result);
+ /* d.ratio */
+ xmlrpc_array_read_item(env, line, 7, &item);
+ tmpi64 = get_i64_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
ratio = (guint64)tmpi64;
- torrent_data.seeded = (gfloat)ratio / 1000.0f;
+ torrent_data->seeded = (gfloat)ratio / 1000.0f;
- xmlrpc_client_call2(env, client, server, "d.down.rate", params,
- &result);
- tmpi64 = get_i64_xmlrpc(env, result);
+ /* d.down.rate */
+ xmlrpc_array_read_item(env, line, 8, &item);
+ tmpi64 = get_i64_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
down_rate = (guint64)tmpi64;
- torrent_data.down = (gfloat)down_rate / 1000.0f;
+ torrent_data->down = (gfloat)down_rate / 1000.0f;
- xmlrpc_client_call2(env, client, server, "d.up.rate", params, &result);
- tmpi64 = get_i64_xmlrpc(env, result);
+ /* d.up.rate */
+ xmlrpc_array_read_item(env, line, 9, &item);
+ tmpi64 = get_i64_xmlrpc(env, item);
if (env->fault_occurred)
{
break;
}
up_rate = (guint64)tmpi64;
- torrent_data.up = (gfloat)up_rate / 1000.0f;
+ torrent_data->up = (gfloat)up_rate / 1000.0f;
- if (torrent_data.downloaded < 100.0f)
+ if (torrent_data->downloaded < 100.0f)
{
if (down_rate >= 512)
{
- torrent_data.left = (torrent_data.size - done) /
+ torrent_data->left = (torrent_data->size - done) /
(down_rate & ~(guint32)511);
}
}
@@ -2000,7 +2013,7 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
{
if (ratio < data->ratio_max)
{
- upload_left = (torrent_data.size *
+ upload_left = (torrent_data->size *
(data->ratio_max - ratio)) / 1000;
}
else
@@ -2011,12 +2024,12 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
if (data->ratio_min > 0)
{
/* Inexact but good enough */
- guint64 uploaded_bytes = (torrent_data.size * ratio) / 1000;
+ guint64 uploaded_bytes = (torrent_data->size * ratio) / 1000;
if (uploaded_bytes >= data->ratio_upload)
{
if (ratio < data->ratio_min)
{
- gint64 tmp = (torrent_data.size *
+ gint64 tmp = (torrent_data->size *
(data->ratio_min - ratio)) / 1000;
if (upload_left < 0 || tmp < upload_left)
{
@@ -2041,24 +2054,97 @@ void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
{
if (up_rate >= 512)
{
- torrent_data.left = (guint64)upload_left /
+ torrent_data->left = (guint64)upload_left /
(up_rate & ~(guint32)511);
}
}
}
}
while (FALSE);
+ if (env->fault_occurred)
+ {
+ worker_respond(data,
+ msg_status("Failed to sync torrent %s (%d): %s",
+ torrent_data->hash,
+ env->fault_code, env->fault_string));
+ env->fault_occurred = FALSE;
+ torrent_data->state = STATE_DEAD;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void sync_torrent_state(worker_data_t* data, xmlrpc_env* env,
+ xmlrpc_client* client, xmlrpc_server_info* server,
+ const gchar* hash)
+{
+ xmlrpc_value* params;
+ state_t state = STATE_DEAD;
+ do
+ {
+ xmlrpc_value* result = NULL, * item;
+ gboolean tmpb;
+ params = xmlrpc_array_new(env);
+ item = xmlrpc_string_new(env, hash);
+ xmlrpc_array_append_item(env, params, item);
+ xmlrpc_DECREF(item);
+
+ xmlrpc_client_call2(env, client, server, "d.is_active", params,
+ &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (!tmpb)
+ {
+ xmlrpc_client_call2(env, client, server, "d.hashing_failed",
+ params, &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (tmpb)
+ {
+ state = STATE_HASHFAILED;
+ }
+ else
+ {
+ state = STATE_DEAD;
+ }
+ }
+ else
+ {
+ xmlrpc_client_call2(env, client, server, "d.is_hash_checking",
+ params, &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (tmpb)
+ {
+ state = STATE_REHASH;
+ }
+ else
+ {
+ state = STATE_ACTIVE;
+ }
+ }
+ }
+ while (FALSE);
xmlrpc_DECREF(params);
if (env->fault_occurred)
{
worker_respond(data,
- msg_status("Failed to update torrent %s (%d): %s",
+ msg_status("Failed to sync torrent %s (%d): %s",
hash, env->fault_code, env->fault_string));
env->fault_occurred = FALSE;
- torrent_data.state = STATE_DEAD;
+ state = STATE_DEAD;
}
- worker_respond(data, msg_sync(hash, torrent, &torrent_data));
+ worker_respond(data, msg_syncstate(hash, state));
}
void listselection_changed(GtkTreeSelection* selection,