~ubuntu-branches/ubuntu/karmic/gtk-gnutella/karmic

« back to all changes in this revision

Viewing changes to src/ui/gtk/search_xml.c

  • Committer: Bazaar Package Importer
  • Author(s): Anand Kumria
  • Date: 2005-08-04 11:32:05 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20050804113205-q746i4lgo3rtlegn
Tags: 0.95.4-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id: search_xml.c,v 1.23 2005/06/25 01:37:44 daichik Exp $
 
3
 *
 
4
 * Copyright (c) 2002-2003, Richard Eckart
 
5
 *
 
6
 *----------------------------------------------------------------------
 
7
 * This file is part of gtk-gnutella.
 
8
 *
 
9
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 
10
 *  it under the terms of the GNU General Public License as published by
 
11
 *  the Free Software Foundation; either version 2 of the License, or
 
12
 *  (at your option) any later version.
 
13
 *
 
14
 *  gtk-gnutella is distributed in the hope that it will be useful,
 
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 *  GNU General Public License for more details.
 
18
 *
 
19
 *  You should have received a copy of the GNU General Public License
 
20
 *  along with gtk-gnutella; if not, write to the Free Software
 
21
 *  Foundation, Inc.:
 
22
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
23
 *----------------------------------------------------------------------
 
24
 */
 
25
 
 
26
/**
 
27
 * @ingroup gtk
 
28
 * @file
 
29
 *
 
30
 * Persistance for searches and filters in XML format.
 
31
 *
 
32
 * @author Richard Eckart
 
33
 * @date 2002-2003
 
34
 */
 
35
 
 
36
#include "gui.h"
 
37
 
 
38
RCSID("$Id: search_xml.c,v 1.23 2005/06/25 01:37:44 daichik Exp $");
 
39
 
 
40
#include <libxml/tree.h>
 
41
#include <libxml/parser.h>
 
42
 
 
43
#include "filter_core.h"
 
44
#include "search_xml.h"
 
45
#include "settings.h"
 
46
#include "search.h"
 
47
 
 
48
#include "if/gui_property_priv.h"
 
49
#include "if/gnet_property.h"
 
50
#include "if/bridge/ui2c.h"
 
51
 
 
52
#include "lib/glib-missing.h"
 
53
#include "lib/utf8.h"
 
54
#include "lib/override.h"               /* Must be the last header included */
 
55
 
 
56
#define GLOBAL_PRE 0
 
57
#define GLOBAL_POST 1
 
58
 
 
59
#define TO_BOOL(v) ((v) != 0 ? TRUE : FALSE)
 
60
 
 
61
typedef struct node_parser {
 
62
    const gchar *name;
 
63
    void (*parser_func)(xmlNodePtr, gpointer);
 
64
} node_parser_t;
 
65
 
 
66
 
 
67
/*
 
68
 * The rulesets are defined in filter.c, but I don't want
 
69
 * them to be public. They are only needed here. As are
 
70
 * global_ruleset_pre and global_ruleset_post.
 
71
 */
 
72
extern void dump_ruleset(GList *ruleset);
 
73
extern void dump_filter(filter_t *filter);
 
74
 
 
75
extern GList *filters;
 
76
extern GList *filters_current;
 
77
 
 
78
/*
 
79
 * Private function prototypes
 
80
 */
 
81
static void parse_xml(xmlNodePtr xmlnode, gpointer user_data);
 
82
static void builtin_to_xml(xmlNodePtr);
 
83
static void search_to_xml(xmlNodePtr, search_t *);
 
84
static void filter_to_xml(xmlNodePtr, filter_t *);
 
85
static void rule_to_xml(xmlNodePtr, rule_t *);
 
86
static void xml_to_builtin(xmlNodePtr, gpointer);
 
87
static void xml_to_search(xmlNodePtr, gpointer);
 
88
static void xml_to_filter(xmlNodePtr, gpointer);
 
89
static void xml_to_text_rule(xmlNodePtr, gpointer);
 
90
static void xml_to_ip_rule(xmlNodePtr, gpointer);
 
91
static void xml_to_size_rule(xmlNodePtr, gpointer);
 
92
static void xml_to_jump_rule(xmlNodePtr, gpointer);
 
93
static void xml_to_sha1_rule(xmlNodePtr, gpointer);
 
94
static void xml_to_flag_rule(xmlNodePtr, gpointer);
 
95
static void xml_to_state_rule(xmlNodePtr, gpointer);
 
96
static guint16 get_rule_flags_from_xml(xmlNodePtr);
 
97
static xmlAttrPtr xml_prop_printf(xmlNodePtr node, const gchar *name,
 
98
        const char *fmt, ...) G_GNUC_PRINTF(3, 4);
 
99
 
 
100
/*
 
101
 * Private variables
 
102
 */
 
103
static const gchar NODE_BUILTIN[]     = "BuiltIn";
 
104
static const gchar NODE_SEARCH[]      = "Search";
 
105
static const gchar NODE_FILTER[]      = "Filter";
 
106
static const gchar NODE_RULE_TEXT[]   = "TextRule";
 
107
static const gchar NODE_RULE_IP[]     = "IpRule";
 
108
static const gchar NODE_RULE_SIZE[]   = "SizeRule";
 
109
static const gchar NODE_RULE_JUMP[]   = "JumpRule";
 
110
static const gchar NODE_RULE_SHA1[]   = "SHA1Rule";
 
111
static const gchar NODE_RULE_FLAG[]   = "FlagRule";
 
112
static const gchar NODE_RULE_STATE[]  = "StateRule";
 
113
 
 
114
static const gchar TAG_BUILTIN_SHOW_UID[]       = "ShowUID";
 
115
static const gchar TAG_BUILTIN_DROP_UID[]       = "DropUID";
 
116
static const gchar TAG_BUILTIN_DOWNLOAD_UID[]   = "DownloadUID";
 
117
static const gchar TAG_BUILTIN_NODOWNLOAD_UID[] = "NoDownloadUID";
 
118
static const gchar TAG_BUILTIN_RETURN_UID[]     = "ReturnUID";
 
119
static const gchar TAG_FILTER_NAME[]            = "Name";
 
120
static const gchar TAG_FILTER_GLOBAL[]          = "Global";
 
121
static const gchar TAG_FILTER_UID[]             = "UID";
 
122
static const gchar TAG_FILTER_ACTIVE[]          = "Active";
 
123
static const gchar TAG_SEARCH_QUERY[]           = "Query";
 
124
static const gchar TAG_SEARCH_ENABLED[]         = "Enabled";
 
125
static const gchar TAG_SEARCH_SPEED[]           = "Speed";
 
126
static const gchar TAG_SEARCH_PASSIVE[]         = "Passive";
 
127
static const gchar TAG_SEARCH_REISSUE_TIMEOUT[] = "ReissueTimeout";
 
128
static const gchar TAG_SEARCH_SORT_COL[]        = "SortCol";
 
129
static const gchar TAG_SEARCH_SORT_ORDER[]      = "SortOrder";
 
130
static const gchar TAG_RULE_TEXT_CASE[]         = "Case";
 
131
static const gchar TAG_RULE_TEXT_MATCH[]        = "Match";
 
132
static const gchar TAG_RULE_TEXT_TYPE[]         = "Type";
 
133
static const gchar TAG_RULE_IP_ADDR[]           = "Address";
 
134
static const gchar TAG_RULE_IP_MASK[]           = "Netmask";
 
135
static const gchar TAG_RULE_SIZE_LOWER[]        = "Lower";
 
136
static const gchar TAG_RULE_SIZE_UPPER[]        = "Upper";
 
137
static const gchar TAG_RULE_SHA1_HASH[]         = "Hash";
 
138
static const gchar TAG_RULE_SHA1_FILENAME[]     = "OriginalFilename";
 
139
static const gchar TAG_RULE_NEGATE[]            = "Negate";
 
140
static const gchar TAG_RULE_ACTIVE[]            = "Active";
 
141
static const gchar TAG_RULE_SOFT[]              = "Soft";
 
142
static const gchar TAG_RULE_TARGET[]            = "Target";
 
143
static const gchar TAG_RULE_FLAG_BUSY[]         = "Busy";
 
144
static const gchar TAG_RULE_FLAG_PUSH[]         = "Push";
 
145
static const gchar TAG_RULE_FLAG_STABLE[]       = "Stable";
 
146
static const gchar TAG_RULE_STATE_DISPLAY[]     = "Display";
 
147
static const gchar TAG_RULE_STATE_DOWNLOAD[]    = "Download";
 
148
 
 
149
static const gchar search_file_xml[] = "searches.xml";
 
150
static const gchar search_file_xml_new[] = "searches.xml.new";
 
151
static const gchar search_file_xml_old[] = "searches.xml.orig";
 
152
 
 
153
static GHashTable *target_map = NULL;
 
154
static GHashTable *id_map = NULL;
 
155
 
 
156
static node_parser_t parser_map[] = {
 
157
    { NODE_BUILTIN,     xml_to_builtin },
 
158
    { NODE_SEARCH,      xml_to_search },
 
159
    { NODE_FILTER,      xml_to_filter },
 
160
    { NODE_RULE_TEXT,   xml_to_text_rule },
 
161
    { NODE_RULE_IP,     xml_to_ip_rule },
 
162
    { NODE_RULE_SIZE,   xml_to_size_rule },
 
163
    { NODE_RULE_JUMP,   xml_to_jump_rule },
 
164
    { NODE_RULE_SHA1,   xml_to_sha1_rule },
 
165
    { NODE_RULE_FLAG,   xml_to_flag_rule },
 
166
    { NODE_RULE_STATE,  xml_to_state_rule },
 
167
    { NULL, NULL }
 
168
};
 
169
 
 
170
/** Get rid of the obnoxious (xmlChar *) */
 
171
static inline gchar *
 
172
xml_get_string(xmlNode *node, const gchar *id)
 
173
{
 
174
        return (gchar *) xmlGetProp(node, (const xmlChar *) id);
 
175
}
 
176
 
 
177
static inline const xmlChar *
 
178
gchar_to_xmlChar(const gchar *p)
 
179
{
 
180
        return (const xmlChar *) p;
 
181
}
 
182
 
 
183
static inline xmlNodePtr
 
184
xml_new_empty_child(xmlNodePtr parent, const gchar *name)
 
185
{
 
186
        return xmlNewChild(parent, NULL, gchar_to_xmlChar(name), NULL);
 
187
}
 
188
 
 
189
 
 
190
/**
 
191
 * A wrapper around parse_uint64. It's a little stricter, so that trailing
 
192
 * characters enforce an error. It accepts base 10 (decimal) only. On failure
 
193
 * *error will be set to a non-zero "errno" value.
 
194
 *
 
195
 * @param buf the string to parse.
 
196
 * @param error an int variable which will indicate success or failure.
 
197
 * @return On success, the parsed value is returned.
 
198
 */
 
199
static guint64
 
200
parse_number(const gchar *buf, gint *error)
 
201
{
 
202
        const gchar *endptr;
 
203
        guint64 ret;
 
204
 
 
205
        g_assert(buf != NULL);
 
206
        g_assert(error != NULL);
 
207
 
 
208
        ret = parse_uint64(buf, &endptr, 10, error);
 
209
        if (0 == *error && *endptr != '\0') {
 
210
                *error = EINVAL;
 
211
        }
 
212
        if (0 != *error) {
 
213
                g_message("buf=\"%s\"", buf);
 
214
                return 0;
 
215
        }
 
216
        
 
217
        return ret;
 
218
}
 
219
 
 
220
/**
 
221
 * A wrapper around parse_uint64. It's a little stricter, so that trailing
 
222
 * characters enforce an error. It accepts base 16 (decimal) only. On failure
 
223
 * *error will be set to a non-zero "errno" value. The value is casted to
 
224
 * a pointer.
 
225
 *
 
226
 * @param buf the string to parse.
 
227
 * @param error an int variable which will indicate success or failure.
 
228
 * @return On success, the parsed value is returned as a pointer.
 
229
 */
 
230
static gpointer
 
231
parse_target(const gchar *buf, gint *error)
 
232
{
 
233
        const gchar *endptr;
 
234
        guint64 v;
 
235
        gulong target; /* Not guint32! for backwards compatibility. See below. */
 
236
 
 
237
        g_assert(buf != NULL);
 
238
        g_assert(error != NULL);
 
239
 
 
240
        if ('0' == buf[0] && 'x' == buf[1]) {
 
241
                /* 
 
242
                 * In previous versions, targets were printed using "%p". This format
 
243
                 * is implementation-specific and thus causes a non-portable
 
244
                 * configuration. We expect a hexadecimal value that is optionally
 
245
                 * preceded by "0x".
 
246
                 */
 
247
                buf += 2;
 
248
        }
 
249
 
 
250
        v = parse_uint64(buf, &endptr, 16, error);
 
251
        if (0 == *error && *endptr != '\0') {
 
252
                *error = EINVAL;
 
253
        }
 
254
        /*
 
255
         * For backwards compatibility we allow values above 2^32-1 if the
 
256
         * machine doesn't use 32-bit wide pointers. Older versions used
 
257
         * the pointer casted to an integer type as target ID.
 
258
         */
 
259
        if (0 == *error && 4 == sizeof(gpointer) && v > (~(guint32) 0)) {
 
260
                *error = ERANGE;
 
261
        }
 
262
        if (0 != *error) {
 
263
                g_message("buf=\"%s\"", buf);
 
264
                return NULL;
 
265
        }
 
266
 
 
267
        target = v;
 
268
 
 
269
        /* Not using GUINT_TO_POINTER() is intentional to prevent truncation
 
270
         * to 32-bit for backwards compability as explained above. */
 
271
        return (gpointer) target;
 
272
}
 
273
 
 
274
/**
 
275
 * Returns the next available target ID.
 
276
 *
 
277
 * @param do_reset if TRUE, the ID counter is reset to an initial value.
 
278
 * @return a 32-bit integer stuffed into a pointer
 
279
 */
 
280
static gpointer 
 
281
target_new_id(gboolean do_reset)
 
282
{
 
283
        static guint32 id_counter;
 
284
        guint32 ret;
 
285
 
 
286
        /* If target_map is NULL, the counter is reset */
 
287
        if (do_reset) {
 
288
                id_counter = 0;
 
289
        } else {
 
290
                id_counter++;
 
291
                /* 4 billion filters/searches should be enough for everyone */
 
292
                g_assert(0 != id_counter);
 
293
        }
 
294
        
 
295
        ret = id_counter;
 
296
        return GUINT_TO_POINTER(ret);
 
297
}
 
298
 
 
299
/**
 
300
 * Resets the target ID counter and frees target_map if it was created. 
 
301
 */
 
302
static void
 
303
target_map_reset(void)
 
304
{
 
305
        target_new_id(TRUE); /* Reset */
 
306
        if (target_map) {
 
307
                g_hash_table_destroy(target_map);
 
308
                target_map = NULL;
 
309
        }
 
310
}
 
311
 
 
312
/**
 
313
 * Creates a string representation of a ``target''.
 
314
 *
 
315
 * @param target a filter target
 
316
 * @return a static buffer holding the string representation
 
317
 */
 
318
static const gchar *
 
319
target_to_string(filter_t *target)
 
320
{
 
321
        gpointer value;
 
322
        static gchar buf[128];
 
323
 
 
324
        if (!target_map) {
 
325
                target_new_id(TRUE); /* Reset */
 
326
                target_map = g_hash_table_new(NULL, NULL);
 
327
        }
 
328
 
 
329
        if (!g_hash_table_lookup_extended(target_map, target, NULL, &value)) {
 
330
                value = target_new_id(FALSE);
 
331
                g_hash_table_insert(target_map, target, value);
 
332
        }
 
333
        
 
334
    gm_snprintf(buf, sizeof buf, "0x%x", GPOINTER_TO_UINT(value));
 
335
        
 
336
        return buf;
 
337
}
 
338
 
 
339
/**
 
340
 * A wrapper around xmlSetProp() to get rid of (xmlChar *).
 
341
 *
 
342
 * @param node the node
 
343
 * @param name the attribute name
 
344
 * @param value an UTF-8 encoded string
 
345
 * @return the result of xmlSetProp().
 
346
 */
 
347
static inline xmlAttrPtr
 
348
xml_prop_set(xmlNodePtr node, const gchar *name, const char *value)
 
349
{
 
350
    return xmlSetProp(node, gchar_to_xmlChar(name), gchar_to_xmlChar(value));
 
351
}
 
352
        
 
353
/**
 
354
 * A wrapper to set use xmlSetProp() through a printf-like interface. The
 
355
 * length of the created string is limited to 4096 byte and truncation occurs
 
356
 * if this limit is exceeded. For mere strings or longer values use
 
357
 * xml_prop_set() instead.
 
358
 *
 
359
 * @param node the node
 
360
 * @param name the attribute name
 
361
 * @param fmt the format string
 
362
 * @return the result of xmlSetProp().
 
363
 */
 
364
static xmlAttrPtr
 
365
xml_prop_printf(xmlNodePtr node, const gchar *name, const char *fmt, ...)
 
366
{
 
367
        va_list ap;
 
368
        gchar buf[4096];
 
369
 
 
370
        va_start(ap, fmt);
 
371
        gm_vsnprintf(buf, sizeof buf, fmt, ap);
 
372
        va_end(ap);
 
373
    return xml_prop_set(node, name, buf);
 
374
}
 
375
 
 
376
/**
 
377
 * Store pending searches.
 
378
 */
 
379
void
 
380
search_store_xml(void)
 
381
{
 
382
        const GList *l;
 
383
        time_t now = time((time_t *) NULL);
 
384
    xmlDocPtr doc;
 
385
    xmlNodePtr root;
 
386
        gchar *filename_new;
 
387
 
 
388
 
 
389
        /* Free target_map and reset the target ID counter */
 
390
        target_map_reset();     
 
391
 
 
392
    /*
 
393
     * Create new xml document with version 1.0
 
394
     */
 
395
    doc = xmlNewDoc(gchar_to_xmlChar("1.0"));
 
396
 
 
397
    /*
 
398
     * Create a new root node "gtkGnutella searches"
 
399
     */
 
400
    root = xmlNewDocNode(doc, NULL, gchar_to_xmlChar("Searches"), NULL);
 
401
    xmlDocSetRootElement(doc, root);
 
402
        /* Discard the newline of the ctime string */
 
403
    xml_prop_printf(root, "Time", "%24.24s", ctime(&now));
 
404
    xml_prop_printf(root, "Version", "%s", GTA_VERSION_NUMBER);
 
405
 
 
406
    /*
 
407
     * Store UIDs for the builtin targets
 
408
     */
 
409
    builtin_to_xml(root);
 
410
 
 
411
    /*
 
412
     * Iterate over the searches and add them to the tree
 
413
     */
 
414
    for (l = search_gui_get_searches(); l; l = g_list_next(l))
 
415
        search_to_xml(root, (search_t *) l->data);
 
416
 
 
417
    /*
 
418
     * Iterate over the rulesets and add them to the tree.
 
419
     * Only those that are not bound to a search.
 
420
     */
 
421
    for (l = filters; l; l = g_list_next(l))
 
422
        filter_to_xml(root, (filter_t *) l->data);
 
423
 
 
424
    /*
 
425
     * Try to save the file
 
426
     */
 
427
 
 
428
    xmlKeepBlanksDefault(0);
 
429
    filename_new = make_pathname(settings_gui_config_dir(),
 
430
                                                search_file_xml_new);
 
431
 
 
432
    if (
 
433
                NULL == filename_new ||
 
434
                -1 == xmlSaveFormatFile(filename_new, doc, TRUE)
 
435
        ) {
 
436
        g_warning("Unable to create %s to persist search: %s",
 
437
                        filename_new, g_strerror(errno));
 
438
    } else {
 
439
                gchar *filename;
 
440
 
 
441
        if (gui_debug >= 3)
 
442
            g_message("saved searches file: %s", filename_new);
 
443
 
 
444
                filename = make_pathname(settings_gui_config_dir(), search_file_xml);
 
445
 
 
446
                if (
 
447
                        NULL == filename ||
 
448
                        NULL == filename_new ||
 
449
                        -1 == rename(filename_new, filename)
 
450
                )
 
451
                        g_warning("could not rename %s as %s: %s",
 
452
                                filename_new, filename, g_strerror(errno));
 
453
 
 
454
                G_FREE_NULL(filename);
 
455
    }
 
456
 
 
457
        G_FREE_NULL(filename_new);
 
458
 
 
459
        xmlFreeDoc(doc);
 
460
 
 
461
        /* Free target_map and reset the target ID counter */
 
462
        target_map_reset();     
 
463
}
 
464
 
 
465
/**
 
466
 * Retrieve search list and restart searches.
 
467
 * This is the new xml version. The searches are normally
 
468
 * retrieved from  ~/.gtk-gnutella/searches.xml.
 
469
 */
 
470
gboolean
 
471
search_retrieve_xml(void)
 
472
{
 
473
        xmlDocPtr doc;
 
474
    xmlNodePtr node;
 
475
    xmlNodePtr root;
 
476
        GList  *f, *f_next;
 
477
        gchar *path = NULL;
 
478
        gchar *path_orig = NULL;
 
479
 
 
480
        /*
 
481
         * We can't use routines from file.c here because libxml2 only defines
 
482
         * interfaces for parsing a path or memory, but not for parsing a FILE
 
483
         * stream!  Unbelievable.
 
484
         *              --RAM, 16/07/2003
 
485
         */
 
486
 
 
487
        path = make_pathname(settings_gui_config_dir(), search_file_xml);
 
488
        if (NULL == path)
 
489
                goto out;
 
490
 
 
491
        path_orig = make_pathname(settings_gui_config_dir(), search_file_xml_old);
 
492
        if (NULL == path_orig)
 
493
                goto out;
 
494
 
 
495
        /*
 
496
     * If the file doesn't exist, try retrieving from the .orig version.
 
497
     */
 
498
 
 
499
        if (file_exists(path)) {
 
500
                if (-1 == rename(path, path_orig)) {
 
501
                        g_warning("could not rename \"%s\" as \"%s\": %s",
 
502
                                path, path_orig, g_strerror(errno));
 
503
                        G_FREE_NULL(path_orig);
 
504
                        path_orig = path;
 
505
                        path = NULL;
 
506
                } else {
 
507
                        G_FREE_NULL(path);
 
508
                }
 
509
        } else {
 
510
        g_warning("searches file does not exist: %s", path);
 
511
                G_FREE_NULL(path);
 
512
 
 
513
                if (!file_exists(path_orig))
 
514
                        goto out;
 
515
 
 
516
                g_warning("retrieving searches from %s instead", path_orig);
 
517
    }
 
518
 
 
519
        /*
 
520
     * parse the file and put the result into newdoc
 
521
     */
 
522
        doc = xmlParseFile(path_orig);
 
523
    root = xmlDocGetRootElement(doc);
 
524
 
 
525
        /*
 
526
     * in case something went wrong
 
527
     */
 
528
    if (!doc) {
 
529
        g_warning("error parsing searches file: %s", path_orig);
 
530
                goto out;
 
531
    }
 
532
 
 
533
        if (/* if there is no root element */
 
534
        (root == NULL) ||
 
535
            /* if it doesn't have a name */
 
536
            (root->name == NULL) ||
 
537
            /* if it isn't a Genealogy node */
 
538
            g_ascii_strcasecmp((const gchar *) root->name, "Searches") != 0
 
539
    ) {
 
540
        g_warning("searches file has invalid format: %s", path);
 
541
                xmlFreeDoc(doc);
 
542
                goto out;
 
543
        }
 
544
        G_FREE_NULL(path_orig);
 
545
 
 
546
    id_map = g_hash_table_new(NULL, NULL);
 
547
 
 
548
    /*
 
549
     * find nodes and add them to the list, this just
 
550
         * loops through all the children of the root of the document
 
551
     */
 
552
        for (node = root->children; node != NULL; node = node->next)
 
553
        parse_xml(node, NULL);
 
554
 
 
555
    /*
 
556
     * We should have collected all ruleset UIDs now. So we can
 
557
     * now resolve the UIDs to the actual pointers we use now.
 
558
     * We need to commit before we do this, because we want to
 
559
     * interate over the rulesets and don't want to cope with
 
560
     * shadows.
 
561
     */
 
562
 
 
563
    if (gui_debug >= 6)
 
564
        g_message("resolving UIDs");
 
565
 
 
566
    for (f = filters; f != NULL; f = f_next) {
 
567
                gboolean damaged = FALSE;
 
568
        filter_t *filter = (filter_t *) f->data;
 
569
        GList *r;
 
570
        gint n = 0;
 
571
 
 
572
                f_next = g_list_next(f);
 
573
 
 
574
        if (gui_debug >= 6) {
 
575
            g_message("\n\nresolving on filter:");
 
576
            dump_filter(filter);
 
577
        }
 
578
 
 
579
        if (!filter_is_builtin(filter)) {
 
580
 
 
581
            for (r = filter->ruleset; r != NULL; r = g_list_next(r)) {
 
582
                rule_t *rule = (rule_t *) r->data;
 
583
                gpointer new_target;
 
584
 
 
585
                g_assert(rule->target != NULL);
 
586
                new_target = g_hash_table_lookup(id_map, rule->target);
 
587
                if (new_target == NULL) {
 
588
                    g_warning("Failed to resolve rule %d in \"%s\": "
 
589
                                                "missing key %p",
 
590
                        n, filter->name,
 
591
                                                cast_to_gconstpointer(filter_rule_to_gchar(rule)));
 
592
 
 
593
                                        /* Remove the corrupted filter, we can't handle it */
 
594
                                        damaged = TRUE;
 
595
                                        break;
 
596
                                }
 
597
                rule->target = new_target;
 
598
                set_flags(rule->flags, RULE_FLAG_VALID);
 
599
 
 
600
                /*
 
601
                 * We circumvent the shadows, so we must do refcounting
 
602
                 * manually here.
 
603
                 */
 
604
                if (gui_debug >= 7) {
 
605
                    g_message("increasing refcount on \"%s\" to %d",
 
606
                        rule->target->name, rule->target->refcount + 1);
 
607
                                }
 
608
                rule->target->refcount++;
 
609
                n++;
 
610
            }
 
611
        }
 
612
 
 
613
        if (gui_debug >= 6) {
 
614
                        g_message("resolved filter:");
 
615
            dump_filter(filter);
 
616
        }
 
617
 
 
618
                if (damaged) {
 
619
                        g_warning("Removing damaged ruleset from filter (name=\"%s\")",
 
620
                                filter->name);
 
621
                        /* This causes a little memory leak but the priority is
 
622
                         * not to crash. */
 
623
                        filter->ruleset = NULL;
 
624
                }
 
625
    }
 
626
 
 
627
        /*
 
628
     * Verify bindings.
 
629
     */
 
630
    {
 
631
        gboolean borked = FALSE;
 
632
        const GList *s;
 
633
 
 
634
        if (gui_debug >= 6)
 
635
            g_message("verifying bindings...");
 
636
 
 
637
        for (s = search_gui_get_searches(); s != NULL; s = g_list_next(s)) {
 
638
            search_t *search = (search_t *) s->data;
 
639
 
 
640
            if (search->filter->search == search) {
 
641
                if (gui_debug >= 6)
 
642
                    g_message("binding ok for: %s", search->query);
 
643
            } else {
 
644
                g_warning("binding broken for: %s", search->query);
 
645
                borked = TRUE;
 
646
            }
 
647
        }
 
648
 
 
649
        g_assert(!borked);
 
650
    }
 
651
 
 
652
    g_list_free(filters_current);
 
653
    filters_current = g_list_copy(filters);
 
654
 
 
655
    g_hash_table_destroy(id_map);
 
656
 
 
657
        xmlFreeDoc(doc);
 
658
    xmlCleanupParser();
 
659
 
 
660
        return TRUE;
 
661
 
 
662
out:
 
663
        G_FREE_NULL(path);
 
664
        G_FREE_NULL(path_orig);
 
665
 
 
666
        return FALSE;
 
667
}
 
668
 
 
669
static void
 
670
builtin_to_xml(xmlNodePtr parent)
 
671
{
 
672
        const struct {
 
673
                const gchar *tag;
 
674
                filter_t * (* target)(void);
 
675
        } builtins[] = {
 
676
                { TAG_BUILTIN_SHOW_UID, filter_get_show_target },
 
677
                { TAG_BUILTIN_DROP_UID, filter_get_drop_target },
 
678
                { TAG_BUILTIN_DOWNLOAD_UID, filter_get_download_target },
 
679
                { TAG_BUILTIN_NODOWNLOAD_UID, filter_get_nodownload_target },
 
680
                { TAG_BUILTIN_RETURN_UID, filter_get_return_target },
 
681
        };
 
682
    xmlNodePtr newxml;
 
683
        guint i;
 
684
 
 
685
    g_assert(parent != NULL);
 
686
 
 
687
    newxml = xml_new_empty_child(parent, NODE_BUILTIN);
 
688
        for (i = 0; i < G_N_ELEMENTS(builtins); i++) {
 
689
        xml_prop_set(newxml, builtins[i].tag,
 
690
                        target_to_string(builtins[i].target()));
 
691
        }
 
692
}
 
693
 
 
694
static void
 
695
search_to_xml(xmlNodePtr parent, search_t *s)
 
696
{
 
697
    xmlNodePtr newxml;
 
698
    GList *l;
 
699
        gchar *query;
 
700
 
 
701
    g_assert(s != NULL);
 
702
    g_assert(s->query != NULL);
 
703
    g_assert(parent != NULL);
 
704
 
 
705
    if (gui_debug >= 6) {
 
706
        g_message(
 
707
                        "saving search: %s (%p enabled=%d)\n"
 
708
                        "  -- filter is bound to: %p\n"
 
709
                        "  -- search is         : %p",
 
710
                        s->query, cast_to_gconstpointer(s), s->enabled,
 
711
                        cast_to_gconstpointer(s->filter->search),
 
712
                        cast_to_gconstpointer(s));
 
713
    }
 
714
 
 
715
        if (NULL == (query = locale_to_utf8_full(s->query))) {
 
716
                g_warning("search_to_xml: Cannot convert search string to UTF-8. "
 
717
                        "Search won't be saved. (query=\"%s\")", s->query);
 
718
                return;
 
719
        }
 
720
 
 
721
    newxml = xml_new_empty_child(parent, NODE_SEARCH);
 
722
    xml_prop_set(newxml, TAG_SEARCH_QUERY, query);
 
723
        if (query != s->query)
 
724
                G_FREE_NULL(query);
 
725
 
 
726
        xml_prop_printf(newxml, TAG_SEARCH_ENABLED, "%u", s->enabled);
 
727
    xml_prop_printf(newxml, TAG_SEARCH_PASSIVE, "%u", TO_BOOL(s->passive));
 
728
    xml_prop_printf(newxml, TAG_SEARCH_REISSUE_TIMEOUT,
 
729
                "%u", guc_search_get_reissue_timeout(s->search_handle));
 
730
    xml_prop_printf(newxml, TAG_SEARCH_SORT_COL, "%i", s->sort_col);
 
731
    xml_prop_printf(newxml, TAG_SEARCH_SORT_ORDER, "%i", s->sort_order);
 
732
 
 
733
    for (l = s->filter->ruleset; l != NULL; l = g_list_next(l))
 
734
        rule_to_xml(newxml, (rule_t *) l->data);
 
735
}
 
736
 
 
737
 
 
738
static void
 
739
filter_to_xml(xmlNodePtr parent, filter_t *f)
 
740
{
 
741
    xmlNodePtr newxml;
 
742
    GList *l;
 
743
        gchar *name;
 
744
 
 
745
    g_assert(f != NULL);
 
746
    g_assert(f->name != NULL);
 
747
    g_assert(parent != NULL);
 
748
 
 
749
    /*
 
750
     * Don't store the builtin targets or bound rulesets
 
751
     */
 
752
    if (filter_is_builtin(f) || filter_is_bound(f)) {
 
753
        if (gui_debug >= 7)
 
754
            g_message("not saving bound/builtin: %s", f->name);
 
755
        return;
 
756
    }
 
757
 
 
758
    if (gui_debug >= 6) {
 
759
                g_message(
 
760
                        "saving filter: %s\n"
 
761
                        "  -- bound   : %p",
 
762
                        f->name,
 
763
                        cast_to_gconstpointer(f->search));
 
764
    }
 
765
 
 
766
        if (NULL == (name = locale_to_utf8_full(f->name))) {
 
767
                g_warning("filter_to_xml: Cannot convert search string to UTF-8. "
 
768
                        "Filter won't be saved. (name=\"%s\")", f->name);
 
769
                return;
 
770
        }
 
771
 
 
772
    newxml = xml_new_empty_child(parent, NODE_FILTER);
 
773
    xml_prop_set(newxml, TAG_FILTER_NAME, name);
 
774
        if (name != f->name)
 
775
                G_FREE_NULL(name);
 
776
 
 
777
        
 
778
    xml_prop_printf(newxml, TAG_FILTER_ACTIVE,
 
779
                "%u", TO_BOOL(filter_is_active(f)));
 
780
 
 
781
    /*
 
782
     * We take the pointer as a unique id which
 
783
     * we use during read-in for setting the
 
784
     * destination of JUMP actions.
 
785
     */
 
786
    xml_prop_set(newxml, TAG_FILTER_UID, target_to_string(f));
 
787
 
 
788
    if (filter_get_global_pre() == f) {
 
789
        xml_prop_printf(newxml, TAG_FILTER_GLOBAL, "%u", GLOBAL_PRE);
 
790
    }
 
791
 
 
792
    if (filter_get_global_post() == f) {
 
793
        xml_prop_printf(newxml, TAG_FILTER_GLOBAL, "%u", GLOBAL_POST);
 
794
    }
 
795
 
 
796
    /*
 
797
     * Since free rulesets don't have bound searches,
 
798
     * we need not save the ->search member.
 
799
     * Visited is only used internally during filter
 
800
     * application.
 
801
     */
 
802
    for (l = f->ruleset; l != NULL; l = g_list_next(l))
 
803
        rule_to_xml(newxml, (rule_t *) l->data);
 
804
}
 
805
 
 
806
static void
 
807
rule_to_xml(xmlNodePtr parent, rule_t *r)
 
808
{
 
809
    xmlNodePtr newxml = NULL;
 
810
 
 
811
    g_assert(parent != NULL);
 
812
 
 
813
    /*
 
814
     * We create no node when there is no filter rule.
 
815
     */
 
816
    if (r == NULL)
 
817
        return;
 
818
 
 
819
    switch (r->type) {
 
820
    case RULE_TEXT:
 
821
                {
 
822
                        gchar *match;
 
823
                        
 
824
                if (NULL == (match = locale_to_utf8_full(r->u.text.match))) {
 
825
                                g_warning("rule_to_xml: Cannot convert string to UTF-8. "
 
826
                                        "Omitting rule (\"%s\")", r->u.text.match);
 
827
                                return;
 
828
                        }
 
829
 
 
830
                        newxml = xml_new_empty_child(parent, NODE_RULE_TEXT);
 
831
                xml_prop_set(newxml, TAG_RULE_TEXT_CASE,
 
832
                                r->u.text.case_sensitive ? "1" : "0");
 
833
                xml_prop_set(newxml, TAG_RULE_TEXT_MATCH, match);
 
834
                        if (match != r->u.text.match)
 
835
                                G_FREE_NULL(match);
 
836
 
 
837
                xml_prop_printf(newxml, TAG_RULE_TEXT_TYPE, "%u", r->u.text.type);
 
838
                }
 
839
        break;
 
840
    case RULE_IP:
 
841
        newxml = xml_new_empty_child(parent, NODE_RULE_IP);
 
842
        xml_prop_set(newxml, TAG_RULE_IP_ADDR, ip_to_gchar(r->u.ip.addr));
 
843
        xml_prop_set(newxml, TAG_RULE_IP_MASK, ip_to_gchar(r->u.ip.mask));
 
844
        break;
 
845
    case RULE_SIZE:
 
846
                {
 
847
                        gchar buf[32];
 
848
                        
 
849
                newxml = xml_new_empty_child(parent, NODE_RULE_SIZE);
 
850
                xml_prop_printf(newxml, TAG_RULE_SIZE_LOWER,
 
851
                                "%s", uint64_to_string_buf(buf, sizeof buf, r->u.size.lower));
 
852
                xml_prop_printf(newxml, TAG_RULE_SIZE_UPPER,
 
853
                                "%s", uint64_to_string_buf(buf, sizeof buf, r->u.size.upper));
 
854
                }
 
855
        break;
 
856
    case RULE_JUMP:
 
857
        newxml = xml_new_empty_child(parent, NODE_RULE_JUMP);
 
858
 
 
859
        /*
 
860
         * Only need target to this rule and that's done below.
 
861
         */
 
862
        break;
 
863
    case RULE_SHA1:
 
864
                {
 
865
                        gchar *name;
 
866
                        
 
867
                newxml = xml_new_empty_child(parent, NODE_RULE_SHA1);
 
868
 
 
869
                if (r->u.sha1.hash != NULL)
 
870
                xml_prop_set(newxml, TAG_RULE_SHA1_HASH,
 
871
                                        sha1_base32(r->u.sha1.hash));
 
872
 
 
873
                if (NULL == (name = locale_to_utf8_full(r->u.sha1.filename))) {
 
874
                                g_warning("rule_to_xml: Cannot convert filename to UTF-8. "
 
875
                                        "Omitting filename (\"%s\")", r->u.sha1.filename);
 
876
                        } else {
 
877
                                xml_prop_set(newxml, TAG_RULE_SHA1_FILENAME, name);
 
878
                                if (name != r->u.sha1.filename)
 
879
                                        G_FREE_NULL(name);
 
880
                        }
 
881
                }
 
882
        /*
 
883
         * r->u.sha1.hash is NULL, we just omit the hash.
 
884
         */
 
885
        break;
 
886
    case RULE_FLAG:
 
887
        newxml = xml_new_empty_child(parent, NODE_RULE_FLAG);
 
888
 
 
889
        xml_prop_printf(newxml, TAG_RULE_FLAG_STABLE, "%u", r->u.flag.stable);
 
890
        xml_prop_printf(newxml, TAG_RULE_FLAG_BUSY, "%u", r->u.flag.busy);
 
891
        xml_prop_printf(newxml, TAG_RULE_FLAG_PUSH, "%u", r->u.flag.push);
 
892
        break;
 
893
    case RULE_STATE:
 
894
        newxml = xml_new_empty_child(parent, NODE_RULE_STATE);
 
895
        xml_prop_printf(newxml, TAG_RULE_STATE_DISPLAY,
 
896
                        "%u", r->u.state.display);
 
897
        xml_prop_printf(newxml, TAG_RULE_STATE_DOWNLOAD,
 
898
                        "%u", r->u.state.download);
 
899
        break;
 
900
    default:
 
901
        g_error("Unknown rule type: 0x%x", r->type);
 
902
    }
 
903
 
 
904
    xml_prop_printf(newxml, TAG_RULE_NEGATE, "%u", TO_BOOL(RULE_IS_NEGATED(r)));
 
905
    xml_prop_printf(newxml, TAG_RULE_ACTIVE, "%u", TO_BOOL(RULE_IS_ACTIVE(r)));
 
906
    xml_prop_printf(newxml, TAG_RULE_SOFT, "%u", TO_BOOL(RULE_IS_SOFT(r)));
 
907
    xml_prop_set(newxml, TAG_RULE_TARGET, target_to_string(r->target));
 
908
}
 
909
 
 
910
static void
 
911
parse_xml(xmlNodePtr xmlnode, gpointer user_data)
 
912
{
 
913
    gint n;
 
914
 
 
915
    g_assert(xmlnode != NULL);
 
916
 
 
917
    if (xmlIsBlankNode(xmlnode))
 
918
        return;
 
919
 
 
920
    if (!xmlnode->name) {
 
921
        g_warning("Unnamed node: ignored");
 
922
            return;
 
923
    }
 
924
 
 
925
    for (n = 0; parser_map[n].name != NULL; n ++) {
 
926
        if (
 
927
                        0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
928
                                        (const gchar *) parser_map[n].name)
 
929
                ) {
 
930
            parser_map[n].parser_func(xmlnode, user_data);
 
931
            return;
 
932
        }
 
933
    }
 
934
 
 
935
    g_warning("Unknown node: \"%s\"", xmlnode->name);
 
936
}
 
937
 
 
938
static void
 
939
xml_to_builtin(xmlNodePtr xmlnode, gpointer unused_udata)
 
940
{
 
941
    gchar *buf;
 
942
    gpointer target;
 
943
        gint error;
 
944
 
 
945
        (void) unused_udata;
 
946
    g_assert(xmlnode != NULL);
 
947
    g_assert(xmlnode->name != NULL);
 
948
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
949
                                        NODE_BUILTIN));
 
950
    g_assert(filter_get_show_target() != NULL);
 
951
    g_assert(filter_get_drop_target() != NULL);
 
952
    g_assert(filter_get_download_target() != NULL);
 
953
 
 
954
    buf = STRTRACK(xml_get_string(xmlnode, TAG_BUILTIN_SHOW_UID));
 
955
    g_assert(buf != NULL);
 
956
    target = parse_target(buf, &error);
 
957
    G_FREE_NULL(buf);
 
958
    if (error) {
 
959
        g_warning("xml_to_builtin: %s", g_strerror(error));
 
960
                return;
 
961
        }
 
962
    g_hash_table_insert(id_map, target, filter_get_show_target());
 
963
 
 
964
    buf = STRTRACK(xml_get_string(xmlnode, TAG_BUILTIN_DROP_UID));
 
965
    g_assert(buf != NULL);
 
966
    target = parse_target(buf, &error);
 
967
    if (error) {
 
968
        g_warning("xml_to_builtin: %s", g_strerror(error));
 
969
                return;
 
970
        }
 
971
    g_hash_table_insert(id_map, target, filter_get_drop_target());
 
972
 
 
973
    buf = STRTRACK(xml_get_string(xmlnode, TAG_BUILTIN_DOWNLOAD_UID));
 
974
    if (buf != NULL) {
 
975
        target = parse_target(buf, &error);
 
976
        G_FREE_NULL(buf);
 
977
        if (error) {
 
978
            g_warning("xml_to_builtin: %s", g_strerror(error));
 
979
                        return;
 
980
                }
 
981
        g_hash_table_insert(id_map, target, filter_get_download_target());
 
982
    } else {
 
983
        g_warning("xml_to_builtin: no \"DOWNLOAD\" target");
 
984
    }
 
985
 
 
986
    buf = STRTRACK(xml_get_string(xmlnode, TAG_BUILTIN_NODOWNLOAD_UID));
 
987
    if (buf != NULL) {
 
988
        target = parse_target(buf, &error);
 
989
        G_FREE_NULL(buf);
 
990
        if (error) {
 
991
            g_warning("xml_to_builtin: %s", g_strerror(error));
 
992
                        return;
 
993
                }
 
994
        g_hash_table_insert(id_map, target, filter_get_nodownload_target());
 
995
    } else {
 
996
        g_warning("xml_to_builtin: no \"DON'T DOWNLOAD\" target");
 
997
    }
 
998
 
 
999
    buf = STRTRACK(xml_get_string(xmlnode, TAG_BUILTIN_RETURN_UID));
 
1000
    if (buf != NULL) {
 
1001
        target = parse_target(buf, &error);
 
1002
        G_FREE_NULL(buf);
 
1003
        if (error) {
 
1004
            g_warning("xml_to_builtin: %s", g_strerror(error));
 
1005
                        return;
 
1006
                }
 
1007
        g_hash_table_insert(id_map, target, filter_get_return_target());
 
1008
    } else {
 
1009
        g_warning("xml_to_builtin: no \"RETURN\" target");
 
1010
    }
 
1011
}
 
1012
 
 
1013
static void
 
1014
xml_to_search(xmlNodePtr xmlnode, gpointer unused_udata)
 
1015
{
 
1016
    gchar *buf;
 
1017
    gchar *query;
 
1018
    gint sort_col = SORT_NO_COL, sort_order = SORT_NONE;
 
1019
    guint32 reissue_timeout;
 
1020
    xmlNodePtr node;
 
1021
    search_t * search;
 
1022
    guint flags = 0;
 
1023
        gboolean override;
 
1024
 
 
1025
        (void) unused_udata;
 
1026
    g_assert(xmlnode != NULL);
 
1027
    g_assert(xmlnode->name != NULL);
 
1028
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1029
                                        NODE_SEARCH));
 
1030
 
 
1031
    gnet_prop_get_guint32_val(PROP_SEARCH_REISSUE_TIMEOUT, &reissue_timeout);
 
1032
        gnet_prop_get_boolean_val(PROP_ALLOW_AUTO_REQUERIES, &override);
 
1033
 
 
1034
        buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_QUERY));
 
1035
    if (!buf) {
 
1036
        g_warning("Ignored search without query");
 
1037
        return;
 
1038
    }
 
1039
        query = buf;
 
1040
 
 
1041
    buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_ENABLED));
 
1042
    if (buf) {
 
1043
        if (atoi(buf) == 1) {
 
1044
                        flags |= SEARCH_ENABLED;
 
1045
                }
 
1046
        G_FREE_NULL(buf);
 
1047
    } else
 
1048
                flags |= SEARCH_ENABLED;         /* Compatibility: searches always began */
 
1049
 
 
1050
    buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_SPEED));
 
1051
    if (buf) {
 
1052
                g_warning("xml_to_search: Found deprecated speed attribute.");
 
1053
        G_FREE_NULL(buf);
 
1054
    }
 
1055
 
 
1056
    buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_REISSUE_TIMEOUT));
 
1057
    if (buf) {
 
1058
        reissue_timeout = atol(buf);
 
1059
        G_FREE_NULL(buf);
 
1060
    }
 
1061
 
 
1062
    buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_PASSIVE));
 
1063
    if (buf) {
 
1064
        if (atol(buf) == 1)
 
1065
                        flags |= SEARCH_PASSIVE;
 
1066
        G_FREE_NULL(buf);
 
1067
    }
 
1068
 
 
1069
    buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_SORT_COL));
 
1070
    if (buf) {
 
1071
        sort_col = atol(buf);
 
1072
        G_FREE_NULL(buf);
 
1073
    }
 
1074
 
 
1075
        buf = STRTRACK(xml_get_string(xmlnode, TAG_SEARCH_SORT_ORDER));
 
1076
    if (buf) {
 
1077
        sort_order = atol(buf);
 
1078
        G_FREE_NULL(buf);
 
1079
    }
 
1080
 
 
1081
        /**
 
1082
         * LimeWire considers *any* form of requerying unacceptable.
 
1083
         * Deactivate it for now.
 
1084
         *              -- cbiere, 2005-03-22
 
1085
         */
 
1086
 
 
1087
        if (!(flags & SEARCH_PASSIVE) && !override)
 
1088
                flags &= ~SEARCH_ENABLED;
 
1089
        
 
1090
    if (gui_debug >= 4) {
 
1091
        g_message("adding new %s %s search: %s",
 
1092
                        (flags & SEARCH_ENABLED) ? "enabled" : "disabled",
 
1093
                        (flags & SEARCH_PASSIVE) ? "passive" : "active",
 
1094
                        query);
 
1095
        }
 
1096
 
 
1097
        search_gui_new_search_full(query, reissue_timeout,
 
1098
                sort_col, sort_order, flags, &search);
 
1099
 
 
1100
    G_FREE_NULL(query);
 
1101
 
 
1102
    /*
 
1103
     * Also parse all children.
 
1104
     */
 
1105
        for (node = xmlnode->children; node != NULL; node = node->next)
 
1106
        parse_xml(node, search->filter);
 
1107
}
 
1108
 
 
1109
static void
 
1110
xml_to_filter(xmlNodePtr xmlnode, gpointer unused_udata)
 
1111
{
 
1112
    gchar *buf;
 
1113
    gchar *name;
 
1114
    xmlNodePtr node;
 
1115
    filter_t *filter;
 
1116
    gpointer dest;
 
1117
    gboolean active = TRUE;
 
1118
        gint error;
 
1119
        guint64 v;
 
1120
 
 
1121
        (void) unused_udata;
 
1122
    g_assert(xmlnode != NULL);
 
1123
    g_assert(xmlnode->name != NULL);
 
1124
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1125
                                        NODE_FILTER));
 
1126
 
 
1127
        buf = STRTRACK(xml_get_string(xmlnode, TAG_FILTER_NAME));
 
1128
    if (!buf) {
 
1129
        g_warning("Ignored unnamed filter");
 
1130
        return;
 
1131
    }
 
1132
    name = buf;
 
1133
 
 
1134
    buf = STRTRACK(xml_get_string(xmlnode, TAG_FILTER_GLOBAL));
 
1135
    if (buf) {
 
1136
        v = parse_number(buf, &error);
 
1137
        G_FREE_NULL(buf);
 
1138
        if (error) {
 
1139
            g_warning("xml_to_filter: %s", g_strerror(error));
 
1140
                        return;
 
1141
                }
 
1142
 
 
1143
        switch (v) {
 
1144
        case GLOBAL_PRE:
 
1145
            filter = filter_get_global_pre();
 
1146
            break;
 
1147
        case GLOBAL_POST:
 
1148
            filter = filter_get_global_post();
 
1149
            break;
 
1150
        default:
 
1151
            filter = NULL;
 
1152
            g_warning("xml_to_filter: Invalid filter");
 
1153
                        return;
 
1154
        }
 
1155
    } else {
 
1156
        if (gui_debug >= 4)
 
1157
            g_message("adding new filter: %s", name);
 
1158
        filter = filter_new(name);
 
1159
        filters = g_list_append(filters, filter);
 
1160
    }
 
1161
 
 
1162
    buf = STRTRACK(xml_get_string(xmlnode, TAG_FILTER_ACTIVE));
 
1163
    if (buf != NULL) {
 
1164
        v = parse_number(buf, &error);
 
1165
        G_FREE_NULL(buf);
 
1166
                if (error || v > 1) {
 
1167
                g_warning("xml_to_filter: Invalid \"active\" tag");
 
1168
                        return;
 
1169
                }
 
1170
        active = 0 != v;
 
1171
    }
 
1172
    if (active)
 
1173
        set_flags(filter->flags, FILTER_FLAG_ACTIVE);
 
1174
    else
 
1175
        clear_flags(filter->flags, FILTER_FLAG_ACTIVE);
 
1176
 
 
1177
    buf = STRTRACK(xml_get_string(xmlnode, TAG_FILTER_UID));
 
1178
    g_assert(buf);
 
1179
    dest = parse_target(buf, &error);
 
1180
    G_FREE_NULL(buf);
 
1181
    if (error) {
 
1182
        g_warning("xml_to_filter: %s", g_strerror(error));
 
1183
                return;
 
1184
        }
 
1185
    g_hash_table_insert(id_map, dest, filter);
 
1186
 
 
1187
    G_FREE_NULL(name);
 
1188
 
 
1189
    /*
 
1190
     * Also parse all children.
 
1191
     */
 
1192
        for (node = xmlnode->children; node != NULL; node = node->next)
 
1193
        parse_xml(node, filter);
 
1194
}
 
1195
 
 
1196
static void
 
1197
xml_to_text_rule(xmlNodePtr xmlnode, gpointer data)
 
1198
{
 
1199
    gchar *match;
 
1200
    enum rule_text_type type;
 
1201
    gboolean case_sensitive;
 
1202
    gchar *buf;
 
1203
    rule_t *rule;
 
1204
    filter_t *target, *filter = data;
 
1205
    guint16 flags;
 
1206
        guint64 v;
 
1207
        gint error;
 
1208
 
 
1209
    g_assert(xmlnode != NULL);
 
1210
    g_assert(xmlnode->name != NULL);
 
1211
    g_assert(filter != NULL);
 
1212
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1213
                                        NODE_RULE_TEXT));
 
1214
 
 
1215
    match = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TEXT_MATCH));
 
1216
    if (match == NULL) {
 
1217
        g_warning("xml_to_text_rule: rule without match string");
 
1218
                return;
 
1219
        }
 
1220
 
 
1221
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TEXT_CASE));
 
1222
    v = parse_number(buf, &error);
 
1223
    G_FREE_NULL(buf);
 
1224
        if (error || v > 1) {
 
1225
        g_warning("xml_to_text_rule: invalid \"text case\" tag");
 
1226
                return;
 
1227
        }
 
1228
 
 
1229
    case_sensitive = 0 != v;
 
1230
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TEXT_TYPE));
 
1231
    type = (enum rule_text_type) atol(buf);
 
1232
    G_FREE_NULL(buf);
 
1233
 
 
1234
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1235
    target = parse_target(buf, &error);
 
1236
    G_FREE_NULL(buf);
 
1237
    if (error) {
 
1238
        g_warning("xml_to_text_rule: %s", g_strerror(error));
 
1239
                return;
 
1240
        }
 
1241
 
 
1242
    flags = get_rule_flags_from_xml(xmlnode);
 
1243
    rule = filter_new_text_rule(match, type, case_sensitive, target, flags);
 
1244
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1245
 
 
1246
    if (gui_debug >= 4) {
 
1247
        g_message("added to filter \"%s\" rule with target %p",
 
1248
            filter->name, cast_to_gconstpointer(rule->target));
 
1249
        }
 
1250
 
 
1251
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1252
 
 
1253
    G_FREE_NULL(match);
 
1254
}
 
1255
 
 
1256
static void
 
1257
xml_to_ip_rule(xmlNodePtr xmlnode, gpointer data)
 
1258
{
 
1259
    guint32 addr;
 
1260
    guint32 mask;
 
1261
    gchar *buf;
 
1262
    rule_t *rule;
 
1263
    filter_t *target, *filter = data;
 
1264
    guint16 flags;
 
1265
        gint error;
 
1266
 
 
1267
    g_assert(xmlnode != NULL);
 
1268
    g_assert(xmlnode->name != NULL);
 
1269
    g_assert(filter != NULL);
 
1270
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1271
                                        NODE_RULE_IP));
 
1272
 
 
1273
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_IP_ADDR));
 
1274
    if (buf == NULL) {
 
1275
        g_warning("xml_to_ip_rule: rule without ip address");
 
1276
                return;
 
1277
        }
 
1278
        addr = gchar_to_ip(buf);        /* XXX: Needs validity check! */
 
1279
    G_FREE_NULL(buf);
 
1280
 
 
1281
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_IP_MASK));
 
1282
    if (buf == NULL) {
 
1283
        g_warning("xml_to_ip_rule: rule without netmask");
 
1284
                return;
 
1285
        }
 
1286
        mask = gchar_to_ip(buf);        /* XXX: Needs validity check! */
 
1287
    G_FREE_NULL(buf);
 
1288
 
 
1289
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1290
    target = parse_target(buf, &error);
 
1291
    if (error) {
 
1292
        g_warning( "xml_to_ip_rule: %s", g_strerror(error));
 
1293
                return;
 
1294
        }
 
1295
    G_FREE_NULL(buf);
 
1296
 
 
1297
    flags = get_rule_flags_from_xml(xmlnode);
 
1298
    rule = filter_new_ip_rule(addr, mask, target, flags);
 
1299
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1300
 
 
1301
    if (gui_debug >= 4) {
 
1302
        g_message("added to filter \"%s\" rule with target %p",
 
1303
            filter->name, cast_to_gconstpointer(rule->target));
 
1304
        }
 
1305
 
 
1306
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1307
}
 
1308
 
 
1309
static void
 
1310
xml_to_size_rule(xmlNodePtr xmlnode, gpointer data)
 
1311
{
 
1312
    filter_t *target = NULL, *filter = data;
 
1313
    filesize_t lower, upper;
 
1314
    gchar *buf;
 
1315
    rule_t *rule;
 
1316
    guint16 flags;
 
1317
        gint error;
 
1318
 
 
1319
    g_assert(xmlnode != NULL);
 
1320
    g_assert(xmlnode->name != NULL);
 
1321
    g_assert(filter != NULL);
 
1322
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1323
                                        NODE_RULE_SIZE));
 
1324
 
 
1325
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_SIZE_LOWER));
 
1326
    if (buf == NULL) {
 
1327
        g_warning("xml_to_size_rule: rule without lower bound");
 
1328
                return;
 
1329
        }
 
1330
    lower = parse_number(buf, &error);
 
1331
        if (error) {
 
1332
        g_warning("xml_to_size_rule: invalid lower bound");
 
1333
                return;
 
1334
        }
 
1335
    G_FREE_NULL(buf);
 
1336
 
 
1337
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_SIZE_UPPER));
 
1338
    if (buf == NULL) {
 
1339
        g_warning("xml_to_size_rule: rule without upper bound");
 
1340
                return;
 
1341
        }
 
1342
    upper = parse_number(buf, &error);
 
1343
        if (error) {
 
1344
        g_warning("xml_to_size_rule: invalid upper bound");
 
1345
                return;
 
1346
        }
 
1347
    G_FREE_NULL(buf);
 
1348
 
 
1349
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1350
    target = parse_target(buf, &error);
 
1351
    if (error) {
 
1352
        g_warning("xml_to_size_rule: %s (%p)",
 
1353
                        g_strerror(error), cast_to_gconstpointer(target));
 
1354
                return;
 
1355
        }
 
1356
    G_FREE_NULL(buf);
 
1357
 
 
1358
    flags = get_rule_flags_from_xml(xmlnode);
 
1359
    rule = filter_new_size_rule(lower, upper, target, flags);
 
1360
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1361
 
 
1362
    if (gui_debug >= 4) {
 
1363
        g_message("added to filter \"%s\" rule with target %p",
 
1364
            filter->name, cast_to_gconstpointer(rule->target));
 
1365
        }
 
1366
 
 
1367
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1368
}
 
1369
 
 
1370
static void
 
1371
xml_to_jump_rule(xmlNodePtr xmlnode, gpointer data)
 
1372
{
 
1373
    gchar *buf;
 
1374
    rule_t *rule;
 
1375
    filter_t *target, *filter = data;
 
1376
    guint16 flags;
 
1377
        gint error;
 
1378
 
 
1379
    g_assert(xmlnode != NULL);
 
1380
    g_assert(xmlnode->name != NULL);
 
1381
    g_assert(filter != NULL);
 
1382
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1383
                                        NODE_RULE_JUMP));
 
1384
 
 
1385
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1386
    g_assert(buf != NULL);
 
1387
    target = parse_target(buf, &error);
 
1388
    G_FREE_NULL(buf);
 
1389
    if (error) {
 
1390
        g_warning( "xml_to_jump_rule: %s", g_strerror(error));
 
1391
                return;
 
1392
        }
 
1393
 
 
1394
    flags = get_rule_flags_from_xml(xmlnode);
 
1395
    rule = filter_new_jump_rule(target,flags);
 
1396
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1397
 
 
1398
    if (gui_debug >= 4) {
 
1399
        g_message("added to filter \"%s\" rule with target %p",
 
1400
            filter->name, cast_to_gconstpointer(rule->target));
 
1401
        }
 
1402
 
 
1403
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1404
}
 
1405
 
 
1406
static void
 
1407
xml_to_sha1_rule(xmlNodePtr xmlnode, gpointer data)
 
1408
{
 
1409
    const gchar *hash = NULL;
 
1410
    gchar *filename = NULL;
 
1411
    gchar *buf;
 
1412
    rule_t *rule;
 
1413
    filter_t *target, *filter = data;
 
1414
    guint16 flags;
 
1415
        gint error;
 
1416
 
 
1417
    g_assert(xmlnode != NULL);
 
1418
    g_assert(xmlnode->name != NULL);
 
1419
    g_assert(filter != NULL);
 
1420
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1421
                                        NODE_RULE_SHA1));
 
1422
 
 
1423
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_SHA1_FILENAME));
 
1424
    filename = buf != NULL ? buf : g_strdup("[Unknown]");
 
1425
 
 
1426
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_SHA1_HASH));
 
1427
    if (buf != NULL) {
 
1428
                hash = strlen(buf) == SHA1_BASE32_SIZE ? base32_sha1(buf) : NULL;
 
1429
        G_FREE_NULL(buf);
 
1430
                if (!hash) {
 
1431
                g_warning("xml_to_sha1_rule: Invalidly encoded SHA1");
 
1432
                        return;
 
1433
                }
 
1434
        } else {
 
1435
                hash = NULL;
 
1436
        }
 
1437
 
 
1438
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1439
    g_assert(buf != NULL);
 
1440
    target = parse_target(buf, &error);
 
1441
    G_FREE_NULL(buf);
 
1442
    if (error) {
 
1443
        g_warning("xml_to_sha1_rule: %s", g_strerror(error));
 
1444
                return;
 
1445
        }
 
1446
 
 
1447
    flags = get_rule_flags_from_xml(xmlnode);
 
1448
    rule = filter_new_sha1_rule(hash, filename, target, flags);
 
1449
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1450
 
 
1451
    G_FREE_NULL(filename);
 
1452
 
 
1453
    if (gui_debug >= 4) {
 
1454
        g_message("added to filter \"%s\" rule with target %p",
 
1455
            filter->name, cast_to_gconstpointer(rule->target));
 
1456
        }
 
1457
 
 
1458
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1459
}
 
1460
 
 
1461
static void
 
1462
xml_to_flag_rule(xmlNodePtr xmlnode, gpointer data)
 
1463
{
 
1464
    enum rule_flag_action stable = RULE_FLAG_IGNORE;
 
1465
    enum rule_flag_action busy   = RULE_FLAG_IGNORE;
 
1466
    enum rule_flag_action push   = RULE_FLAG_IGNORE;
 
1467
    gchar *buf;
 
1468
    rule_t *rule;
 
1469
    filter_t *target, *filter = data;
 
1470
    guint16 flags;
 
1471
        guint64 v;
 
1472
        gint error;
 
1473
 
 
1474
    g_assert(xmlnode != NULL);
 
1475
    g_assert(xmlnode->name != NULL);
 
1476
    g_assert(filter != NULL);
 
1477
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1478
                                        NODE_RULE_FLAG));
 
1479
 
 
1480
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_FLAG_STABLE));
 
1481
    if (buf != NULL) {
 
1482
        v = parse_number(buf, &error);
 
1483
        G_FREE_NULL(buf);
 
1484
        if (error) {
 
1485
                g_warning("xml_to_flag_rule: %s", g_strerror(error));
 
1486
                } else if (v == RULE_FLAG_SET || v == RULE_FLAG_UNSET) {
 
1487
            stable = v;
 
1488
                }
 
1489
    }
 
1490
 
 
1491
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_FLAG_BUSY));
 
1492
    if (buf != NULL) {
 
1493
        v = parse_number(buf, &error);
 
1494
        G_FREE_NULL(buf);
 
1495
        if (error) {
 
1496
                g_warning("xml_to_flag_rule: %s", g_strerror(error));
 
1497
                } else if (v == RULE_FLAG_SET || v == RULE_FLAG_UNSET) {
 
1498
            busy = v;
 
1499
                }
 
1500
    }
 
1501
 
 
1502
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_FLAG_PUSH));
 
1503
    if (buf != NULL) {
 
1504
        v = parse_number(buf, &error);
 
1505
        G_FREE_NULL(buf);
 
1506
        if (error) {
 
1507
                g_warning("xml_to_flag_rule: %s", g_strerror(error));
 
1508
                } else if (v == RULE_FLAG_SET || v == RULE_FLAG_UNSET) {
 
1509
            push = v;
 
1510
                }
 
1511
    }
 
1512
 
 
1513
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1514
    g_assert(buf != NULL);
 
1515
    target = parse_target(buf, &error);
 
1516
    G_FREE_NULL(buf);
 
1517
    if (error) {
 
1518
        g_warning( "xml_to_flag_rule: %s", g_strerror(error));
 
1519
                return;
 
1520
        }
 
1521
 
 
1522
    flags = get_rule_flags_from_xml(xmlnode);
 
1523
    rule = filter_new_flag_rule(stable, busy, push, target, flags);
 
1524
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1525
 
 
1526
    if (gui_debug >= 4) {
 
1527
        g_message("added to filter \"%s\" rule with target %p",
 
1528
            filter->name, cast_to_gconstpointer(rule->target));
 
1529
        }
 
1530
 
 
1531
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1532
}
 
1533
 
 
1534
static void
 
1535
xml_to_state_rule(xmlNodePtr xmlnode, gpointer data)
 
1536
{
 
1537
    enum filter_prop_state display = FILTER_PROP_STATE_UNKNOWN;
 
1538
    enum filter_prop_state download = FILTER_PROP_STATE_UNKNOWN;
 
1539
    gchar *buf;
 
1540
    rule_t *rule;
 
1541
    filter_t *target, *filter = data;
 
1542
    guint16 flags;
 
1543
        gint error;
 
1544
        guint64 v;
 
1545
 
 
1546
    g_assert(xmlnode != NULL);
 
1547
    g_assert(xmlnode->name != NULL);
 
1548
    g_assert(filter != NULL);
 
1549
    g_assert(0 == g_ascii_strcasecmp((const gchar *) xmlnode->name,
 
1550
                                        NODE_RULE_STATE));
 
1551
 
 
1552
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_STATE_DISPLAY));
 
1553
    if (buf != NULL) {
 
1554
        v = parse_number(buf, &error);
 
1555
        G_FREE_NULL(buf);
 
1556
                if (error) {
 
1557
                g_warning( "xml_to_state_rule: %s", g_strerror(error));
 
1558
                }
 
1559
        if (v <= MAX_FILTER_PROP_STATE || v == FILTER_PROP_STATE_IGNORE) {
 
1560
            display = v;
 
1561
                }
 
1562
    }
 
1563
 
 
1564
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_STATE_DOWNLOAD));
 
1565
    if (buf != NULL) {
 
1566
        v = parse_number(buf, &error);
 
1567
        G_FREE_NULL(buf);
 
1568
                if (error) {
 
1569
                g_warning( "xml_to_state_rule: %s", g_strerror(error));
 
1570
                        return;
 
1571
                }
 
1572
        if (v <= MAX_FILTER_PROP_STATE || v == FILTER_PROP_STATE_IGNORE) {
 
1573
            download = v;
 
1574
                }
 
1575
    }
 
1576
 
 
1577
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_TARGET));
 
1578
    g_assert(buf != NULL);
 
1579
    target = parse_target(buf, &error);
 
1580
    G_FREE_NULL(buf);
 
1581
    if (error) {
 
1582
        g_warning( "xml_to_state_rule: %s", g_strerror(error));
 
1583
                return;
 
1584
        }
 
1585
 
 
1586
    flags = get_rule_flags_from_xml(xmlnode);
 
1587
    rule = filter_new_state_rule(display, download, target, flags);
 
1588
    clear_flags(rule->flags, RULE_FLAG_VALID);
 
1589
 
 
1590
    if (gui_debug >= 4) {
 
1591
        g_message("added to filter \"%s\" rule with target %p",
 
1592
            filter->name, cast_to_gconstpointer(rule->target));
 
1593
        }
 
1594
 
 
1595
    filter->ruleset = g_list_append(filter->ruleset, rule);
 
1596
}
 
1597
 
 
1598
static guint16
 
1599
get_rule_flags_from_xml(xmlNodePtr xmlnode)
 
1600
{
 
1601
    gboolean negate = FALSE;
 
1602
    gboolean active = TRUE;
 
1603
    gboolean soft   = FALSE;
 
1604
    guint16 flags;
 
1605
    gchar *buf;
 
1606
        gint error;
 
1607
        guint64 v;
 
1608
 
 
1609
    g_assert(xmlnode != NULL);
 
1610
    g_assert(xmlnode->name != NULL);
 
1611
 
 
1612
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_NEGATE));
 
1613
    if (buf != NULL) {
 
1614
        v = parse_number(buf, &error);
 
1615
        G_FREE_NULL(buf);
 
1616
                if (error || v > 1) {
 
1617
                        g_warning("get_rule_flags_from_xml: Invalid \"negate\" tag");
 
1618
                } else {
 
1619
                negate = 0 != v;
 
1620
                }
 
1621
    }
 
1622
 
 
1623
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_ACTIVE));
 
1624
    if (buf != NULL) {
 
1625
        v = parse_number(buf, &error);
 
1626
        G_FREE_NULL(buf);
 
1627
                if (error || v > 1) {
 
1628
                        g_warning("get_rule_flags_from_xml: Invalid \"active\" tag");
 
1629
                } else {
 
1630
                active = 0 != v;
 
1631
                }
 
1632
    }
 
1633
 
 
1634
    buf = STRTRACK(xml_get_string(xmlnode, TAG_RULE_SOFT));
 
1635
    if (buf != NULL) {
 
1636
        v = parse_number(buf, &error);
 
1637
        G_FREE_NULL(buf);
 
1638
                if (error || v > 1) {
 
1639
                        g_warning("get_rule_flags_from_xml: Invalid \"soft\" tag");
 
1640
                } else {
 
1641
                soft = 0 != v;
 
1642
                }
 
1643
    }
 
1644
 
 
1645
    flags =
 
1646
        (negate ? RULE_FLAG_NEGATE : 0) |
 
1647
        (active ? RULE_FLAG_ACTIVE : 0) |
 
1648
        (soft   ? RULE_FLAG_SOFT   : 0);
 
1649
 
 
1650
    return flags;
 
1651
}
 
1652
 
 
1653
/* vi: set ts=4 sw=4 cindent: */