Revision 1913

trunk/nntpgrab_core/nntpgrab_plugin.h (revision 1913)
27 27

                
28 28
G_BEGIN_DECLS
29 29

                
30
#define NNTPGRAB_PLUGIN_API_VERSION       20111121
30
#define NNTPGRAB_PLUGIN_API_VERSION       20111201
31 31

                
32 32
typedef enum {
33 33
    NNTP_ERROR_NONE,
... ...
67 67
 * @config_save:                   Save the configuration to disk
68 68
 * @config_get_folder_listing:     Retrieve a list containing all the sub-folders which are in the given folder. Needs to be free'd using config_free_folder_listing
69 69
 * @config_free_folder_listing:    Free a list of sub-folders
70
 * @config_get_config_folder:      Retrieve the folder where all configuration files can be stored. Needs to be free'd using ng_free
70 71
 * @schedular_start:               Start the schedular
71 72
 * @schedular_stop:                Stop the schedular
72 73
 * @schedular_get_state:           Retrieve the current state of the schedular
... ...
101 102
    NGConfigOpts (*config_get_opts) (void);
102 103
    void (*config_set_opts) (NGConfigOpts opts);
103 104
    ngboolean (*config_save) (char **errmsg);
105
    char *(*config_get_config_folder) (void);
104 106
    ngboolean (*schedular_start) (void);
105 107
    ngboolean (*schedular_stop) (const char *reason, ngboolean wait);
106 108
    NGSchedularState (*schedular_get_state) (void);
trunk/nntpgrab_core/configuration.c (revision 1913)
564 564
}
565 565

                
566 566
/**
567
 * configuration_get_config_folder:
568
 *
569
 * Retrieve the folder where all configuration files can be stored
570
 *
571
 * Returns:             The folder where all configuration files can be stored. Needs to be g_free'd after use
572
 */
573
char *
574
configuration_get_config_folder(void)
575
{
576
    if (g_getenv("NNTPGRAB_CONFIG_DIR")) {
577
        return g_build_path(G_DIR_SEPARATOR_S, g_getenv("NNTPGRAB_CONFIG_DIR"), "NNTPGrab", NULL);
578
    } else {
579
        return g_build_path(G_DIR_SEPARATOR_S, g_get_user_config_dir(), "NNTPGrab", NULL);
580
    }
581
}
582

                
583
/**
567 584
 * Load all settings from a local file on the harddrive
568 585
 *
569 586
 * @param obj           The configuration object
... ...
576 593
    Configuration *config = CONFIGURATION(obj);
577 594
    GKeyFile *keyfile;
578 595
    GError *err = NULL;
596
    char *config_folder;
579 597
    char *filename;
580 598
    char **groups;
581 599
    int i;
... ...
583 601

                
584 602
    g_static_rw_lock_writer_lock(&config->rwlock);
585 603

                
586
    // Free the already known settings
604
    /* Free the already known settings */
587 605
    list = config->servers;
588 606
    while (list) {
589 607
        NGConfigServer *server = list->data;
... ...
596 614
    g_list_free(config->servers);
597 615
    config->servers = NULL;
598 616

                
599
    // Try to load the config file
600
    if (g_getenv("NNTPGRAB_CONFIG_DIR")) {
601
        filename = g_build_filename(g_getenv("NNTPGRAB_CONFIG_DIR"), "NNTPGrab", "nntpgrab.conf", NULL);
602
    } else {
603
        filename = g_build_filename(g_get_user_config_dir(), "NNTPGrab", "nntpgrab.conf", NULL);
604
    }
617
    /* Try to load the config file */
618
    config_folder = configuration_get_config_folder();
619
    filename = g_build_filename(config_folder, "nntpgrab.conf", NULL);
620
    g_free(config_folder);
621

                
605 622
    keyfile = g_key_file_new();
606 623
    if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &err)) {
607 624
        char *tmp;
... ...
907 924
    Configuration *config = CONFIGURATION(obj);
908 925
    GKeyFile *keyfile;
909 926
    GError *err = NULL;
927
    char *config_folder;
910 928
    char *filename;
911
    char *dirname;
912 929
    char *contents;
913 930
    GList *list;
914
    const char *config_dir;
915 931

                
916 932
    g_static_rw_lock_reader_lock(&config->rwlock);
917 933

                
... ...
965 981
        return FALSE;
966 982
    }
967 983

                
968
    // Create the folder ~/.config/NNTPGrab if it didn't exist already
969
    if (g_getenv("NNTPGRAB_CONFIG_DIR")) {
970
        config_dir = g_getenv("NNTPGRAB_CONFIG_DIR");
971
    } else {
972
        config_dir = g_get_user_config_dir();
973
    }
984
    /* Create the folder ~/.config/NNTPGrab if it didn't exist already */
985
    config_folder = configuration_get_config_folder();
986
    g_mkdir_with_parents(config_folder, 0700);
974 987

                
975
    dirname = g_build_path(G_DIR_SEPARATOR_S, config_dir, "NNTPGrab", NULL);
976
    g_mkdir_with_parents(dirname, 0700);
977
    g_free(dirname);
988
    /* Write the configuration to disk */
989
    filename = g_build_filename(config_folder, "nntpgrab.conf", NULL);
990
    g_free(config_folder);
978 991

                
979
    filename = g_build_filename(config_dir, "NNTPGrab", "nntpgrab.conf", NULL);
980

                
981 992
    if (!g_file_set_contents(filename, contents, -1, &err)) {
982 993
        if (errmsg) {
983 994
            *errmsg = g_strdup_printf(_("configuration_save(): Error while opening file '%s'\n%s"), filename, err->message);
trunk/nntpgrab_core/configuration.h (revision 1913)
46 46
void             configuration_set_newsgroup_info(Configuration *obj, const char *servername, const char *newsgroup, ConfigGroupInfo info);
47 47
NGConfigOpts     configuration_get_opts(Configuration *obj);
48 48
void             configuration_set_opts(Configuration *obj, NGConfigOpts opts);
49
char            *configuration_get_config_folder(void);
49 50
gboolean         configuration_load(Configuration *config, char **errmsg);
50 51
gboolean         configuration_save(Configuration *config, char **errmsg);
51 52

                
trunk/nntpgrab_core/plugins.c (revision 1913)
212 212
    obj->core_funcs.config_get_opts = config_get_opts_wrapper;
213 213
    obj->core_funcs.config_set_opts = config_set_opts_wrapper;
214 214
    obj->core_funcs.config_save = config_save_wrapper;
215
    obj->core_funcs.config_get_config_folder = configuration_get_config_folder;
215 216
    obj->core_funcs.schedular_start = schedular_start_wrapper;
216 217
    obj->core_funcs.schedular_stop = schedular_stop_wrapper;
217 218
    obj->core_funcs.schedular_get_state = download_thread_get_state;
trunk/plugins/counter/plugin_counter.c (revision 1913)
17 17
*/
18 18

                
19 19
#include 
20
#include 
20 21
#include "nntpgrab_plugin.h"
22
#include "nntpgrab_utils.h"
21 23
#include "config.h"
22 24

                
23
static void config_changed_cb(NGPlugin *plugin_data, gpointer data);
24
static void part_done_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num, int size, gpointer data);
25
typedef struct _counter_server_data {
26
    NGConfigServer server_info;
27
    guint64 num_bytes_downloaded;
28
    guint64 num_parts_succeeded;
29
    guint64 num_parts_failed;
30
} CounterServerData;
25 31

                
32
typedef struct _counter_data {
33
    time_t stamp_started;
34
    gboolean data_changed_since_last_announce;
35
    GList *collected_data;     /* List contains CounterServerData elements */
36
} CounterData;
37

                
38
static void plugin_counter_free_cached_data(NGPlugin *plugin_data);
39
static gboolean plugin_counter_load_cached_data(NGPlugin *plugin_data, char **errmsg);
40
static gboolean plugin_counter_save_cached_data(NGPlugin *plugin_data, char **errmsg);
41
static gboolean plugin_counter_send_update_announcement(gpointer data);
42
static CounterServerData *plugin_counter_lookup_server(NGPlugin *plugin_data, const char *servername);
43
static void plugin_counter_part_done_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num, int size, gpointer data);
44
static void plugin_counter_part_failed_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num,int size, gboolean all_servers_tried);
45

                
26 46
void
27 47
nntpgrab_plugin_initialize(NGPlugin *plugin_data)
28 48
{
... ...
32 52
    ng_plugin_set_url(plugin_data, "https://www.nntpgrab.nl");
33 53
    ng_plugin_set_description(plugin_data, "Count the amount of download traffic for each configured usenet server ");
34 54

                
35
    ng_plugin_set_required_event(plugin_data, "config_changed");
55
    ng_plugin_create_event(plugin_data, "counter_data_changed", 0);
56

                
36 57
    ng_plugin_set_required_event(plugin_data, "part_done");
58
    ng_plugin_set_required_event(plugin_data, "part_failed");
37 59
}
38 60

                
39 61
ngboolean
40 62
nntpgrab_plugin_load(NGPlugin *plugin_data, char **errmsg)
41 63
{
42
    ng_plugin_connect_event(plugin_data, "config_changed", NG_PLUGIN_FUNCTION(config_changed_cb), NULL);
43
    ng_plugin_connect_event(plugin_data, "part_done", NG_PLUGIN_FUNCTION(part_done_cb), NULL);
64
    ng_plugin_connect_event(plugin_data, "part_done", NG_PLUGIN_FUNCTION(plugin_counter_part_done_cb), NULL);
65
    ng_plugin_connect_event(plugin_data, "part_failed", NG_PLUGIN_FUNCTION(plugin_counter_part_failed_cb), NULL);
44 66

                
67
    plugin_data->priv = g_slice_new0(CounterData);
68

                
69
    if (!plugin_counter_load_cached_data(plugin_data, errmsg)) {
70
        return FALSE;
71
    }
72

                
73
    /* Make sure the plugin sends update announcements every 10 seconds */
74
    g_timeout_add_seconds(10, plugin_counter_send_update_announcement, plugin_data);
75
    plugin_counter_send_update_announcement(plugin_data);
76

                
45 77
    return TRUE;
46 78
}
47 79

                
... ...
54 86
void
55 87
nntpgrab_plugin_unload(NGPlugin *plugin_data)
56 88
{
89
    char *errmsg = NULL;
90

                
91
    if (!plugin_counter_save_cached_data(plugin_data, &errmsg)) {
92
        ng_plugin_emit_log_msg(plugin_data, NG_LOG_LEVEL_WARNING, errmsg);
93
        g_free(errmsg);
94
    }
95

                
96
    plugin_counter_free_cached_data(plugin_data);
97

                
98
    g_slice_free(CounterData, plugin_data->priv);
99
    plugin_data->priv = NULL;
57 100
}
58 101

                
59 102
void
... ...
67 110
    return NNTPGRAB_PLUGIN_API_VERSION;
68 111
}
69 112

                
113
static NGVariant *
114
get_collected_data(NGPlugin *plugin_data, NGVariant *parameters, char **errmsg)
115
{
116
    GVariant *variant_counter;
117
    GVariant *variant_servers;
118
    GVariantBuilder *builder_counter;
119
    GVariantBuilder *builder_servers;
120
    CounterData *counter_data;
121
    NGList *servers;
122
    NGList *list;
123

                
124
    g_return_if_fail(plugin_data != NULL);
125

                
126
    counter_data = (CounterData*) plugin_data->priv;
127

                
128
    g_return_if_fail(counter_data != NULL);
129

                
130
    builder_counter = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
131
    g_variant_builder_add(builder_counter, "{sv}", "stamp_started", g_variant_new_uint64((guint64) counter_data->stamp_started));
132

                
133
    builder_servers = g_variant_builder_new(G_VARIANT_TYPE("av"));
134

                
135
    servers = plugin_data->core_funcs.config_get_avail_servers();
136
    list = servers;
137
    while (list) {
138
        const char *servername = (const char*) list->data;
139
        GVariant *variant_server;
140
        GVariantBuilder *builder_server;
141
        CounterServerData *server_data;
142

                
143
        server_data = plugin_counter_lookup_server(plugin_data, servername);
144

                
145
        g_return_val_if_fail(server_data != NULL, NULL);
146

                
147
        builder_server = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
148
        g_variant_builder_add(builder_server, "{sv}", "servername", g_variant_new_string(server_data->server_info.servername));
149
        g_variant_builder_add(builder_server, "{sv}", "num_bytes_downloaded", g_variant_new_uint64(server_data->num_bytes_downloaded));
150
        g_variant_builder_add(builder_server, "{sv}", "num_parts_succeeded", g_variant_new_uint64(server_data->num_parts_succeeded));
151
        g_variant_builder_add(builder_server, "{sv}", "num_parts_failed", g_variant_new_uint64(server_data->num_parts_failed));
152
        variant_server = g_variant_builder_end(builder_server);
153

                
154
        g_variant_builder_add(builder_servers, "v", variant_server);
155

                
156
        list = ng_list_next(list);
157
    }
158

                
159
    variant_servers = g_variant_builder_end(builder_servers);
160
    g_variant_builder_add(builder_counter, "{sv}", "collected_data", variant_servers);
161

                
162
    plugin_data->core_funcs.config_free_avail_servers(servers);
163

                
164
    variant_counter = g_variant_builder_end(builder_counter);
165
    return (NGVariant*) variant_counter;
166
}
167

                
168
static NGVariant *
169
reset_collected_data(NGPlugin *plugin_data, NGVariant *parameters, char **errmsg)
170
{
171
    plugin_counter_free_cached_data(plugin_data);
172

                
173
    /* Manually emit an update announcement so that all frontends
174
     * will know that the collected data was reset */
175
    plugin_counter_send_update_announcement(plugin_data);
176

                
177
    return (NGVariant*) g_variant_new_boolean(TRUE);
178
}
179

                
70 180
NGVariant *
71 181
nntpgrab_plugin_call_plugin_method(NGPlugin *plugin_data, const char *method, NGVariant *parameters, char **errmsg)
72 182
{
73
    return NULL;
183
    if (!g_strcmp0(method, "get_collected_data")) {
184
        return get_collected_data(plugin_data, parameters, errmsg);
185
    } else if (!g_strcmp0(method, "reset")) {
186
        return reset_collected_data(plugin_data, parameters, errmsg);
187
    } else {
188
        return NULL;
189
    }
74 190
}
75 191

                
76 192
static void
77
config_changed_cb(NGPlugin *plugin_data, gpointer data)
193
plugin_counter_free_cached_data(NGPlugin *plugin_data)
78 194
{
79
    /* Make sure all our cached servers are still part of the NNTPGrab configuration */
195
    CounterData *counter_data;
196
    GList *list;
80 197

                
198
    g_return_if_fail(plugin_data != NULL);
199

                
200
    counter_data = (CounterData*) plugin_data->priv;
201

                
202
    g_return_if_fail(counter_data != NULL);
203

                
204
    list = counter_data->collected_data;
205
    while (list) {
206
        CounterServerData *server_data = (CounterServerData*) list->data;
207

                
208
        g_slice_free(CounterServerData, server_data);
209

                
210
        list = g_list_next(list);
211
    }
212

                
213
    g_list_free(counter_data->collected_data);
214
    counter_data->collected_data = NULL;
215
    counter_data->stamp_started = time(NULL);
216

                
217
    counter_data->data_changed_since_last_announce = TRUE;
81 218
}
82 219

                
220
static gboolean
221
plugin_counter_load_cached_data(NGPlugin *plugin_data, char **errmsg)
222
{
223
    CounterData *counter_data;
224
    GKeyFile *keyfile;
225
    char *config_dir;
226
    char *filename;
227
    GError *err = NULL;
228
    char **groups;
229
    int i;
230

                
231
    g_return_val_if_fail(plugin_data != NULL, FALSE);
232
    g_return_val_if_fail(errmsg != NULL, FALSE);
233

                
234
    counter_data = (CounterData*) plugin_data->priv;
235

                
236
    g_return_if_fail(counter_data != NULL);
237

                
238
    counter_data->stamp_started = time(NULL);
239

                
240
    config_dir = plugin_data->core_funcs.config_get_config_folder();
241
    g_return_val_if_fail(config_dir != NULL, FALSE);
242

                
243
    filename = g_build_filename(config_dir, "plugin_counter.conf", NULL);
244
    ng_free(config_dir);
245

                
246
    if (!g_file_test(filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
247
        /* No cached configuration file exists yet */
248
        return TRUE;
249
    }
250

                
251
    /* Clean up old entries */
252
    plugin_counter_free_cached_data(plugin_data);
253

                
254
    /* Load the contents of the configuration file from disk */
255
    keyfile = g_key_file_new();
256
    if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &err)) {
257
        *errmsg = g_strdup_printf("Unable to load file '%s': %s", filename, err->message);
258
        g_error_free(err);
259
        g_free(filename);
260
        return FALSE;
261
    }
262
    g_free(filename);
263

                
264
    groups = g_key_file_get_groups(keyfile, NULL);
265
    i = 0;
266
    while (groups && groups[i] != NULL) {
267
        CounterServerData *server_data;
268
        NGConfigServer server_info;
269

                
270
        /* Only read the stamp_started key from the generic group */
271
        if (!g_strcmp0(groups[i], "generic")) {
272
            if (!g_key_file_has_key(keyfile, groups[i], "stamp_started", NULL)) {
273
                counter_data->stamp_started = time(NULL);
274
            } else {
275
                counter_data->stamp_started = (time_t) g_key_file_get_uint64(keyfile, groups[i], "stamp_started", NULL);
276
            }
277

                
278
            i++;
279
            continue;
280
        }
281

                
282
        if (!g_key_file_has_key(keyfile, groups[i], "num_bytes_downloaded", NULL) ||
283
            !g_key_file_has_key(keyfile, groups[i], "num_parts_succeeded", NULL) ||
284
            !g_key_file_has_key(keyfile, groups[i], "num_parts_failed", NULL)) {
285

                
286
            /* One or more keys weren't found */
287
            ng_plugin_emit_log_msg(plugin_data, NG_LOG_LEVEL_INFO, "Server named '%s' was found in counter plugin configuration file, but is missing some keys", groups[i]);
288
            i++;
289
            continue;
290
        }
291

                
292
        /* All required keys are found */
293

                
294
        if (!plugin_data->core_funcs.config_get_server_info(groups[i], &server_info)) {
295
            /* Server doesn't exist in current NNTPGrab configuration */
296
            ng_plugin_emit_log_msg(plugin_data, NG_LOG_LEVEL_INFO, "Server named '%s' was found in counter plugin configuration file, but isn't known in the NNTPGrab configuration", groups[i]);
297
            i++;
298
            continue;
299
        }
300

                
301
        server_data = g_slice_new0(CounterServerData);
302

                
303
        server_data->server_info = server_info;
304
        server_data->num_bytes_downloaded = g_key_file_get_uint64(keyfile, groups[i], "num_bytes_downloaded", NULL);
305
        server_data->num_parts_succeeded = g_key_file_get_uint64(keyfile, groups[i], "num_parts_succeeded", NULL);
306
        server_data->num_parts_failed = g_key_file_get_uint64(keyfile, groups[i], "num_parts_failed", NULL);
307

                
308
        counter_data->collected_data = g_list_append(counter_data->collected_data, server_data);
309

                
310
        i++;
311
    }
312

                
313
    g_key_file_free(keyfile);
314

                
315
    return TRUE;
316
}
317

                
318
static gboolean
319
plugin_counter_save_cached_data(NGPlugin *plugin_data, char **errmsg)
320
{
321
    GKeyFile *keyfile;
322
    char *config_dir;
323
    char *filename;
324
    GError *err = NULL;
325
    CounterData *counter_data = (CounterData*) plugin_data->priv;
326
    GList *list;
327
    char *contents;
328

                
329
    g_return_val_if_fail(plugin_data != NULL, FALSE);
330
    g_return_val_if_fail(errmsg != NULL, FALSE);
331
    g_return_val_if_fail(counter_data != NULL, FALSE);
332

                
333
    keyfile = g_key_file_new();
334

                
335
    g_key_file_set_uint64(keyfile, "generic", "stamp_started", (guint64) counter_data->stamp_started);
336

                
337
    list = counter_data->collected_data;
338
    while (list) {
339
        CounterServerData *server_data = (CounterServerData*) list->data;
340

                
341
        g_key_file_set_uint64(keyfile, server_data->server_info.servername, "num_bytes_downloaded", server_data->num_bytes_downloaded);
342
        g_key_file_set_uint64(keyfile, server_data->server_info.servername, "num_parts_succeeded", server_data->num_parts_succeeded);
343
        g_key_file_set_uint64(keyfile, server_data->server_info.servername, "num_parts_failed", server_data->num_parts_failed);
344

                
345
        list = g_list_next(list);
346
    }
347

                
348
    contents = g_key_file_to_data(keyfile, NULL, &err);
349
    if (!contents) {
350
        *errmsg = g_strdup(err->message);
351
        g_error_free(err);
352
        g_free(contents);
353
        g_key_file_free(keyfile);
354

                
355
        return FALSE;
356
    }
357

                
358
    /* Write the configuration to disk */
359
    config_dir = plugin_data->core_funcs.config_get_config_folder();
360
    g_return_val_if_fail(config_dir != NULL, FALSE);
361
    filename = g_build_filename(config_dir, "plugin_counter.conf", NULL);
362
    g_free(config_dir);
363

                
364
    if (!g_file_set_contents(filename, contents, -1, &err)) {
365
        *errmsg = g_strdup_printf("Error while saving download counter configuration to file '%s'\n%s", filename, err->message);
366
        g_error_free(err);
367
        g_free(contents);
368
        g_free(filename);
369
        g_key_file_free(keyfile);
370

                
371
        return FALSE;
372
    }
373

                
374
    g_free(contents);
375
    g_free(filename);
376
    g_key_file_free(keyfile);
377

                
378
    return TRUE;
379
}
380

                
381
static gboolean
382
plugin_counter_send_update_announcement(gpointer data)
383
{
384
    static int times_been_here = 0;
385
    CounterData *counter_data;
386
    NGPlugin *plugin_data = (NGPlugin*) data;
387

                
388
    g_return_val_if_fail(plugin_data != NULL, FALSE);
389
    g_return_val_if_fail(plugin_data->priv != NULL, FALSE);
390

                
391
    counter_data = (CounterData*) plugin_data->priv;
392

                
393
    /* Only send a new announcement when something got changed */
394
    if (!counter_data->data_changed_since_last_announce) {
395
        return TRUE;
396
    }
397

                
398
    ng_plugin_emit_event(plugin_data, "counter_data_changed", NULL);
399
    counter_data->data_changed_since_last_announce = FALSE;
400

                
401
    /* Periodically (every 60 seconds) save the cached data back to disk */
402
    if (times_been_here % 6 == 0) {
403
        char *errmsg = NULL;
404

                
405
        if (!plugin_counter_save_cached_data(plugin_data, &errmsg)) {
406
            ng_plugin_emit_log_msg(plugin_data, NG_LOG_LEVEL_WARNING, errmsg);
407
            g_free(errmsg);
408
        }
409
    }
410
    times_been_here++;
411

                
412
    return TRUE;
413
}
414

                
415
static CounterServerData*
416
plugin_counter_lookup_server(NGPlugin *plugin_data, const char *servername)
417
{
418
    CounterData *counter_data;
419
    CounterServerData *server_data;
420
    GList *list;
421

                
422
    g_return_val_if_fail(plugin_data != NULL, NULL);
423
    g_return_val_if_fail(servername != NULL, NULL);
424
    g_return_val_if_fail(plugin_data->priv != NULL, NULL);
425

                
426
    counter_data = (CounterData*) plugin_data->priv;
427

                
428
    list = counter_data->collected_data;
429
    while (list) {
430
        server_data = list->data;
431

                
432
        g_return_val_if_fail(server_data != NULL, NULL);
433

                
434
        if (!g_strcmp0(server_data->server_info.servername, servername)) {
435
            return server_data;
436
        }
437

                
438
        list = g_list_next(list);
439
    }
440

                
441
    /* If we got here then there's no entry available for this server. Add it now */
442
    server_data = g_slice_new0(CounterServerData);
443
    g_return_val_if_fail(plugin_data->core_funcs.config_get_server_info(servername, &server_data->server_info), NULL);
444

                
445
    server_data->num_bytes_downloaded = 0;
446
    server_data->num_parts_succeeded = 0;
447
    server_data->num_parts_failed = 0;
448

                
449
    counter_data->collected_data = g_list_append(counter_data->collected_data, server_data);
450

                
451
    return server_data;
452
}
453

                
83 454
static void
84
part_done_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num, int size, gpointer data)
455
plugin_counter_part_done_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num, int size, gpointer data)
85 456
{
457
    CounterData *counter_data;
458
    CounterServerData *server_data;
459

                
86 460
    /* We only process the part_done event as this is the only event which contains both
87 461
     * the servername and the size of the part. The event traffic_monitor_update contains
88 462
     * a more reliable amount of traffic transferred, but doesn't show the per-server
... ...
91 465
     * When only processing this event we will lose some traffic data (for missing parts
92 466
     * for example) but that amount of traffic should be low so it should be okay to
93 467
     * ignore that type of traffic */
468
    g_return_if_fail(plugin_data != NULL);
469
    g_return_if_fail(servername != NULL);
470
    g_return_if_fail(size >= 0);
471
    g_return_if_fail(plugin_data->priv != NULL);
94 472

                
473
    counter_data = (CounterData*) plugin_data->priv;
474
    server_data = plugin_counter_lookup_server(plugin_data, servername);
95 475

                
476
    g_return_if_fail(server_data != NULL);
477

                
478
    counter_data->data_changed_since_last_announce = TRUE;
479
    server_data->num_bytes_downloaded += size;
480
    server_data->num_parts_succeeded++;
96 481
}
482

                
483
static void
484
plugin_counter_part_failed_cb(NGPlugin *plugin_data, const char *servername, int conn_id, const char *collection_name, const char *subject, int part_num,int size, gboolean all_servers_tried)
485
{
486
    CounterData *counter_data;
487
    CounterServerData *server_data;
488

                
489
    g_return_if_fail(plugin_data != NULL);
490
    g_return_if_fail(servername != NULL);
491
    g_return_if_fail(plugin_data->priv != NULL);
492

                
493
    counter_data = (CounterData*) plugin_data->priv;
494
    server_data = plugin_counter_lookup_server(plugin_data, servername);
495

                
496
    g_return_if_fail(server_data != NULL);
497

                
498
    counter_data->data_changed_since_last_announce = TRUE;
499
    server_data->num_parts_failed++;
500
}
trunk/plugins/jsonrpc/jsonrpc_events.c (revision 1913)
479 479
    json_object_object_add(event, "values", obj_values);
480 480

                
481 481
    i = 0;
482
    while (values[i]) {
482
    while (values && values[i]) {
483 483
        json_object_array_add(obj_values, json_object_new_string((char*) values[i]));
484 484
        i++;
485 485
    }
trunk/glue/glue_json.c (revision 1913)
1834 1834
    GVariant *ret;
1835 1835
    GError *error = NULL;
1836 1836

                
1837
    g_return_val_if_fail(glue != NULL, NULL);
1838
    g_return_val_if_fail(plugin_name != NULL, NULL);
1839
    g_return_val_if_fail(method != NULL, NULL);
1840
    g_return_val_if_fail(parameters != NULL, NULL);
1841

                
1837 1842
    params_str = g_variant_print((GVariant*) parameters, TRUE);
1838 1843

                
1839 1844
    request_params = json_object_new_array();
trunk/base/utils.c (revision 1913)
379 379
    }
380 380
}
381 381

                
382
void
383
nntpgrab_utils_get_readable_time_stamp(time_t stamp, char *stamp_str, int stamp_str_len)
384
{
385
    struct tm *now;
386
    static int nugrft_once = 0;
387

                
388
    if (stamp <= 0) {
389
        stamp_str[0] = '\0';
390
        return;
391
    }
392

                
393
    if ((now = localtime(&stamp)) == NULL) {
394
        // Date could not be parsed
395
        stamp_str[0] = '\0';
396
        return;
397
    }
398

                
399
    if (strftime(stamp_str, stamp_str_len, "%c", now) == 0) {
400
        if (!nugrft_once) {
401
            g_error(__FILE__ ":%i buffer too small", __LINE__);
402
            nugrft_once = 1;
403
        }
404
    }
405
}
406

                
382 407
static gint
383 408
sort_folders_func(gconstpointer a, gconstpointer b)
384 409
{
trunk/base/nntpgrab_utils.h (revision 1913)
319 319
void             nntpgrab_utils_get_readable_finished_time(int estimated_time_remaining, char *time_remaining_str, int time_remaining_str_len);
320 320

                
321 321
/**
322
 * nntpgrab_utils_get_readable_time_stamp:
323
 * @stamp:                             The time stamp which needs to be transformed to a readable notation
324
 * @stamp_str:                 (out):  Pointer to the location where the human readable notation (like 'Friday February 6 2009 - 22:15') can be saved
325
 * @stamp_str_len:                     The maximum length of the time_remaining_str buffer
326
 *
327
 * Transform a time stamp into a human readable value
328
 */
329
void             nntpgrab_utils_get_readable_time_stamp(time_t stamp, char *stamp_str, int stamp_str_len);
330

                
331
/**
322 332
 * nntpgrab_utils_sanitize_text:
323 333
 * @text:      (inout):  The text which need to be sanitized
324 334
 * @length:              The length of the text
trunk/base/nntpgrab_utils.def (revision 1913)
9 9
nntpgrab_utils_calculate_estimated_time_remaining
10 10
nntpgrab_utils_get_readable_time_remaining
11 11
nntpgrab_utils_get_readable_finished_time
12
nntpgrab_utils_get_readable_time_stamp
12 13
nntpgrab_utils_get_folder_listing
13 14
nntpgrab_utils_free_folder_listing
14 15
nntpgrab_utils_sanitize_text
trunk/client/gui/gui_plugins.c (revision 1913)
420 420
    nntpgrab_glue_signal_connect(glue, "plugin_unloaded", NG_CALLBACK(on_plugin_unloaded), NULL);
421 421
}
422 422

                
423
void
424
gui_plugins_cleanup(void)
425
{
426
#ifdef HAVE_LIBPEAS
427
    if (extension_set != NULL) {
428
        g_object_unref(extension_set);
429
        extension_set = NULL;
430
    }
431
#endif
432
}
trunk/client/gui/plugins/counter/plugin_counter_gtk.c (revision 1913)
29 29
#include 
30 30
#include 
31 31

                
32
#include "nntpgrab_utils.h"
32 33
#include "nntpgrab_gui_base.h"
34
#include "gui.h"
33 35

                
34 36
#include "plugin_counter_gtk.h"
35 37
///#include "peasdemo-hello-world-configurable.h"
... ...
92 94
nntpgrab_counter_plugin_init(NntpgrabCounterPlugin *plugin)
93 95
{
94 96
    plugin->menuItemDownloadCounter = NULL;
97
    plugin->counter_data = g_slice_new0(CounterData);
95 98
}
96 99

                
97 100
static void
... ...
104 107
        plugin->menuItemDownloadCounter = NULL;
105 108
    }
106 109

                
110
    g_slice_free(CounterData, plugin->counter_data);
111

                
107 112
    G_OBJECT_CLASS (nntpgrab_counter_plugin_parent_class)->finalize (object);
108 113
}
109 114

                
... ...
114 119
    gtk_widget_hide(windowPluginCounter);
115 120
}
116 121

                
117
G_MODULE_EXPORT void
122
static void
118 123
btnCounterPluginReset_clicked(GtkWidget *widget, gpointer data)
119 124
{
120
    GVariant *ret;
125
    NGVariant *ret;
121 126
    char *errmsg = NULL;
122 127
    NntpgrabCounterPlugin *plugin = NNTPGRAB_COUNTER_PLUGIN(data);
123 128

                
124 129
    g_return_if_fail(plugin != NULL);
125 130

                
126
    ret = nntpgrab_glue_plugins_call_plugin_method(plugin->glue, PLUGIN_NAME, "reset", NULL, &errmsg);
131
    ret = nntpgrab_glue_plugins_call_plugin_method(plugin->glue,
132
                                                   PLUGIN_NAME,
133
                                                   "reset",
134
                                                   (NGVariant*) g_variant_new("()"),
135
                                                   &errmsg);
136

                
127 137
    if (!ret) {
128 138
        g_warning(_("Unable to reset download counters: %s"), errmsg);
129 139
        return;
130 140
    }
131 141

                
132
    g_variant_unref(ret);
142
    g_variant_unref((GVariant*) ret);
133 143
}
134 144

                
135 145
G_MODULE_EXPORT gboolean
... ...
139 149
    return TRUE;
140 150
}
141 151

                
152
/* Copied over from plugin_counter.c */
142 153
static void
143
menuItemDownloadCounter_clicked(GtkWidget *widget, gpointer data)
154
free_cached_data(CounterData *counter_data)
144 155
{
145
    GVariant *ret;
146
    char *errmsg = NULL;
147
    NntpgrabCounterPlugin *plugin = NNTPGRAB_COUNTER_PLUGIN(data);
156
    GList *list;
148 157

                
158
    g_return_if_fail(counter_data != NULL);
159

                
160
    list = counter_data->collected_data;
161
    while (list) {
162
        CounterServerData *server_data = (CounterServerData*) list->data;
163

                
164
        g_slice_free(CounterServerData, server_data);
165

                
166
        list = g_list_next(list);
167
    }
168

                
169
    g_list_free(counter_data->collected_data);
170
    counter_data->collected_data = NULL;
171
}
172

                
173
static void
174
update_counter_user_interface(NntpgrabCounterPlugin *plugin)
175
{
176
    GtkWidget *lblPluginCounterMeasuredSince;
177
    GtkListStore *store;
178
    GList *list;
179
    char stamp[64];
180

                
149 181
    g_return_if_fail(plugin != NULL);
182
    g_return_if_fail(plugin->counter_data != NULL);
150 183

                
151
    gtk_widget_show(plugin->windowPluginCounter);
152
    gtk_window_present(GTK_WINDOW(plugin->windowPluginCounter));
184
    nntpgrab_utils_get_readable_time_stamp(plugin->counter_data->stamp_started, stamp, sizeof(stamp));
185
    lblPluginCounterMeasuredSince = nntpgrab_gui_base_get_widget("lblPluginCounterMeasuredSince");
186
    gtk_label_set_text(GTK_LABEL(lblPluginCounterMeasuredSince), stamp);
153 187

                
188
    store = GTK_LIST_STORE(nntpgrab_gui_base_get_object("liststorePluginCounter"));
189

                
190
    g_return_if_fail(store != NULL);
191

                
192
    gtk_list_store_clear(store);
193

                
194
    list = plugin->counter_data->collected_data;
195
    while (list) {
196
        CounterServerData *server_data = (CounterServerData*) list->data;
197
        char file_size[64];
198
        GtkTreeIter iter;
199

                
200
        memset(&file_size, 0, sizeof(file_size));
201
        nntpgrab_utils_calculate_file_size(server_data->num_bytes_downloaded, file_size, sizeof(file_size));
202

                
203
        gtk_list_store_append(store, &iter);
204
        gtk_list_store_set(store, &iter, 0, server_data->servername, 1, file_size, 2, server_data->num_parts_succeeded, 3, server_data->num_parts_failed, -1);
205

                
206
        list = g_list_next(list);
207
    }
208
}
209

                
210
static void
211
process_collected_data(CounterData *counter_data, GVariant *servers_data)
212
{
213
    GVariantIter *iter_servers;
214
    GVariant *variant_server_data;
215

                
216
    g_variant_get(servers_data, "av", &iter_servers);
217
    while (g_variant_iter_loop(iter_servers, "v", &variant_server_data)) {
218
        char *key;
219
        GVariant *value;
220
        GVariantIter *iter_server;
221
        CounterServerData *server_data = g_slice_new0(CounterServerData);
222

                
223
        g_variant_get(variant_server_data, "a{sv}", &iter_server);
224
        while (g_variant_iter_loop(iter_server, "{sv}", &key, &value)) {
225
            if (!g_strcmp0(key, "servername")) {
226
                strncpy(server_data->servername, g_variant_get_string(value, NULL), sizeof(server_data->servername) - 1);
227
            } else if (!g_strcmp0(key, "num_bytes_downloaded")) {
228
                server_data->num_bytes_downloaded = g_variant_get_uint64(value);
229
            } else if (!g_strcmp0(key, "num_parts_succeeded")) {
230
                server_data->num_parts_succeeded = g_variant_get_uint64(value);
231
            } else if (!g_strcmp0(key, "num_parts_failed")) {
232
                server_data->num_parts_failed = g_variant_get_uint64(value);
233
            } else {
234
                g_print(__FILE__ ":%i Invalid parameter detected in result from 'get_collected_data' call: %s\n", __LINE__, key);
235
            }
236
        }
237

                
238
        counter_data->collected_data = g_list_append(counter_data->collected_data, server_data);
239
    }
240
}
241

                
242
static void
243
retrieve_collected_data(NntpgrabCounterPlugin *plugin)
244
{
245
    NGVariant *ret;
246
    GVariantIter *iter;
247
    char *key;
248
    GVariant *value;
249
    char *errmsg = NULL;
250

                
154 251
    ret = nntpgrab_glue_plugins_call_plugin_method(plugin->glue,
155 252
                                                   PLUGIN_NAME,
156 253
                                                   "get_collected_data",
157
                                                   g_variant_new("()"),
254
                                                   (NGVariant*) g_variant_new("()"),
158 255
                                                   &errmsg);
159 256

                
160 257
    if (!ret) {
... ...
162 259
        return;
163 260
    }
164 261

                
165
    g_variant_unref(ret);
262
    free_cached_data(plugin->counter_data);
263

                
264
    g_variant_get((GVariant*) ret, "a{sv}", &iter);
265
    while (g_variant_iter_loop(iter, "{sv}", &key, &value)) {
266
        if (!g_strcmp0(key, "stamp_started")) {
267
            plugin->counter_data->stamp_started = (time_t) g_variant_get_uint64(value);
268
        } else if (!g_strcmp0(key, "collected_data")) {
269
            process_collected_data(plugin->counter_data, value);
270
        } else {
271
            g_print(__FILE__ ":%i Invalid parameter detected in result from 'get_collected_data' call: %s\n", __LINE__, key);
272
        }
273
    }
274
    g_variant_iter_free(iter);
275

                
276
    g_variant_unref((GVariant*) ret);
277

                
166 278
}
167 279

                
168 280
static void
281
menuItemDownloadCounter_clicked(GtkWidget *widget, gpointer data)
282
{
283
    NntpgrabCounterPlugin *plugin = NNTPGRAB_COUNTER_PLUGIN(data);
284

                
285
    g_return_if_fail(plugin != NULL);
286

                
287
    gtk_widget_show(plugin->windowPluginCounter);
288
    gtk_window_present(GTK_WINDOW(plugin->windowPluginCounter));
289

                
290
    retrieve_collected_data(plugin);
291
    update_counter_user_interface(plugin);
292
}
293

                
294
static void
295
plugin_event_cb(NntpgrabGlue *obj, const char *plugin_name, const char *event_name, const char **values, gpointer data)
296
{
297
    NntpgrabCounterPlugin *plugin = NNTPGRAB_COUNTER_PLUGIN(data);
298

                
299
    g_return_if_fail(plugin != NULL);
300

                
301
    if (!strcmp(plugin_name, "Download counter")) {
302
        if (!strcmp(event_name, "counter_data_changed")) {
303
            /* Only retrieve the new statistics when the statistics window is currently visible */
304
            GtkWidget *windowPluginCounter = nntpgrab_gui_base_get_widget("windowPluginCounter");
305

                
306
            if (!gtk_widget_get_visible(windowPluginCounter)) {
307
                return;
308
            }
309

                
310
            retrieve_collected_data(plugin);
311
            update_counter_user_interface(plugin);
312
        }
313
    }
314
}
315

                
316
static void
169 317
nntpgrab_counter_plugin_activate(PeasActivatable *activatable)
170 318
{
319
    GtkWidget *btnCounterPluginReset;
171 320
    GtkWidget *menuPlugins;
172 321
    NntpgrabCounterPlugin *plugin = NNTPGRAB_COUNTER_PLUGIN(activatable);
173 322

                
... ...
177 326
    plugin->windowPluginCounter = nntpgrab_gui_base_get_widget("windowPluginCounter");
178 327
    g_return_if_fail(plugin->windowPluginCounter != NULL);
179 328

                
329
    btnCounterPluginReset = nntpgrab_gui_base_get_widget("btnCounterPluginReset");
330
    g_return_if_fail(btnCounterPluginReset != NULL);
331

                
180 332
    /* Hook up a new menu item to the 'Plugins' menu */
181 333
    menuPlugins = nntpgrab_gui_base_get_widget("menuPlugins");
182 334
    g_return_if_fail(menuPlugins != NULL);
... ...
187 339
    gtk_menu_shell_append(GTK_MENU_SHELL(menuPlugins), plugin->menuItemDownloadCounter);
188 340

                
189 341
    g_signal_connect(plugin->menuItemDownloadCounter, "activate", G_CALLBACK(menuItemDownloadCounter_clicked), plugin);
342
    g_signal_connect(btnCounterPluginReset, "clicked", G_CALLBACK(btnCounterPluginReset_clicked), plugin);
343

                
344
    nntpgrab_glue_signal_connect(glue, "plugin_event", NG_CALLBACK(plugin_event_cb), plugin);
190 345
}
191 346

                
192 347
static void
trunk/client/gui/plugins/counter/plugin_counter_gtk.h (revision 1913)
36 36
typedef struct _NntpgrabCounterPlugin NntpgrabCounterPlugin;
37 37
typedef struct _NntpgrabCounterPluginClass NntpgrabCounterPluginClass;
38 38

                
39
typedef struct _counter_server_data {
40
    char servername[128];
41
    guint64 num_bytes_downloaded;
42
    guint64 num_parts_succeeded;
43
    guint64 num_parts_failed;
44
} CounterServerData;
45

                
46
typedef struct _counter_data {
47
    time_t stamp_started;
48
    GList *collected_data;     /* List contains CounterServerData elements */
49
} CounterData;
50

                
51

                
39 52
struct _NntpgrabCounterPlugin {
40 53
    PeasExtensionBase parent_instance;
41 54

                
42 55
    NntpgrabGlue *glue;
56
    CounterData *counter_data;
43 57

                
44 58
    GtkWidget *menuItemDownloadCounter;
45 59
    GtkWidget *windowPluginCounter;
trunk/client/gui/plugins/counter/Makefile.am (revision 1913)
5 5
plugin_DATA = nntpgrab_plugin_counter_gtk.plugin
6 6

                
7 7
libnntpgrab_plugin_counter_gtk_la_SOURCES = plugin_counter_gtk.c
8
libnntpgrab_plugin_counter_gtk_la_CFLAGS = -I$(top_srcdir)/nntpgrab_core -I$(top_srcdir)/base -I$(top_srcdir)/gui_base -I$(top_srcdir)/glue $(GLIB_CFLAGS) $(GTK3_CFLAGS) $(GTK_CFLAGS) $(LIBPEAS_CFLAGS)
8
libnntpgrab_plugin_counter_gtk_la_CFLAGS = -I$(top_srcdir)/nntpgrab_core -I$(top_srcdir)/base -I$(top_srcdir)/gui_base -I$(top_srcdir)/glue -I$(top_srcdir)/client/gui $(GLIB_CFLAGS) $(GTK3_CFLAGS) $(GTK_CFLAGS) $(LIBPEAS_CFLAGS)
9 9
libnntpgrab_plugin_counter_gtk_la_LDFLAGS = -module -no-undefined -avoid-version
10 10
libnntpgrab_plugin_counter_gtk_la_LIBADD = $(GLIB_LIBS) $(GIO_LIBS) $(GTK3_LIBS) $(GTK_LIBS) $(LIBEAS_LIBS) $(top_srcdir)/gui_base/libnntpgrab_gui_base.la $(top_srcdir)/glue/libnntpgrab_glue.la
11 11

                
trunk/client/gui/plugins/counter/counter_plugin_gtk.ui (revision 1913)
1 1

2 2

3
  
3
  
4 4
  

                
5 5
    
6 6
      
7 7
      
8
      
8
      
9 9
      
10
      
10
      
11 11
      
12
      
12
      
13 13
      
14 14
    
15 15
  
... ...
17 17
    False
18 18
    5
19 19
    Download counter
20
    center-on-parent
20
    center-always
21
    720
22
    300
21 23
    
22 24
    
23 25
      

                
... ...
137 139
                1
138 140
              
139 141
            
142
            
143
              

                
144
                True
145
                False
146
                The information shown here will be updated once every 10 seconds
147
              
148
              
149
                False
150
                True
151
                5
152
                2
153
              
154
            
140 155
          
141 156
          
142 157
            True
... ...
158 173
                True
159 174
                False
160 175
                True
161
                
162 176
              
163 177
              
164 178
                False
trunk/client/gui/main.c (revision 1913)
58 58
void auto_import_initialize(void);
59 59
void autoshutdown_initialize(void);
60 60
void gui_plugins_initialize(void);
61
void gui_plugins_cleanup(void);
61 62

                
62 63
// auto_import.c
63 64
void    disable_auto_import(void);
... ...
727 728
        nntpgrab_gui_base_tray_destroy();
728 729
    }
729 730

                
731
    /* Unload the GUI plugins before cleaning up the NNTPGrabGlue */
732
    gui_plugins_cleanup();
733

                
730 734
    if (glue) {
731 735
        // Always try to disable the auto import even if it isn't in use
732 736
        if (initialized) {

Also available in: Unified diff