441 lines
10 KiB
C
441 lines
10 KiB
C
|
#include <strings.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <libxml/parser.h>
|
||
|
#include "gm-triggers.h"
|
||
|
#include "gm-debug.h"
|
||
|
|
||
|
#define GM_TRIGGERS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TRIGGERS, GmTriggersPrivate))
|
||
|
|
||
|
typedef struct _trigger_trans {
|
||
|
gint type;
|
||
|
const gchar *name;
|
||
|
} trigger_trans;
|
||
|
|
||
|
static const trigger_trans table_conditions[] = {
|
||
|
{TCT_CONTAINS, "contains"},
|
||
|
{TCT_NOT_CONTAINS, "not-contains"},
|
||
|
{TCT_BEGINS, "begins"},
|
||
|
{TCT_NOT_BEGINS, "not-begins"},
|
||
|
{TCT_ENDS, "ends"},
|
||
|
{TCT_NOT_ENDS, "not-ends"},
|
||
|
{TCT_MATCHES, "matches"},
|
||
|
{TCT_NOT_MATCHES, "not-matches"},
|
||
|
{TCT_USER_ONLINE, "online"},
|
||
|
{TCT_USER_OFFLINE, "offline"},
|
||
|
{TCT_USER_IDLE, "idle"},
|
||
|
{TCT_USER_IDLE_OFF, "idle-off"},
|
||
|
{TCT_USER_AWAY, "away"},
|
||
|
{TCT_USER_AWAY_OFF, "away-off"},
|
||
|
{-1, NULL}
|
||
|
};
|
||
|
|
||
|
static const trigger_trans table_actions[] = {
|
||
|
{TAT_HIGHLIGHT_LINE, "highlight-line"},
|
||
|
{TAT_HIGHLIGHT_MATCH, "highlight-match"},
|
||
|
{TAT_BEEP, "beep"},
|
||
|
{TAT_PLAY_SOUND, "play-sound"},
|
||
|
{TAT_NOTIFY, "notify"},
|
||
|
#ifdef HASRUBY
|
||
|
{TAT_RUN_SCRIPT, "run-script"},
|
||
|
#endif
|
||
|
{TAT_RUN, "run"},
|
||
|
{-1, NULL}
|
||
|
};
|
||
|
|
||
|
struct _GmTriggersPrivate {
|
||
|
GList *triggers;
|
||
|
gchar *path;
|
||
|
};
|
||
|
|
||
|
/* Signals */
|
||
|
|
||
|
/*enum {
|
||
|
PROTO
|
||
|
NUM_SIGNALS
|
||
|
};
|
||
|
|
||
|
static guint triggers_signals[NUM_SIGNALS] = {0};*/
|
||
|
|
||
|
G_DEFINE_TYPE(GmTriggers, gm_triggers, G_TYPE_OBJECT)
|
||
|
|
||
|
static void
|
||
|
gm_triggers_finalize(GObject *object) {
|
||
|
GmTriggers *trg = GM_TRIGGERS(object);
|
||
|
GList *item;
|
||
|
|
||
|
for (item = trg->priv->triggers; item; item = item->next) {
|
||
|
gm_trigger_free((GmTrigger *)(item->data));
|
||
|
}
|
||
|
|
||
|
g_free(trg->priv->path);
|
||
|
g_list_free(trg->priv->triggers);
|
||
|
|
||
|
G_OBJECT_CLASS(gm_triggers_parent_class)->finalize(object);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gm_triggers_class_init(GmTriggersClass *klass) {
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||
|
|
||
|
object_class->finalize = gm_triggers_finalize;
|
||
|
|
||
|
g_type_class_add_private(object_class, sizeof(GmTriggersPrivate));
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
gm_triggers_init(GmTriggers *trg) {
|
||
|
trg->priv = GM_TRIGGERS_GET_PRIVATE(trg);
|
||
|
trg->priv->triggers = NULL;
|
||
|
trg->priv->path = NULL;
|
||
|
}
|
||
|
|
||
|
GmTrigger *
|
||
|
gm_trigger_new() {
|
||
|
return g_new0(GmTrigger, 1);
|
||
|
}
|
||
|
|
||
|
GList *
|
||
|
gm_trigger_list_dup(GList *list) {
|
||
|
GList *result = NULL;
|
||
|
GList *item;
|
||
|
GmTriggerData *data;
|
||
|
|
||
|
for (item = list; item; item = item->next) {
|
||
|
data = (GmTriggerData *)(item->data);
|
||
|
result = g_list_append(result, gm_trigger_data_new(data->type,
|
||
|
g_strdup(data->data)));
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
GmTrigger *
|
||
|
gm_trigger_dup(GmTrigger *source) {
|
||
|
GmTrigger *result = gm_trigger_new();
|
||
|
|
||
|
result->name = g_strdup(source->name);
|
||
|
result->event = source->event;
|
||
|
result->conditions = gm_trigger_list_dup(source->conditions);
|
||
|
result->actions = gm_trigger_list_dup(source->actions);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_set_name(GmTrigger *trigger, const gchar *name) {
|
||
|
g_free(trigger->name);
|
||
|
trigger->name = g_strdup(name);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_free_list(GList *list) {
|
||
|
GList *item;
|
||
|
GmTriggerData *data;
|
||
|
|
||
|
for (item = list; item; item = item->next) {
|
||
|
data = (GmTriggerData *)(item->data);
|
||
|
gm_trigger_data_free(data);
|
||
|
}
|
||
|
|
||
|
g_list_free(list);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_free(GmTrigger *trigger) {
|
||
|
gm_trigger_free_list(trigger->conditions);
|
||
|
gm_trigger_free_list(trigger->actions);
|
||
|
g_free(trigger->name);
|
||
|
|
||
|
g_free(trigger);
|
||
|
}
|
||
|
|
||
|
GmTriggerData *
|
||
|
gm_trigger_data_new(gint type, gchar *data) {
|
||
|
GmTriggerData *tdata = g_new0(GmTriggerData, 1);
|
||
|
tdata->type = type;
|
||
|
tdata->data = data;
|
||
|
|
||
|
memset(&(tdata->expr), 0, sizeof(regex_t));
|
||
|
|
||
|
switch (tdata->type) {
|
||
|
case TCT_MATCHES: case TCT_NOT_MATCHES: case TCT_USER_ONLINE:
|
||
|
case TCT_USER_OFFLINE: case TCT_USER_IDLE: case TCT_USER_IDLE_OFF:
|
||
|
case TCT_USER_AWAY: case TCT_USER_AWAY_OFF:
|
||
|
regcomp(&(tdata->expr), tdata->data, REG_EXTENDED);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return tdata;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_data_free(GmTriggerData *tdata) {
|
||
|
g_free(tdata->data);
|
||
|
g_free(tdata);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_add_condition(GmTrigger *trigger, GmTriggerData *condition) {
|
||
|
trigger->conditions = g_list_append(trigger->conditions, condition);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_add_action(GmTrigger *trigger, GmTriggerData *action) {
|
||
|
trigger->actions = g_list_append(trigger->actions, action);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_set_conditions(GmTrigger *trigger, GList *conditions) {
|
||
|
gm_trigger_free_list(trigger->conditions);
|
||
|
trigger->conditions = conditions;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_set_actions(GmTrigger *trigger, GList *actions) {
|
||
|
gm_trigger_free_list(trigger->actions);
|
||
|
trigger->actions = actions;
|
||
|
}
|
||
|
|
||
|
gint
|
||
|
gm_trigger_type_from_name(const gchar *name,
|
||
|
const trigger_trans *trans_table) {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; trans_table[i].type != -1; i++) {
|
||
|
if (strcasecmp(trans_table[i].name, name) == 0) {
|
||
|
return trans_table[i].type;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
const gchar *
|
||
|
gm_trigger_name_from_type(gint type, const trigger_trans *trans_table) {
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; trans_table[i].type != -1; i++) {
|
||
|
if (trans_table[i].type == type) {
|
||
|
return trans_table[i].name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_parse_trigger(GmTriggers *trg, xmlDocPtr doc, xmlNodePtr node) {
|
||
|
GmTrigger *result = gm_trigger_new();
|
||
|
xmlChar *tmp, *tmp2;
|
||
|
gint type;
|
||
|
|
||
|
tmp = xmlGetProp(node, (const xmlChar *)"name");
|
||
|
result->name = (gchar *)tmp;
|
||
|
|
||
|
tmp = xmlGetProp(node, (const xmlChar *)"event");
|
||
|
|
||
|
if (xmlStrcmp(tmp, (const xmlChar *)("world")) == 0) {
|
||
|
result->event = TT_OUTPUT;
|
||
|
} else if (xmlStrcmp(tmp, (const xmlChar *)("player")) == 0){
|
||
|
result->event = TT_USERS;
|
||
|
} else {
|
||
|
xmlFree(tmp);
|
||
|
gm_trigger_free(result);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
xmlFree(tmp);
|
||
|
|
||
|
for (node = node->xmlChildrenNode; node; node = node->next) {
|
||
|
tmp = xmlGetProp(node, (const xmlChar *)"type");
|
||
|
tmp2 = xmlGetProp(node, (const xmlChar *)"data");
|
||
|
|
||
|
if (xmlStrcmp(node->name, (const xmlChar *)("condition")) == 0) {
|
||
|
type = gm_trigger_type_from_name((const gchar *)(tmp),
|
||
|
table_conditions);
|
||
|
|
||
|
if (type != -1) {
|
||
|
gm_trigger_add_condition(result, gm_trigger_data_new(type,
|
||
|
g_strdup((char *)tmp2)));
|
||
|
}
|
||
|
} else if (xmlStrcmp(node->name, (const xmlChar *)("action")) == 0) {
|
||
|
type = gm_trigger_type_from_name((const gchar *)(tmp),
|
||
|
table_actions);
|
||
|
|
||
|
if (type != -1) {
|
||
|
gm_trigger_add_action(result, gm_trigger_data_new(type,
|
||
|
g_strdup((char *)tmp2)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
xmlFree(tmp2);
|
||
|
xmlFree(tmp);
|
||
|
}
|
||
|
|
||
|
gm_triggers_add(trg, result);
|
||
|
}
|
||
|
|
||
|
GmTriggers *
|
||
|
gm_triggers_new() {
|
||
|
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
|
||
|
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_add(GmTriggers *trg, GmTrigger *t) {
|
||
|
trg->priv->triggers = g_list_append(trg->priv->triggers, t);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_clear(GmTriggers *trg) {
|
||
|
GList *item;
|
||
|
|
||
|
for (item = trg->priv->triggers; item; item = item->next) {
|
||
|
gm_trigger_free((GmTrigger *)(item->data));
|
||
|
}
|
||
|
|
||
|
g_list_free(trg->priv->triggers);
|
||
|
trg->priv->triggers = NULL;
|
||
|
}
|
||
|
|
||
|
GmTriggers *
|
||
|
gm_triggers_new_from_file(gchar *filename) {
|
||
|
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
|
||
|
xmlDocPtr doc;
|
||
|
xmlNodePtr cur;
|
||
|
|
||
|
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
|
||
|
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: Trigger file does not exist");
|
||
|
return trg;
|
||
|
|
||
|
}
|
||
|
|
||
|
trg->priv->path = g_strdup(filename);
|
||
|
doc = xmlParseFile(filename);
|
||
|
|
||
|
if (doc == NULL) {
|
||
|
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: Error on parsing triggers");
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
cur = xmlDocGetRootElement(doc);
|
||
|
|
||
|
if (cur == NULL) {
|
||
|
xmlFreeDoc(doc);
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
if (xmlStrcmp(cur->name, (const xmlChar *)("triggers"))) {
|
||
|
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: invalid root node");
|
||
|
xmlFreeDoc(doc);
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
|
||
|
if (!xmlStrcmp(cur->name, (const xmlChar *)("trigger"))) {
|
||
|
gm_triggers_parse_trigger(trg, doc, cur);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
xmlFreeDoc(doc);
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_set_path(GmTriggers *trg, gchar *path) {
|
||
|
g_free(trg->priv->path);
|
||
|
trg->priv->path = g_strdup(path);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_trigger_rules_xml(GmTrigger *t, xmlNodePtr trig) {
|
||
|
GmTriggerData *data;
|
||
|
GList *item;
|
||
|
xmlNodePtr child;
|
||
|
|
||
|
for (item = t->conditions; item; item = item->next) {
|
||
|
data = (GmTriggerData *)(item->data);
|
||
|
child = xmlNewChild(trig, NULL, (const xmlChar *)("condition"), NULL);
|
||
|
xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *)
|
||
|
(gm_trigger_name_from_type(data->type, table_conditions)));
|
||
|
|
||
|
if (data->data) {
|
||
|
xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *)
|
||
|
(data->data));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (item = t->actions; item; item = item->next) {
|
||
|
data = (GmTriggerData *)(item->data);
|
||
|
child = xmlNewChild(trig, NULL, (const xmlChar *)("action"), NULL);
|
||
|
xmlNewProp(child, (const xmlChar *)("type"), (const xmlChar *)
|
||
|
(gm_trigger_name_from_type(data->type, table_actions)));
|
||
|
|
||
|
if (data->data) {
|
||
|
xmlNewProp(child, (const xmlChar *)("data"), (const xmlChar *)
|
||
|
(data->data));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_save(GmTriggers *trg) {
|
||
|
xmlDocPtr doc;
|
||
|
xmlNodePtr root;
|
||
|
xmlNodePtr trig;
|
||
|
GList *item;
|
||
|
GmTrigger *t;
|
||
|
|
||
|
g_return_if_fail(trg->priv->path != NULL);
|
||
|
|
||
|
doc = xmlNewDoc((const xmlChar *)("1.0"));
|
||
|
root = xmlNewNode(NULL, (const xmlChar *)("triggers"));
|
||
|
xmlDocSetRootElement(doc, root);
|
||
|
|
||
|
for (item = trg->priv->triggers; item; item = item->next) {
|
||
|
t = (GmTrigger *)(item->data);
|
||
|
trig = xmlNewChild(root, NULL, (const xmlChar *)("trigger"), NULL);
|
||
|
xmlNewProp(trig, (const xmlChar *)("name"), (const xmlChar *)(t->name));
|
||
|
|
||
|
if (t->event == TT_OUTPUT) {
|
||
|
xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("world"));
|
||
|
} else {
|
||
|
xmlNewProp(trig, (const xmlChar *)("event"), (const xmlChar *)("player"));
|
||
|
}
|
||
|
|
||
|
gm_trigger_rules_xml(t, trig);
|
||
|
}
|
||
|
|
||
|
xmlSaveFormatFileEnc(trg->priv->path, doc, "UTF-8", 1);
|
||
|
xmlFreeDoc(doc);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gm_triggers_save_as(GmTriggers *trg, const gchar *path) {
|
||
|
g_free(trg->priv->path);
|
||
|
trg->priv->path = g_strdup(path);
|
||
|
gm_triggers_save(trg);
|
||
|
}
|
||
|
|
||
|
GmTriggers *
|
||
|
gm_triggers_dup(GmTriggers *source) {
|
||
|
GmTriggers *trg = GM_TRIGGERS(g_object_new(GM_TYPE_TRIGGERS, NULL));
|
||
|
GList *item;
|
||
|
|
||
|
for (item = source->priv->triggers; item; item = item->next) {
|
||
|
trg->priv->triggers = g_list_append(trg->priv->triggers,
|
||
|
gm_trigger_dup((GmTrigger *)(item->data)));
|
||
|
}
|
||
|
|
||
|
return trg;
|
||
|
}
|
||
|
|
||
|
const GList *
|
||
|
gm_triggers_list(GmTriggers *trg) {
|
||
|
return trg->priv->triggers;
|
||
|
}
|