/* -*- Mode: C; c-file-style: "linux"; indent-tabs-mode: t; c-basic-offset: 8; -*- */
/* Load save filter descriptions/options from an xml file */
#include <glib.h>
#include <gtk/gtk.h>
#include <gnome.h>
#include <gtkhtml/gtkhtml.h>
#include <gnome-xml/tree.h>
#include <gnome-xml/parser.h>
#include "filter-arg-types.h"
#include "filter-xml.h"
#define d(x)
struct token_tab {
char *name;
enum filter_xml_token token;
};
struct token_tab token_table[] = {
{ "action", FILTER_XML_ACTION },
{ "address", FILTER_XML_ADDRESS },
{ "code", FILTER_XML_CODE },
{ "description", FILTER_XML_DESC },
{ "except", FILTER_XML_EXCEPT },
{ "folder", FILTER_XML_FOLDER },
{ "match", FILTER_XML_MATCH },
{ "name", FILTER_XML_NAME },
{ "option", FILTER_XML_OPTION },
{ "optionrule", FILTER_XML_OPTIONRULE },
{ "optionset", FILTER_XML_OPTIONSET },
{ "optionvalue", FILTER_XML_OPTIONVALUE },
{ "receive", FILTER_XML_RECEIVE },
{ "rule", FILTER_XML_RULE },
{ "ruleset", FILTER_XML_RULESET },
{ "send", FILTER_XML_SEND },
{ "source", FILTER_XML_SOURCE },
{ "text", FILTER_XML_TEXT },
};
/* convert a name to a token value */
static int
tokenise(const char *name)
{
int i;
int len = sizeof(token_table)/sizeof(token_table[0]);
if (name) {
for (i=0;i<len;i++) {
if (strcmp(name, token_table[i].name) == 0)
return token_table[i].token;
}
}
return -1;
}
static int
tokenise_xmlfreeprop(char *name)
{
int ret = -1;
ret = tokenise(name);
free(name);
return ret;
}
static char *
detokenise(int token)
{
int i;
int len = sizeof(token_table)/sizeof(token_table[0]);
if (token>=0) {
for (i=0;i<len;i++) {
if (token_table[i].token == token)
return token_table[i].name;
}
}
return "<unknown>";
}
static xmlNodePtr
find_node(xmlNodePtr start, char *name)
{
d(printf("trying to find node '%s'\n", name));
while (start && strcmp(start->name, name))
start = start->next;
d(printf("node = %p\n", start));
return start;
}
static xmlNodePtr
find_node_attr(xmlNodePtr start, char *name, char *attrname, char *attrvalue)
{
char *s;
d(printf("looking for node named %s with attribute %s=%s\n", name, attrname, attrvalue));
while ( start && (start = find_node(start, name)) ) {
s = xmlGetProp(start, attrname);
d(printf(" comparing '%s' to '%s'\n", s, attrvalue));
if (s && !strcmp(s, attrvalue)) {
free(s);
break;
}
free(s);
start = start->next;
}
return start;
}
static GList *
load_desc(xmlNodePtr node, int type, int vartype, char *varname)
{
struct filter_desc *desc;
xmlNodePtr n;
int newtype;
int newvartype;
char *newvarname;
GList *list = NULL;
while (node) {
if (node->content) {
desc = g_malloc0(sizeof(*desc));
desc->data = node->content;
desc->type = type;
desc->vartype = vartype;
desc->varname = varname?g_strdup(varname):0;
d(printf(" **** node name = %s var name = %s var type = %s\n", node->name, varname, detokenise(vartype)));
list = g_list_append(list, desc);
d(printf("appending '%s'\n", node->content));
newtype = type;
newvartype = -1;
newvarname = NULL;
} else {
newtype = tokenise(node->name);
newvartype = tokenise_xmlfreeprop(xmlGetProp(node, "type"));
newvarname = xmlGetProp(node, "name");
}
n = node->childs;
while (n) {
d(printf("adding child '%s'\n", n->name));
list = g_list_concat(list, load_desc(n, newtype, newvartype, newvarname));
n = n->next;
}
if (newvarname)
free(newvarname);
node = node->next;
}
return list;
}
GList *
filter_load_ruleset(xmlDocPtr doc)
{
xmlNodePtr ruleset, rule, n;
struct filter_rule *r;
int type;
int ruletype;
GList *rules = NULL;
g_return_val_if_fail(doc!=NULL, NULL);
ruleset = find_node(doc->root->childs, "ruleset");
while (ruleset) {
rule = ruleset->childs;
ruletype = tokenise_xmlfreeprop(xmlGetProp(ruleset, "type"));
d(printf("ruleset, name = %s\n", ruleset->name));
while (rule) {
n = rule->childs;
r = g_malloc0(sizeof(*r));
r->type = ruletype;
r->name = xmlGetProp(rule, "name");
d(printf(" rule, name = %s\n", r->name));
while (n) {
type = tokenise(n->name);
d(printf(" n, name = %s\n", n->name));
d(printf(" ncontent = %s\n", n->content));
d(printf(" childs = %p\n", n->childs));
if (n->childs) {
d(printf(" childs content = %s\n", n->childs->content));
}
switch(type) {
case FILTER_XML_CODE:
r->code = xmlNodeGetContent(n);
break;
case FILTER_XML_DESC:
d(printf(" ** loading description\n"));
r->description = load_desc(n->childs, type, -1, NULL);
d(printf(" ** done loading description\n"));
break;
default:
printf("warning, unknown token encountered\n");
break;
}
n = n->next;
}
if (r)
rules = g_list_append(rules, r);
rule = rule->next;
}
ruleset = find_node(ruleset->next, "ruleset");
}
return rules;
}
int
filter_find_rule(struct filter_rule *a, char *name)
{
d(printf("finding, is %s = %s?\n", a->name, name));
return strcmp(a->name, name);
}
int
filter_find_arg(FilterArg *a, char *name)
{
d(printf("finding, is %s = %s?\n", a->name, name));
return strcmp(a->name, name);
}
static FilterArg *
load_optionvalue(struct filter_desc *desc, xmlNodePtr node)
{
FilterArg *arg = NULL;
d(printf("creating arg entry for '%s'\n", desc->varname));
switch(desc->vartype) {
case FILTER_XML_ADDRESS:
arg = filter_arg_address_new(desc->varname);
break;
case FILTER_XML_FOLDER:
arg = filter_arg_folder_new(desc->varname);
break;
default:
d(printf("ok, maybe we're not\n"));
/* unknown arg type, drop it */
return NULL;
}
if (node == NULL)
return arg;
filter_arg_values_add_xml(arg, node);
return arg;
}
GList *
filter_load_optionset(xmlDocPtr doc, GList *rules)
{
xmlNodePtr optionset, option, o, or;
struct filter_option *op;
struct filter_optionrule *optionrule;
struct filter_rule *fr;
struct filter_desc *desc;
int type;
GList *l = NULL;
GList *lrule;
GList *ldesc;
g_return_val_if_fail(doc!=NULL, NULL);
optionset = find_node(doc->root->childs, "optionset");
if (optionset == NULL) {
printf("optionset not found\n");
return NULL;
}
option = find_node(optionset->childs, "option");
while (option) {
o = option->childs;
op = g_malloc0(sizeof(*op));
d(printf("option = %s\n", o->name));
d(printf("option, type=%s\n", xmlGetProp(option, "type")));
op->type = tokenise_xmlfreeprop(xmlGetProp(option, "type"));
while (o) {
char *rulestr;
type = tokenise(o->name);
switch (type) {
case FILTER_XML_OPTIONRULE:
rulestr = xmlGetProp(o, "rule");
lrule = g_list_find_custom(rules, rulestr, (GCompareFunc) filter_find_rule);
if (lrule) {
fr = lrule->data;
d(printf("found rule : %s\n", fr->name));
optionrule = g_malloc0(sizeof(*optionrule));
optionrule->rule = fr;
op->options = g_list_append(op->options, optionrule);
/* scan through all variables required, setup blank variables if they do not exist */
ldesc = fr->description;
while (ldesc) {
desc = ldesc->data;
if (desc->varname && desc->vartype!=-1) {
FilterArg *arg;
/* try and see if there is a setting for this value */
or = find_node_attr(o->childs, "optionvalue", "name", desc->varname);
arg = load_optionvalue(desc, or);
if (arg) {
optionrule->args = g_list_append(optionrule->args, arg);
d(printf("Adding arg %s\n", arg->name));
}
}
ldesc = g_list_next(ldesc);
}
} else {
/* FIXME: memleak */
printf("Cannot find rule: %s\n", rulestr);
}
free(rulestr);
break;
case FILTER_XML_DESC:
d(printf("loading option descriptiong\n"));
op->description = load_desc(option->childs, type, -1, NULL);
break;
}
o = o->next;
}
l = g_list_append(l, op);
option = find_node(option->next, "option");
}
return l;
}
static xmlNodePtr
write_description(xmlDocPtr doc, GList *descl)
{
xmlNodePtr d;
struct filter_desc *desc;
desc = descl->data;
d = xmlNewDocNode(doc, NULL, "description", NULL);
xmlNodeSetContent(d, desc->data);
return d;
}
xmlNodePtr
filter_write_optionset(xmlDocPtr doc, GList *optionl)
{
xmlNodePtr root, option, optionrule, optionvalue;
GList *optionrulel, *argl;
struct filter_optionrule *or;
root = xmlNewDocNode(doc, NULL, "optionset", NULL);
/* for all options */
while (optionl) {
struct filter_option *op = optionl->data;
option = xmlNewDocNode(doc, NULL, "option", NULL);
xmlSetProp(option, "type", detokenise(op->type));
if (op->description) {
xmlNodePtr desc;
desc = write_description(doc, op->description);
xmlAddChild(option, desc);
}
optionrulel = op->options;
while (optionrulel) {
or = optionrulel->data;
optionrule = xmlNewDocNode(doc, NULL, "optionrule", NULL);
xmlSetProp(optionrule, "type", detokenise(or->rule->type));
xmlSetProp(optionrule, "rule", or->rule->name);
argl = or->args;
while (argl) {
FilterArg *arg = argl->data;
optionvalue = filter_arg_values_get_xml(arg);
if (optionvalue)
xmlAddChild(optionrule, optionvalue);
argl = g_list_next(argl);
}
xmlAddChild(option, optionrule);
optionrulel = g_list_next(optionrulel);
}
xmlAddChild(root, option);
optionl = g_list_next(optionl);
}
return root;
}
/* utility functions */
struct filter_optionrule *
filter_clone_optionrule(struct filter_optionrule *or)
{
GList *arg;
struct filter_optionrule *rule;
rule = g_malloc0(sizeof(*rule));
rule->rule = or->rule;
arg = or->args;
while (arg) {
FilterArg *new = filter_arg_clone(FILTER_ARG(arg->data));
gtk_object_set_data((GtkObject *)new, "origin", arg->data);
rule->args = g_list_append(rule->args, new);
arg = g_list_next(arg);
}
return rule;
}
void
filter_clone_optionrule_free(struct filter_optionrule *or)
{
GList *argl;
d(printf("---- free optionrule\n"));
argl = or->args;
while (argl) {
gtk_object_unref(GTK_OBJECT(argl->data));
argl = g_list_next(argl);
}
g_list_free(or->args);
g_free(or);
}
struct filter_optionrule *
filter_optionrule_new_from_rule(struct filter_rule *rule)
{
struct filter_optionrule *or;
GList *descl;
or = g_malloc0(sizeof(*or));
or->rule = rule;
descl = rule->description;
while (descl) {
struct filter_desc *desc = descl->data;
if (desc->varname && desc->vartype != -1) {
FilterArg *arg = NULL;
switch (desc->vartype) {
case FILTER_XML_ADDRESS:
arg = filter_arg_address_new(desc->varname);
break;
case FILTER_XML_FOLDER:
arg = filter_arg_folder_new(desc->varname);
break;
}
if (arg) {
or->args = g_list_append(or->args, arg);
}
}
descl = g_list_next(descl);
}
return or;
}
void
filter_description_free(GList *descl)
{
GList *node;
node = descl;
while (node) {
GList *next = g_list_next(node);
struct filter_desc *d = node->data;
g_free(d->data);
g_free(d->varname);
g_free(d);
node = next;
}
g_list_free(descl);
}
void
filter_load_ruleset_free(GList *nodel)
{
GList *node = nodel;
while (node) {
GList *next = g_list_next(node);
struct filter_rule *r = node->data;
filter_description_free(r->description);
/* g_free(r->name); */
/* g_free(r->code); */
g_free(r);
node = next;
}
g_list_free(nodel);
}
void
filter_load_optionset_free(GList *optionl)
{
GList *option = optionl;
while (option) {
GList *next = g_list_next(option);
struct filter_option *fo = option->data;
GList *optionrule = fo->options;
while (optionrule) {
GList *next = g_list_next(optionrule);
struct filter_optionrule *or = optionrule->data;
GList *arg = or->args;
while (arg) {
gtk_object_unref(arg->data);
arg = g_list_next(arg);
}
g_list_free(or->args);
g_free(or);
optionrule = next;
}
filter_description_free(fo->description);
g_list_free(fo->options);
g_free(fo);
option = next;
}
}
GList *filter_load_ruleset_file(const char *name)
{
xmlDocPtr doc;
GList *rules;
doc = xmlParseFile(name);
rules = filter_load_ruleset(doc);
xmlFreeDoc(doc);
return rules;
}
GList *filter_load_optionset_file(const char *name, GList *rules)
{
xmlDocPtr doc;
GList *options;
doc = xmlParseFile(name);
options = filter_load_optionset(doc, rules);
xmlFreeDoc(doc);
return options;
}
int filter_write_optionset_file(const char *name, GList *optionl)
{
xmlDocPtr out;
xmlDocPtr optionset;
xmlNodePtr filteroptions;
int ret;
out = xmlNewDoc("1.0");
optionset = filter_write_optionset(out, optionl);
filteroptions = xmlNewDocNode(out, NULL, "filteroptions", NULL);
xmlAddChild(filteroptions, optionset);
xmlDocSetRootElement(out, filteroptions);
ret = xmlSaveFile(name, out);
xmlFreeDoc(out);
return ret;
}