This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
gnoemoe/gnoemoe/gm-triggers.c

605 lines
14 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_match_user(GmTrigger *trigger, gchar const *username,
GmTriggerConditionType condition, regmatch_t *matches, gint nmatch) {
GmTriggerData *data;
GList *item;
gint cmatch = 0, offset, ret;
if (!trigger->conditions) {
return FALSE;
}
for (item = trigger->conditions; item; item = item->next) {
data = (GmTriggerData *)(item->data);
if (cmatch >= nmatch) {
return 0;
}
if (data->type == condition) {
offset = 0;
ret = regexec(&(data->expr), (char *)(username),
(nmatch - cmatch) - 1, matches + cmatch, 0);
if (ret == 0) {
while (matches[cmatch].rm_so != -1) {
matches[cmatch].rm_eo = matches[cmatch].rm_eo -
matches[cmatch].rm_so;
matches[cmatch].rm_so = matches[cmatch].rm_so + offset;
cmatch++;
}
} else {
return 0;
}
} else {
return 0;
}
}
return cmatch;
}
gint
gm_trigger_match(GmTrigger *trigger, gchar *text, regmatch_t *matches,
gint nmatch) {
GmTriggerData *data;
GList *item;
gchar *tmp;
gboolean istrue;
gint cmatch = 0, ret, offset, len;
if (!trigger->conditions) {
return 0;
}
for (item = trigger->conditions; item; item = item->next) {
data = (GmTriggerData *)(item->data);
len = g_utf8_strlen(data->data, -1);
if (cmatch >= nmatch) {
return 0;
}
switch (data->type) {
case TCT_CONTAINS: case TCT_NOT_CONTAINS: case TCT_BEGINS:
case TCT_NOT_BEGINS: case TCT_ENDS: case TCT_NOT_ENDS:
tmp = strstr(text, data->data);
switch (data->type) {
case TCT_CONTAINS:
if (tmp != NULL) {
while (tmp != NULL && cmatch < nmatch) {
matches[cmatch].rm_so =
g_utf8_pointer_to_offset(text, tmp);
matches[cmatch].rm_eo = len;
tmp = strstr(tmp + len, data->data);
if (tmp != NULL) {
++cmatch;
}
}
} else {
return 0;
}
break;
case TCT_NOT_CONTAINS:
if (tmp == NULL) {
matches[cmatch].rm_so = 0;
matches[cmatch].rm_eo = len;
} else {
return 0;
}
break;
case TCT_NOT_BEGINS:
if (tmp != text) {
matches[cmatch].rm_so = 0;
matches[cmatch].rm_eo = g_utf8_strlen(text, -1);
} else {
return 0;
}
break;
default:
istrue = (tmp != NULL &&
(g_utf8_pointer_to_offset(text, tmp) + len) ==
g_utf8_strlen(text, -1));
if (istrue && data->type == TCT_ENDS) {
matches[cmatch].rm_so =
g_utf8_pointer_to_offset(text, tmp);
matches[cmatch].rm_eo = len;
} else if (!istrue && data->type == TCT_NOT_ENDS) {
matches[cmatch].rm_so = 0;
matches[cmatch].rm_eo = g_utf8_strlen(text, -1);
} else {
return 0;
}
}
cmatch++;
matches[cmatch].rm_so = -1;
break;
case TCT_MATCHES: case TCT_NOT_MATCHES:
offset = 0;
ret = regexec(&(data->expr), (char *)text,
(nmatch - cmatch) - 1, matches + cmatch, 0);
if (ret == 0 && data->type == TCT_MATCHES) {
while (ret == 0) {
while (matches[cmatch].rm_so != -1) {
matches[cmatch].rm_eo = matches[cmatch].rm_eo -
matches[cmatch].rm_so;
matches[cmatch].rm_so = matches[cmatch].rm_so +
offset;
++cmatch;
}
offset = matches[cmatch - 1].rm_so +
matches[cmatch - 1].rm_eo;
if (cmatch < nmatch) {
ret = regexec(&(data->expr),
(char *)(text + offset),
(nmatch - cmatch) - 1,
matches + cmatch,
0);
} else {
ret = 1;
}
}
} else if (ret != 0 && data->type == TCT_NOT_MATCHES) {
matches[cmatch].rm_so = 0;
matches[cmatch].rm_eo = g_utf8_strlen(text, -1);
} else {
return 0;
}
break;
default:
return 0;
break;
}
}
return cmatch;
}
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;
trg->priv->path = g_strdup(filename);
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
gm_debug_msg(DEBUG_DEFAULT, "GmTriggers.NewFromFile: Trigger file does not exist");
return trg;
}
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;
}