Statistics
| Revision:

root / trunk / nntpgrab_core / configuration.c @ 1850

History | View | Annotate | Download (33.3 KB)

1
/* 
2
    Copyright (C) 2005-2010  Erik van Pienbroek
3

                
4
    This program is free software; you can redistribute it and/or modify
5
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation; either version 2 of the License, or
7
    (at your option) any later version.
8

                
9
    This program is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
    GNU General Public License for more details.
13

                
14
    You should have received a copy of the GNU General Public License
15
    along with this program; if not, write to the Free Software
16
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
*/
18

                
19
#include 
20
#include 
21
#include 
22
#include 
23
#ifdef WIN32
24
#include 
25
#include 
26
#include 
27
#endif
28

                
29
#include "configuration.h"
30
#include "nntpgrab_internal.h"
31
#include "nntpgrab_plugin.h"
32

                
33
typedef struct ConfigurationClass ConfigurationClass;
34

                
35
Configuration *configuration_get_object(void);
36

                
37
typedef struct _config_server_details
38
{
39
    GList *groups;                  // containing ConfigGroupInfo's
40
} ConfigServerDetails;
41

                
42
struct Configuration
43
{
44
    GObject parent;
45

                
46
    GStaticRWLock rwlock;
47
    GList *servers;                 // containing ConfigServer's
48
    GHashTable *group_details;      // containing ConfigGroupInfo's
49

                
50
    NGConfigOpts opts;
51
};
52

                
53
struct ConfigurationClass
54
{
55
    GObjectClass parent;
56
};
57

                
58
enum
59
{
60
    CONFIG_CHANGED_SIGNAL,
61
    LAST_SIGNAL
62
};
63

                
64
static guint signals[LAST_SIGNAL] = { 0 };
65

                
66
G_DEFINE_TYPE(Configuration, configuration, G_TYPE_OBJECT)
67

                
68
static void
69
group_details_free_func(gpointer data)
70
{
71
    g_slice_free(ConfigGroupInfo, data);
72
}
73

                
74
static void
75
configuration_init (Configuration *obj)
76
{
77
    g_static_rw_lock_init(&obj->rwlock);
78

                
79
    obj->servers = NULL;
80
    obj->group_details = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, group_details_free_func);
81
}
82

                
83
static void
84
configuration_finalize (GObject *obj)
85
{
86
    char *errmsg = NULL;
87
    Configuration *config = CONFIGURATION(obj);
88
    GList *list;
89

                
90
    if (!(configuration_save(config, &errmsg))) {
91
        ng_plugin_emit_log_msg(NULL, NG_LOG_LEVEL_WARNING, "%s", errmsg);
92
        g_free(errmsg);
93
    }
94

                
95
    g_static_rw_lock_writer_lock(&config->rwlock);
96

                
97
    list = config->servers;
98
    while (list) {
99
        g_slice_free(NGConfigServer, list->data);
100
        list = g_list_next(list);
101
    }
102
    g_list_free(config->servers);
103

                
104
    g_hash_table_destroy(config->group_details);
105

                
106
    g_static_rw_lock_writer_unlock(&config->rwlock);
107
    g_static_rw_lock_free(&config->rwlock);
108
}
109

                
110
static void
111
configuration_class_init (ConfigurationClass *klass)
112
{
113
    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
114

                
115
    gobject_class->finalize = configuration_finalize;
116

                
117
    signals[CONFIG_CHANGED_SIGNAL] = g_signal_new  ("config_changed",
118
                                                    G_OBJECT_CLASS_TYPE (klass),
119
                                                    G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
120
                                                    0,
121
                                                    NULL, NULL,
122
                                                    g_cclosure_marshal_VOID__INT,
123
                                                    G_TYPE_NONE, 1, G_TYPE_INT);
124
}
125

                
126
Configuration *
127
configuration_new(char **errmsg, char **warnings)
128
{
129
    Configuration *config = g_object_new(CONFIGURATION_TYPE_OBJECT, NULL);
130

                
131
    if (!configuration_load(config, errmsg)) {
132
        // Load of configuration failed..probably no configuration file created yet.
133
        // Create a default configuration file
134
        g_free(*errmsg);
135
        *errmsg = NULL;
136

                
137
        if (warnings) {
138
            *warnings = g_strdup(_("Configuration file for the NNTPGrab backend could not be loaded. Default configuration file is now created"));
139
        }
140
    }
141

                
142
    return config;
143
}
144

                
145
void
146
configuration_destroy(Configuration *obj)
147
{
148
    g_object_unref(obj);
149
}
150

                
151
/** 
152
 * Retrieve a list of all the available usenet servers
153
 *
154
 * @param obj   The configuration object
155
 * @return      a GList* containing g_malloc'ed char* strings with servernames
156
 */
157
GList *
158
configuration_get_avail_servers(Configuration *obj)
159
{
160
    Configuration *config = CONFIGURATION(obj);
161
    GList *list;
162
    GList *ret = NULL;
163

                
164
    g_static_rw_lock_reader_lock(&config->rwlock);
165

                
166
    list = config->servers;
167
    while (list) {
168
        NGConfigServer *server = list->data;
169

                
170
        ret = g_list_append(ret, g_strdup(server->servername));
171

                
172
        list = g_list_next(list);
173
    }
174

                
175
    g_static_rw_lock_reader_unlock(&config->rwlock);
176

                
177
    return ret;
178
}
179

                
180
/** 
181
 * Frees the list returned by configuration_get_avail_servers()
182
 *
183
 * @param obj       The configuration object
184
 * @param servers   A GList* containing g_malloc'ed char* strings with servernames
185
 */
186
void
187
configuration_free_avail_servers(Configuration *obj, GList *servers)
188
{
189
    GList *list;
190

                
191
    list = servers;
192
    while (list) {
193
        g_free(list->data);
194
        list = g_list_next(list);
195
    }
196

                
197
    g_list_free(servers);
198
}
199

                
200
/** 
201
 * Retrieve information about a specific usenet server
202
 *
203
 * @param obj           The Configuration object
204
 * @param servername    The name of the usenet server whose info should be lookup up
205
 * @return              A g_slice_new'ed ConfigServer structure containing the requested info or NULL when the servername could not be found
206
 */
207
NGConfigServer *
208
configuration_get_server_info(Configuration *obj, const char *servername)
209
{
210
    Configuration *config = CONFIGURATION(obj);
211
    GList *list;
212

                
213
    g_static_rw_lock_reader_lock(&config->rwlock);
214

                
215
    list = config->servers;
216
    while (list) {
217
        NGConfigServer *server = list->data;
218

                
219
        if (!strcmp(server->servername, servername)) {
220
            NGConfigServer *ret = g_slice_new0(NGConfigServer);
221

                
222
            memcpy(ret, server, sizeof(NGConfigServer));
223

                
224
            g_static_rw_lock_reader_unlock(&config->rwlock);
225

                
226
            return ret;
227
        }
228

                
229
        list = g_list_next(list);
230
    }
231

                
232
    g_static_rw_lock_reader_unlock(&config->rwlock);
233

                
234
    return NULL;
235
}
236

                
237
/** 
238
 * Add a new server to the list of known usenet servers
239
 *
240
 * @param obj           The configuration object
241
 * @param new_server    The structure containing the settings of the new server
242
 * @param errmsg        If an error occurs, an error message will be set in this variable
243
 * @return              TRUE on success, FALSE on error (errmsg will be set)
244
 */
245
gboolean
246
configuration_add_server(Configuration *obj, NGConfigServer new_server, char **errmsg)
247
{
248
    Configuration *config = CONFIGURATION(obj);
249
    NGConfigServer *server;
250
    GList *list;
251

                
252
    // Check is the new_server contains sane data
253
    g_assert(new_server.port > 0 && new_server.port < 65536);
254
    g_assert(new_server.max_threads > 0);
255

                
256
    if (!strcmp(new_server.servername, "options")) {
257
        if (errmsg) {
258
            *errmsg = g_strdup_printf("%s%s", _("The servername 'options' cannot be used.\n"), _("Please use a different servername"));
259
        }
260
        return FALSE;
261
    }
262

                
263
    // Check if the servername of the new_server is already known
264
    g_static_rw_lock_reader_lock(&config->rwlock);
265

                
266
    list = config->servers;
267
    while (list) {
268
        server = list->data;
269

                
270
        if (!strcmp(new_server.servername, server->servername)) {
271
            // servername already exists, refuse the addition of a new server
272
            g_static_rw_lock_reader_unlock(&config->rwlock);
273

                
274
            if (errmsg) {
275
                *errmsg = g_strdup_printf("%s%s", _("The given servername already exists in the configuration.\n"), _("Please use a different servername"));
276
            }
277

                
278
            return FALSE;
279
        }
280

                
281
        list = g_list_next(list);
282
    }
283

                
284
    // Prevent the addition of more than MAX_NNTP_SERVERS servers
285
    if (g_list_length(config->servers) == MAX_NNTP_SERVERS - 1) {
286
        g_static_rw_lock_reader_unlock(&config->rwlock);
287

                
288
        if (errmsg) {
289
            *errmsg = g_strdup(_("There are too many servers configured"));
290
        }
291

                
292
        return FALSE;
293
    }
294

                
295
    g_static_rw_lock_reader_unlock(&config->rwlock);
296

                
297
    // servername not found, it is safe to add the new configuration
298
    g_static_rw_lock_writer_lock(&config->rwlock);
299

                
300
    server = g_slice_new0(NGConfigServer);
301
    memcpy(server, &new_server, sizeof(NGConfigServer));
302
    config->servers = g_list_append(config->servers, server);
303

                
304
    g_static_rw_lock_writer_unlock(&config->rwlock);
305

                
306
    // Notify listeners that the configuration has changed
307
    g_static_rw_lock_reader_lock(&config->rwlock);
308
    g_signal_emit (config, signals[CONFIG_CHANGED_SIGNAL], 0, CONFIG_CHANGED_SERVER_ADDED);
309
    g_static_rw_lock_reader_unlock(&config->rwlock);
310

                
311
    return TRUE;
312
}
313

                
314
/** 
315
 * Delete the settings from the specified servername from the list of known usenet servers
316
 *
317
 * @param obj           The configuration object
318
 * @param servername    The name of the server whose settings need to be removed
319
 * @param errmsg        If an error occurs, an error message will be set in this variable
320
 *
321
 * @return              TRUE on success, FALSE on error (errmsg will be set)
322
 */
323
gboolean
324
configuration_del_server(Configuration *obj, const char *servername, char **errmsg)
325
{
326
    Configuration *config = CONFIGURATION(obj);
327
    NGConfigServer *server;
328
    GList *list;
329

                
330
    // Check if the servername is known
331
    g_static_rw_lock_reader_lock(&config->rwlock);
332

                
333
    list = config->servers;
334
    while (list) {
335
        server = list->data;
336

                
337
        if (!strcmp(servername, server->servername)) {
338
            // servername exists, remove it from the list
339
            g_static_rw_lock_reader_unlock(&config->rwlock);
340

                
341
            g_static_rw_lock_writer_lock(&config->rwlock);
342

                
343
            g_slice_free(NGConfigServer, server);
344
            config->servers = g_list_remove(config->servers, server);
345

                
346
            g_static_rw_lock_writer_unlock(&config->rwlock);
347

                
348
            // Notify listeners that the configuration has changed
349
            g_static_rw_lock_reader_lock(&config->rwlock);
350
            g_signal_emit (config, signals[CONFIG_CHANGED_SIGNAL], 0, CONFIG_CHANGED_SERVER_DELETED);
351
            g_static_rw_lock_reader_unlock(&config->rwlock);
352

                
353
            return TRUE;
354
        }
355

                
356
        list = g_list_next(list);
357
    }
358

                
359
    g_static_rw_lock_reader_unlock(&config->rwlock);
360

                
361
    if (errmsg) {
362
        *errmsg = g_strdup(_("The given servername doesn't exist in the NNTPGrab configuration"));
363
    }
364

                
365
    return FALSE;
366
}
367

                
368
/** 
369
 * Change the settings from the specified servername in the list of known usenet servers
370
 *
371
 * @param obj           The configuration object
372
 * @param servername    The name of the server whose settings need to be changed
373
 * @param new_server    The new settings of the server which need to be saved
374
 * @param errmsg        If an error occurs, an error message will be set in this variable
375
 *
376
 * @return              TRUE on success, FALSE on error (errmsg will be set)
377
 */
378
gboolean
379
configuration_edit_server(Configuration *obj, const char *servername, NGConfigServer new_server, char **errmsg)
380
{
381
    Configuration *config = CONFIGURATION(obj);
382
    NGConfigServer *server;
383
    GList *list;
384

                
385
    if (!strcmp(new_server.servername, "options")) {
386
        if (errmsg) {
387
            *errmsg = g_strdup_printf("%s%s", _("The servername 'options' cannot be used.\n"), _("Please use a different servername"));
388
        }
389
        return FALSE;
390
    }
391

                
392
    // Check if the servername of the new_server is already known
393
    g_static_rw_lock_reader_lock(&config->rwlock);
394

                
395
    list = config->servers;
396
    while (list) {
397
        server = list->data;
398

                
399
        if (!strcmp(servername, server->servername)) {
400
            // servername exists, edit the settings
401
            g_static_rw_lock_reader_unlock(&config->rwlock);
402

                
403
            g_static_rw_lock_writer_lock(&config->rwlock);
404

                
405
            // apply the new settings
406
            memcpy(server, &new_server, sizeof(NGConfigServer));
407

                
408
            g_static_rw_lock_writer_unlock(&config->rwlock);
409

                
410
            // Notify listeners that the configuration has changed
411
            g_static_rw_lock_reader_lock(&config->rwlock);
412
            g_signal_emit (config, signals[CONFIG_CHANGED_SIGNAL], 0, CONFIG_CHANGED_SERVER_ADDED);
413
            g_static_rw_lock_reader_unlock(&config->rwlock);
414

                
415
            return TRUE;
416
        }
417

                
418
        list = g_list_next(list);
419
    }
420

                
421
    g_static_rw_lock_reader_unlock(&config->rwlock);
422

                
423
    if (errmsg) {
424
        *errmsg = g_strdup(_("The given servername doesn't exist in the NNTPGrab configuration"));
425
    }
426

                
427
    return FALSE;
428
}
429

                
430
/** 
431
 * Retrieves the configuration options
432
 *
433
 * @param obj       The configuration object
434
 * @return          A structure containing configuration data
435
 */
436
NGConfigOpts
437
configuration_get_opts(Configuration *obj)
438
{
439
    Configuration *config = CONFIGURATION(obj);
440
    NGConfigOpts ret;
441

                
442
    g_static_rw_lock_reader_lock(&config->rwlock);
443

                
444
    ret = config->opts;
445

                
446
    g_static_rw_lock_reader_unlock(&config->rwlock);
447

                
448
    return ret;
449
}
450

                
451
/** 
452
 * Set the configuration options
453
 *
454
 * @param obj   The configuration object
455
 * @param opts  A structure containing configuration data
456
 */
457
void
458
configuration_set_opts(Configuration *obj, NGConfigOpts opts)
459
{
460
    Configuration *config = CONFIGURATION(obj);
461

                
462
    // Hack for Win32, if the download folder or temp folder is the root of a drive, append an extra \ to it
463
#ifdef WIN32
464
    if (strlen(opts.download_directory) == 2 && opts.download_directory[1] == ':') {
465
        opts.download_directory[2] = '\\';
466
        opts.download_directory[3] = '\0';
467
    }
468

                
469
    if (strlen(opts.temp_directory) == 2 && opts.temp_directory[1] == ':') {
470
        opts.temp_directory[2] = '\\';
471
        opts.temp_directory[3] = '\0';
472
    }
473

                
474
    if (strlen(opts.auto_import_directory) == 2 && opts.auto_import_directory[1] == ':') {
475
        opts.temp_directory[2] = '\\';
476
        opts.temp_directory[3] = '\0';
477
    }
478
#endif
479

                
480
    /* Make sure that the download and temp folder really exist */
481
    if (!g_file_test(opts.download_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ||
482
        !g_file_test(opts.temp_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ||
483
        !g_file_test(opts.auto_import_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
484

                
485
        /* Refuse to accept the new configuration */
486
        return;
487
    }
488

                
489
    g_static_rw_lock_writer_lock(&config->rwlock);
490
    config->opts = opts;
491
    g_static_rw_lock_writer_unlock(&config->rwlock);
492

                
493
    // Notify listeners that the configuration has changed
494
    g_static_rw_lock_reader_lock(&config->rwlock);
495
    g_signal_emit (config, signals[CONFIG_CHANGED_SIGNAL], 0, CONFIG_CHANGED_OPTS_CHANGED);
496
    g_static_rw_lock_reader_unlock(&config->rwlock);
497
}
498

                
499
/** 
500
 * Get the details concerning the given server and newsgroup
501
 *
502
 * @param obj           The configuration object
503
 * @param servername    The name of the server
504
 * @param newsgroup     The name of the newsgroup
505
 *
506
 * @return              Structure containing the details
507
 */
508
ConfigGroupInfo
509
configuration_get_newsgroup_info(Configuration *obj, const char *servername, const char *newsgroup)
510
{
511
    ConfigGroupInfo ret;
512
    ConfigGroupInfo *info;
513
    Configuration *config = CONFIGURATION(obj);
514
    char *hash;
515

                
516
    g_static_rw_lock_reader_lock(&config->rwlock);
517

                
518
    hash = g_strdup_printf("%s_%s", servername, newsgroup);
519
    info = g_hash_table_lookup(config->group_details, hash);
520
    g_free(hash);
521

                
522
    if (info) {
523
        memcpy(&ret, info, sizeof(ConfigGroupInfo));
524
    } else {
525
        memset(&ret, 0, sizeof(ConfigGroupInfo));
526
        strncpy((char *) ret.newsgroup, newsgroup, sizeof(ret.newsgroup));
527
    }
528

                
529
    g_static_rw_lock_reader_unlock(&config->rwlock);
530

                
531
    return ret;
532
}
533

                
534
/** 
535
 * Set the details concerning the given server and newsgroup
536
 *
537
 * @param obj           The configuration object
538
 * @param servername    The name of the server
539
 * @param newsgroup     The name of the newsgroup
540
 * @param info          Structure containing the details
541
 */
542
void
543
configuration_set_newsgroup_info(Configuration *obj, const char *servername, const char *newsgroup, ConfigGroupInfo info)
544
{
545
    Configuration *config = CONFIGURATION(obj);
546
    ConfigGroupInfo *info_new;
547
    char *hash;
548

                
549
    g_static_rw_lock_writer_lock(&config->rwlock);
550

                
551
    info_new = g_slice_new(ConfigGroupInfo);
552
    memcpy(info_new, &info, sizeof(ConfigGroupInfo));
553

                
554
    hash = g_strdup_printf("%s_%s", servername, newsgroup);
555
    g_hash_table_replace(config->group_details, hash, info_new);
556
    g_free(hash);
557

                
558
    g_static_rw_lock_writer_unlock(&config->rwlock);
559
}
560

                
561
/** 
562
 * Load all settings from a local file on the harddrive
563
 *
564
 * @param obj           The configuration object
565
 * @param errmsg        A pointer to a char* to save a possible error message
566
 * @return              TRUE on success, FALSE on failure (errmsg will be set and needs to be g_free'd after use)
567
 */
568
gboolean
569
configuration_load(Configuration *obj, char **errmsg)
570
{
571
    Configuration *config = CONFIGURATION(obj);
572
    GKeyFile *keyfile;
573
    GError *err = NULL;
574
    char *filename;
575
    char **groups;
576
    int i;
577
    GList *list;
578

                
579
    g_static_rw_lock_writer_lock(&config->rwlock);
580

                
581
    // Free the already known settings
582
    list = config->servers;
583
    while (list) {
584
        NGConfigServer *server = list->data;
585

                
586
        g_slice_free(NGConfigServer, server);
587

                
588
        list = g_list_next(list);
589
    }
590

                
591
    g_list_free(config->servers);
592
    config->servers = NULL;
593

                
594
    // Try to load the config file
595
    if (g_getenv("NNTPGRAB_CONFIG_DIR")) {
596
        filename = g_build_filename(g_getenv("NNTPGRAB_CONFIG_DIR"), "NNTPGrab", "nntpgrab.conf", NULL);
597
    } else {
598
        filename = g_build_filename(g_get_user_config_dir(), "NNTPGrab", "nntpgrab.conf", NULL);
599
    }
600
    keyfile = g_key_file_new();
601
    if (!g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &err)) {
602
        char *tmp;
603
        const char *homedir;
604

                
605
        if (errmsg) {
606
            *errmsg = g_strdup_printf(_("configuration_load(): Error while opening file '%s'\n%s"), filename, err->message);
607
        }
608

                
609
        g_free(filename);
610
        g_error_free(err);
611
        g_key_file_free(keyfile);
612

                
613
#ifdef WIN32
614
        homedir = g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS);
615
#else
616
        if (g_getenv("NNTPGRAB_DEFAULT_DL_DIR")) {
617
            homedir = g_getenv("NNTPGRAB_DEFAULT_DL_DIR");
618
        } else {
619
            homedir = g_get_home_dir();
620
        }
621
#endif
622
        // Set up sane defaults
623
        tmp = g_build_path(G_DIR_SEPARATOR_S, homedir, "NNTPGrab", "Downloads", NULL);
624
        strncpy(config->opts.download_directory, tmp, sizeof(config->opts.download_directory));
625
        g_mkdir_with_parents(tmp, 0700);
626
        g_free(tmp);
627

                
628
        tmp = g_build_path(G_DIR_SEPARATOR_S, homedir, "NNTPGrab", "Temp", NULL);
629
        strncpy(config->opts.temp_directory, tmp, sizeof(config->opts.temp_directory));
630
        g_mkdir_with_parents(tmp, 0700);
631
        g_free(tmp);
632

                
633
        tmp = g_build_path(G_DIR_SEPARATOR_S, homedir, "NNTPGrab", "NZB", NULL);
634
        strncpy(config->opts.auto_import_directory, tmp, sizeof(config->opts.auto_import_directory));
635
        g_mkdir_with_parents(tmp, 0700);
636
        g_free(tmp);
637

                
638
        config->opts.enable_par2_repair = TRUE;
639
        config->opts.enable_intelligent_par2_downloading = TRUE;
640
        config->opts.enable_auto_unpack = TRUE;
641
        config->opts.enable_bandwidth_shaping = FALSE;
642
        config->opts.max_bandwidth = 100;
643
        config->opts.enable_webserver = FALSE;
644
        config->opts.webserver_port = 5423;
645
        config->opts.enable_logger = FALSE;
646
        config->opts.auto_remove_files_after_repair = FALSE;
647
        config->opts.auto_remove_files_after_unpack = FALSE;
648

                
649
        g_static_rw_lock_writer_unlock(&config->rwlock);
650

                
651
        return FALSE;
652
    }
653

                
654
    g_free(filename);
655

                
656
    groups = g_key_file_get_groups(keyfile, NULL);
657
    i = 0;
658
    while (groups[i]) {
659
        char *value;
660
        NGConfigServer *server;
661

                
662
        if (!strcmp(groups[i], "options")) {
663
            char *tmp;
664

                
665
            tmp = g_key_file_get_string(keyfile, groups[i], "download_directory", NULL);
666
            if (tmp) {
667
                strncpy(config->opts.download_directory, tmp, sizeof(config->opts.download_directory));
668
                g_free(tmp);
669
            }
670

                
671
            tmp = g_key_file_get_string(keyfile, groups[i], "temp_directory", NULL);
672
            if (tmp) {
673
                strncpy(config->opts.temp_directory, tmp, sizeof(config->opts.temp_directory));
674
                g_free(tmp);
675
            }
676

                
677
            if (g_key_file_has_key(keyfile, groups[i], "enable_intelligent_par2_downloading", NULL)) {
678
                config->opts.enable_intelligent_par2_downloading = g_key_file_get_boolean(keyfile, groups[i], "enable_intelligent_par2_downloading", NULL);
679
            } else {
680
                config->opts.enable_intelligent_par2_downloading = TRUE;
681
            }
682

                
683
            if (g_key_file_has_key(keyfile, groups[i], "enable_par2_repair", NULL)) {
684
                config->opts.enable_par2_repair = g_key_file_get_boolean(keyfile, groups[i], "enable_par2_repair", NULL);
685
            } else {
686
                config->opts.enable_par2_repair = TRUE;
687
            }
688

                
689
            if (g_key_file_has_key(keyfile, groups[i], "enable_auto_unpack", NULL)) {
690
                config->opts.enable_auto_unpack = g_key_file_get_boolean(keyfile, groups[i], "enable_auto_unpack", NULL);
691
            } else {
692
                config->opts.enable_auto_unpack = TRUE;
693
            }
694

                
695
            tmp = g_key_file_get_string(keyfile, groups[i], "auto_import_directory", NULL);
696
            if (tmp) {
697
                strncpy(config->opts.auto_import_directory, tmp, sizeof(config->opts.auto_import_directory));
698
                g_free(tmp);
699
            }
700

                
701
            if (g_key_file_has_key(keyfile, groups[i], "enable_auto_import", NULL)) {
702
                config->opts.enable_auto_import = g_key_file_get_boolean(keyfile, groups[i], "enable_auto_import", NULL);
703
            } else {
704
                config->opts.enable_auto_import = TRUE;
705
            }
706

                
707
            if (g_key_file_has_key(keyfile, groups[i], "move_file_after_auto_import", NULL)) {
708
                config->opts.move_file_after_auto_import = g_key_file_get_boolean(keyfile, groups[i], "move_file_after_auto_import", NULL);
709
            } else {
710
                config->opts.move_file_after_auto_import = TRUE;
711
            }
712

                
713
            if (g_key_file_has_key(keyfile, groups[i], "enable_bandwidth_shaping", NULL)) {
714
                config->opts.enable_bandwidth_shaping = g_key_file_get_boolean(keyfile, groups[i], "enable_bandwidth_shaping", NULL);
715
            } else {
716
                config->opts.enable_bandwidth_shaping = FALSE;
717
            }
718

                
719
            if (g_key_file_has_key(keyfile, groups[i], "max_bandwidth", NULL)) {
720
                config->opts.max_bandwidth = g_key_file_get_integer(keyfile, groups[i], "max_bandwidth", NULL);
721
            } else {
722
                config->opts.max_bandwidth = 100;
723
            }
724

                
725
            if (g_key_file_has_key(keyfile, groups[i], "enable_webserver", NULL)) {
726
                config->opts.enable_webserver = g_key_file_get_boolean(keyfile, groups[i], "enable_webserver", NULL);
727
            } else {
728
                config->opts.enable_webserver = FALSE;
729
            }
730

                
731
            if (g_key_file_has_key(keyfile, groups[i], "webserver_port", NULL)) {
732
                config->opts.webserver_port = g_key_file_get_integer(keyfile, groups[i], "webserver_port", NULL);
733
            } else {
734
                config->opts.webserver_port = 5423;
735
            }
736

                
737
            if (g_key_file_has_key(keyfile, groups[i], "enable_logger", NULL)) {
738
                config->opts.enable_logger = g_key_file_get_boolean(keyfile, groups[i], "enable_logger", NULL);
739
            } else {
740
                config->opts.enable_logger = FALSE;
741
            }
742

                
743
            if (g_key_file_has_key(keyfile, groups[i], "auto_remove_files_after_repair", NULL)) {
744
                config->opts.auto_remove_files_after_repair = g_key_file_get_boolean(keyfile, groups[i], "auto_remove_files_after_repair", NULL);
745
            } else {
746
                config->opts.auto_remove_files_after_repair = FALSE;
747
            }
748

                
749
            if (g_key_file_has_key(keyfile, groups[i], "auto_remove_files_after_unpack", NULL)) {
750
                config->opts.auto_remove_files_after_unpack = g_key_file_get_boolean(keyfile, groups[i], "auto_remove_files_after_unpack", NULL);
751
            } else {
752
                config->opts.auto_remove_files_after_unpack = FALSE;
753
            }
754

                
755
            /* backwards compatibility */
756
            if (g_key_file_has_key(keyfile, groups[i], "auto_remove_files", NULL)) {
757
                config->opts.auto_remove_files_after_repair = g_key_file_get_boolean(keyfile, groups[i], "auto_remove_files", NULL);
758
                config->opts.auto_remove_files_after_unpack = g_key_file_get_boolean(keyfile, groups[i], "auto_remove_files", NULL);
759
            }
760
            i++;
761
            continue;
762
        }
763

                
764
        server = g_slice_new(NGConfigServer);
765

                
766
        strncpy(server->servername, groups[i], sizeof(server->servername));
767

                
768
        value = g_key_file_get_string(keyfile, groups[i], "hostname", NULL);
769
        if (!value) {
770
            g_print(_("No hostname could be found for servername '%s'. Ignoring server\n"), groups[i]);
771
            i++;
772
            continue;
773
        }
774

                
775
        strncpy(server->hostname, value, sizeof(server->hostname));
776
        g_free(value);
777

                
778
        server->port = g_key_file_get_integer(keyfile, groups[i], "port", NULL);
779

                
780
        value = g_key_file_get_string(keyfile, groups[i], "username", NULL);
781
        if (value) {
782
            strncpy(server->username, value, sizeof(server->username));
783
            g_free(value);
784
        }
785

                
786
        value = g_key_file_get_string(keyfile, groups[i], "password", NULL);
787
        if (value) {
788
            strncpy(server->password, value, sizeof(server->password));
789
            g_free(value);
790
        }
791

                
792
        server->max_threads = g_key_file_get_integer(keyfile, groups[i], "max_threads", NULL);
793
        server->use_ssl = g_key_file_get_boolean(keyfile, groups[i], "use_ssl", NULL);
794

                
795
        // backwards compatibility
796
        if (!g_key_file_has_key(keyfile, groups[i], "priority", NULL)) {
797
            server->priority = SERVER_PRIORITY_NORMAL;
798
        } else {
799
            server->priority = g_key_file_get_integer(keyfile, groups[i], "priority", NULL);
800
        }
801

                
802
        if (!g_key_file_has_key(keyfile, groups[i], "enabled", NULL)) {
803
            server->enabled = TRUE;
804
        } else {
805
            server->enabled = g_key_file_get_boolean(keyfile, groups[i], "enabled", NULL);
806
        }
807

                
808
        if (!g_key_file_has_key(keyfile, groups[i], "send_group_command", NULL)) {
809
            server->send_group_command = FALSE;
810
        } else {
811
            server->send_group_command = g_key_file_get_boolean(keyfile, groups[i], "send_group_command", NULL);
812
        }
813

                
814
        config->servers = g_list_append(config->servers, server);
815

                
816
        i++;
817
    }
818

                
819
    g_strfreev(groups);
820

                
821
    g_key_file_free(keyfile);
822

                
823
    // Do we have sane values for the download and temp directory ?
824
#ifdef WIN32
825
    if (strlen(config->opts.download_directory) == 2) {
826
        config->opts.download_directory[2] = '\\';
827
        config->opts.download_directory[3] = '\0';
828
    }
829

                
830
    if (strlen(config->opts.temp_directory) == 2) {
831
        config->opts.temp_directory[2] = '\\';
832
        config->opts.temp_directory[3] = '\0';
833
    }
834
#endif
835

                
836
    if (!g_file_test(config->opts.download_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
837
        char *tmp;
838

                
839
#ifdef WIN32
840
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS), "NNTPGrab", "Downloads", NULL);
841
#else
842
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_home_dir(), "NNTPGrab", "Downloads", NULL);
843
#endif
844
        strncpy(config->opts.download_directory, tmp, sizeof(config->opts.download_directory));
845
        g_mkdir_with_parents(tmp, 0700);
846
        g_free(tmp);
847
    }
848

                
849
    if (!g_file_test(config->opts.temp_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
850
        char *tmp;
851

                
852
#ifdef WIN32
853
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS), "NNTPGrab", "Temp", NULL);
854
#else
855
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_home_dir(), "NNTPGrab", "Temp", NULL);
856
#endif
857
        strncpy(config->opts.temp_directory, tmp, sizeof(config->opts.temp_directory));
858
        g_mkdir_with_parents(tmp, 0700);
859
        g_free(tmp);
860
    }
861

                
862
    if (!g_file_test(config->opts.auto_import_directory, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
863
        char *tmp;
864

                
865
#ifdef WIN32
866
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS), "NNTPGrab", "NZB", NULL);
867
#else
868
        tmp = g_build_path(G_DIR_SEPARATOR_S, g_get_home_dir(), "NNTPGrab", "NZB", NULL);
869
#endif
870
        strncpy(config->opts.auto_import_directory, tmp, sizeof(config->opts.auto_import_directory));
871
        g_mkdir_with_parents(tmp, 0700);
872
        g_free(tmp);
873
    }
874

                
875
    g_static_rw_lock_writer_unlock(&config->rwlock);
876

                
877
    // Notify listeners that the configuration has changed
878
    g_static_rw_lock_reader_lock(&config->rwlock);
879
    g_signal_emit (config, signals[CONFIG_CHANGED_SIGNAL], 0, CONFIG_CHANGED_SERVER_ADDED | CONFIG_CHANGED_SERVER_DELETED);
880
    g_static_rw_lock_reader_unlock(&config->rwlock);
881

                
882
    return TRUE;
883
}
884

                
885
/** 
886
 * Save all settings to a local file on the harddrive
887
 *
888
 * @param obj           The configuration object
889
 * @param errmsg        A pointer to a char* to save a possible error message
890
 * @return              TRUE on success, FALSE on failure (errmsg will be set and needs to be g_free'd after use)
891
 */
892
gboolean
893
configuration_save(Configuration *obj, char **errmsg)
894
{
895
    Configuration *config = CONFIGURATION(obj);
896
    GKeyFile *keyfile;
897
    GError *err = NULL;
898
    char *filename;
899
    char *dirname;
900
    char *contents;
901
    GList *list;
902
    const char *config_dir;
903

                
904
    g_static_rw_lock_reader_lock(&config->rwlock);
905

                
906
    keyfile = g_key_file_new();
907

                
908
    g_key_file_set_string(keyfile, "options", "download_directory", config->opts.download_directory);
909
    g_key_file_set_string(keyfile, "options", "temp_directory", config->opts.temp_directory);
910
    g_key_file_set_boolean(keyfile, "options", "enable_intelligent_par2_downloading", config->opts.enable_intelligent_par2_downloading);
911
    g_key_file_set_boolean(keyfile, "options", "enable_par2_repair", config->opts.enable_par2_repair);
912
    g_key_file_set_boolean(keyfile, "options", "enable_auto_unpack", config->opts.enable_auto_unpack);
913
    g_key_file_set_boolean(keyfile, "options", "enable_auto_import", config->opts.enable_auto_import);
914
    g_key_file_set_string (keyfile, "options", "auto_import_directory", config->opts.auto_import_directory);
915
    g_key_file_set_boolean(keyfile, "options", "move_file_after_auto_import", config->opts.move_file_after_auto_import);
916
    g_key_file_set_boolean(keyfile, "options", "enable_bandwidth_shaping", config->opts.enable_bandwidth_shaping);
917
    g_key_file_set_integer(keyfile, "options", "max_bandwidth", config->opts.max_bandwidth);
918
    g_key_file_set_boolean(keyfile, "options", "enable_webserver", config->opts.enable_webserver);
919
    g_key_file_set_integer(keyfile, "options", "webserver_port", config->opts.webserver_port);
920
    g_key_file_set_boolean(keyfile, "options", "enable_logger", config->opts.enable_logger);
921
    g_key_file_set_boolean(keyfile, "options", "auto_remove_files_after_repair", config->opts.auto_remove_files_after_repair);
922
    g_key_file_set_boolean(keyfile, "options", "auto_remove_files_after_unpack", config->opts.auto_remove_files_after_unpack);
923

                
924
    list = config->servers;
925
    while (list) {
926
        NGConfigServer *server = list->data;
927

                
928
        g_key_file_set_string (keyfile, server->servername, "hostname", server->hostname);
929
        g_key_file_set_integer(keyfile, server->servername, "port", server->port);
930
        g_key_file_set_string (keyfile, server->servername, "username", server->username);
931
        g_key_file_set_string (keyfile, server->servername, "password", server->password);
932
        g_key_file_set_integer(keyfile, server->servername, "max_threads", server->max_threads);
933
        g_key_file_set_boolean(keyfile, server->servername, "send_group_command", server->send_group_command);
934
        g_key_file_set_boolean(keyfile, server->servername, "use_ssl", server->use_ssl);
935
        g_key_file_set_integer(keyfile, server->servername, "priority", server->priority);
936
        g_key_file_set_boolean(keyfile, server->servername, "enabled", server->enabled);
937

                
938
        list = g_list_next(list);
939
    }
940

                
941
    contents = g_key_file_to_data(keyfile, NULL, &err);
942
    if (!contents) {
943
        if (errmsg) {
944
            *errmsg = g_strdup(err->message);
945
        }
946

                
947
        g_error_free(err);
948
        g_free(contents);
949
        g_key_file_free(keyfile);
950
        g_static_rw_lock_reader_unlock(&config->rwlock);
951

                
952
        return FALSE;
953
    }
954

                
955
    // Create the folder ~/.config/NNTPGrab if it didn't exist already
956
    if (g_getenv("NNTPGRAB_CONFIG_DIR")) {
957
        config_dir = g_getenv("NNTPGRAB_CONFIG_DIR");
958
    } else {
959
        config_dir = g_get_user_config_dir();
960
    }
961

                
962
    dirname = g_build_path(G_DIR_SEPARATOR_S, config_dir, "NNTPGrab", NULL);
963
    g_mkdir_with_parents(dirname, 0700);
964
    g_free(dirname);
965

                
966
    filename = g_build_filename(config_dir, "NNTPGrab", "nntpgrab.conf", NULL);
967

                
968
    if (!g_file_set_contents(filename, contents, -1, &err)) {
969
        if (errmsg) {
970
            *errmsg = g_strdup_printf(_("configuration_save(): Error while opening file '%s'\n%s"), filename, err->message);
971
        }
972

                
973
        g_error_free(err);
974
        g_free(contents);
975
        g_free(filename);
976
        g_key_file_free(keyfile);
977
        g_static_rw_lock_reader_unlock(&config->rwlock);
978

                
979
        return FALSE;
980
    }
981

                
982
    g_free(contents);
983
    g_free(filename);
984
    g_key_file_free(keyfile);
985
    g_static_rw_lock_reader_unlock(&config->rwlock);
986

                
987
    return TRUE;
988
}