#include <stdio.h>
#include <time.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <sys/stat.h>
#include <sys/types.h>
-#ifdef _WIN32
-#include <direct.h>
-#define mkdir(s,t) _mkdir(s)
-#endif
-
-
#define MAX_SUBST 256
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "log_custom"
-const char *logfilename = NULL;
-GHashTable *fmts = NULL;
+struct file_info {
+ FILE *file;
+ time_t last_used;
+};
+
+struct log_custom_data {
+ char *logfilename;
+ GKeyFile *kf;
+};
+
+static GHashTable *files;
/* Translation table */
struct log_mapping {
char subst;
unsigned int index;
/* If index is -1 */
- char *(*callback) (struct network *, struct line *l, gboolean case_sensitive);
+ char *(*callback) (struct network *, const struct line *l, gboolean case_sensitive);
};
-static char *get_hours(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_hours(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%02d", t->tm_hour);
}
-static char *get_minutes(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_minutes(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%02d", t->tm_min);
}
-static char *get_seconds(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_seconds(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%02d", t->tm_sec);
}
-static char *get_seconds_since_1970(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_seconds_since_1970(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
return g_strdup_printf("%ld", ti);
}
-static char *get_day(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_day(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%02d", t->tm_mday);
}
-static char *get_month(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_month(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%02d", t->tm_mon + 1);
}
-static char *get_year(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_year(struct network *n, const struct line *l, gboolean case_sensitive)
+{
time_t ti = time(NULL);
struct tm *t = localtime(&ti);
return g_strdup_printf("%04d", t->tm_year + 1900);
}
-static char *get_user(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_user(struct network *n, const struct line *l, gboolean case_sensitive)
+{
char *nick = NULL;
char *user = NULL;
else return g_strdup(user);
}
-static char *get_monthname(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_monthname(struct network *n, const struct line *l, gboolean case_sensitive)
+{
char stime[512];
time_t ti = time(NULL);
strftime(stime, sizeof(stime), "%b", localtime(&ti));
return g_strdup_printf("%s", stime);
}
-static char *get_nick(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_nick(struct network *n, const struct line *l, gboolean case_sensitive)
+{
if (l->origin) {
- if(case_sensitive) return g_ascii_strdown(line_get_nick(l), -1);
- else return g_strdup(line_get_nick(l));
+ char *n = line_get_nick(l);
+ if(case_sensitive) {
+ char *r = g_ascii_strdown(n, -1);
+ g_free(n);
+ return r;
+ }
+ else return n;
}
return g_strdup("");
}
-static char *get_network(struct network *n, struct line *l, gboolean case_sensitive)
+static char *get_network(struct network *n, const struct line *l, gboolean case_sensitive)
{ return g_strdup(n->name); }
-static char *get_server(struct network *n, struct line *l, gboolean case_sensitive)
-{ return g_strdup(n->connection.data.tcp.current_server->name); }
-static char *get_percent(struct network *n, struct line *l, gboolean case_sensitive) { return g_strdup("%"); }
+static char *get_server(struct network *n, const struct line *l, gboolean case_sensitive)
+{
+ if (n->connection.data.tcp.current_server)
+ return g_strdup(n->connection.data.tcp.current_server->host);
+ return g_strdup("");
+}
+
+static char *get_percent(struct network *n, const struct line *l, gboolean case_sensitive) { return g_strdup("%"); }
static const char *identifier = NULL;
-static char *get_identifier(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_identifier(struct network *n, const struct line *l, gboolean case_sensitive) {
if(case_sensitive) return g_ascii_strdown(identifier, -1);
else return g_strdup(identifier);
}
-static char *get_modechanges(struct network *n, struct line *l, gboolean case_sensitive) {
+static char *get_modechanges(struct network *n, const struct line *l, gboolean case_sensitive) {
char buf[512] = "";
int i;
{ NULL }
};
-static char *find_mapping(struct network *network, struct line *l, char c, gboolean case_sensitive)
+static char *find_mapping(struct network *network, const struct line *l, char c, gboolean case_sensitive)
{
int i;
for(i = 0; mappings[i].subst; i++) {
for (j = 0; a[j]; j++) if (a[j] == '/') a[j] = '_';
}
-static void custom_subst(struct network *network, char **_new, const char *fmt, struct line *l, const char *_identifier, gboolean case_sensitive, gboolean noslash)
+static void custom_subst(struct network *network, char **_new, const char *fmt, const struct line *l, const char *_identifier, gboolean case_sensitive, gboolean noslash)
{
char *subst[MAX_SUBST];
char *new;
-- NICK: %r
*/
-static GHashTable *files = NULL;
-
-static FILE *find_add_channel_file(struct network *network, struct line *l, const char *identifier, gboolean create_file)
+static FILE *find_add_channel_file(struct log_custom_data *data, struct network *network, const struct line *l, const char *identifier, gboolean create_file)
{
char *n = NULL, *dn, *p;
- FILE *f;
- if(!logfilename) return NULL;
- custom_subst(network, &n, logfilename, l, identifier, TRUE, TRUE);
- f = g_hash_table_lookup(files, n);
- if(!f && create_file) {
+ struct file_info *fi;
+ if(!data->logfilename) return NULL;
+ custom_subst(network, &n, data->logfilename, l, identifier, TRUE, TRUE);
+ fi = g_hash_table_lookup(files, n);
+ if(fi == NULL && create_file) {
dn = g_strdup(n);
/* Only include directory-part */
if(p) *p = '\0';
/* Check if directory needs to be created */
- if(!g_file_test(dn, G_FILE_TEST_IS_DIR) && mkdir(dn, 0700) == -1) {
- log_network("log_custom", LOG_ERROR, network, "Couldn't create directory %s for logging!", dn);
+ if(!g_file_test(dn, G_FILE_TEST_IS_DIR) && g_mkdir(dn, 0700) == -1) {
+ log_network(LOG_ERROR, network, "Couldn't create directory %s for logging!", dn);
g_free(dn);
g_free(n);
return NULL;
}
g_free(dn);
+
+ fi = g_new0(struct file_info, 1);
/* Then open the correct filename */
- f = fopen(n, "a+");
- if(!f) {
- log_network("log_custom", LOG_ERROR, network, "Couldn't open file %s for logging!", n);
+ fi->file = fopen(n, "a+");
+ if(!fi->file) {
+ log_network(LOG_ERROR, network, "Couldn't open file %s for logging!", n);
g_free(n);
+ g_free(fi);
return NULL;
}
- g_hash_table_insert(files, n, f);
+ g_hash_table_insert(files, n, fi);
} else g_free(n);
- return f;
+
+ if (fi == NULL)
+ return NULL;
+
+ fi->last_used = time(NULL);
+ return fi->file;
}
-static void file_write_target(struct network *network, const char *n, struct line *l)
+static void file_write_target(struct log_custom_data *data, struct network *network, const char *n, const struct line *l)
{
char *t, *s, *fmt;
FILE *f;
-
- fmt = g_hash_table_lookup(fmts, n);
+
+ fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
if(!fmt) return;
-
- if(!irccmp(network->state->info, network->state->me.nick, l->args[1])) {
- if (l->origin) t = g_strdup(line_get_nick(l));
+
+ g_assert(l->args[0]);
+ g_assert(l->args[1]);
+ g_assert(network->state);
+ g_assert(network->state->me.nick);
+ g_assert(network->state->info);
+
+ if (!irccmp(network->state->info, network->state->me.nick, l->args[1])) {
+ if (l->origin) t = line_get_nick(l);
else t = g_strdup("_messages_");
} else {
t = g_strdup(l->args[1]);
}
- f = find_add_channel_file(network, l, t, TRUE);
+ f = find_add_channel_file(data, network, l, t, TRUE);
if(!f) { g_free(t); return; }
custom_subst(network, &s, fmt, l, t, FALSE, FALSE);
g_free(s);
}
-static void file_write_channel_only(struct network *network, const char *n, struct line *l)
+static void file_write_channel_only(struct log_custom_data *data, struct network *network, const char *n, const struct line *l)
{
char *s, *fmt;
FILE *f;
- fmt = g_hash_table_lookup(fmts, n);
+ fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
if(!fmt) return;
- f = find_add_channel_file(network, l, l->args[1], TRUE);
+ f = find_add_channel_file(data, network, l, l->args[1], TRUE);
if(!f) return;
custom_subst(network, &s, fmt, l, l->args[1], FALSE, FALSE);
g_free(s);
}
-static void file_write_channel_query(struct network *network, const char *n, struct line *l)
+static void file_write_channel_query(struct log_custom_data *data, struct network *network, const char *n, const struct line *l)
{
char *s, *fmt;
char *nick;
struct network_nick *nn;
if (!l->origin) return;
- nick = line_get_nick(l);
- fmt = g_hash_table_lookup(fmts, n);
+ g_assert(n);
+
+ fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
if(!fmt) return;
/* check for the query first */
- f = find_add_channel_file(network, l, nick, FALSE);
+ nick = line_get_nick(l);
+ f = find_add_channel_file(data, network, l, nick, FALSE);
if(f) {
custom_subst(network, &s, fmt, l, nick, FALSE, FALSE);
}
nn = find_network_nick(network->state, nick);
+ g_free(nick);
g_assert(nn);
/* now, loop thru the users' channels */
for (gl = nn->channel_nicks; gl; gl = gl->next) {
struct channel_nick *cn = gl->data;
- f = find_add_channel_file(network, l, cn->channel->name, TRUE);
+ f = find_add_channel_file(data, network, l, cn->channel->name, TRUE);
if(!f) continue;
custom_subst(network, &s, fmt, l, cn->channel->name, FALSE, FALSE);
}
}
-static gboolean log_custom_data(struct network *network, struct line *l, enum data_direction dir, void *userdata)
+static gboolean log_custom_data(struct network *network, const struct line *l, enum data_direction dir, void *userdata)
{
- const char *nick = NULL;
- char *user = NULL;
+ struct log_custom_data *data = userdata;
+ char *nick = NULL;
if(!l->args || !l->args[0])return TRUE;
- if (l->origin) nick = line_get_nick(l);
- if(user){ *user = '\0';user++; }
+ if (l->origin)
+ nick = line_get_nick(l);
/* Loop thru possible values for %@ */
*/
if(dir == FROM_SERVER && !g_strcasecmp(l->args[0], "JOIN")) {
- file_write_target(network, "join", l);
+ file_write_target(data, network, "join", l);
} else if(dir == FROM_SERVER && !g_strcasecmp(l->args[0], "PART")) {
- file_write_channel_only(network, "part", l);
+ file_write_channel_only(data, network, "part", l);
} else if(!g_strcasecmp(l->args[0], "PRIVMSG")) {
if(l->args[2][0] == '\ 1') {
l->args[2][strlen(l->args[2])-1] = '\0';
if(!g_ascii_strncasecmp(l->args[2], "\ 1ACTION ", 8)) {
l->args[2]+=8;
- file_write_target(network, "action", l);
+ file_write_target(data, network, "action", l);
l->args[2]-=8;
}
l->args[2][strlen(l->args[2])] = '\ 1';
/* Ignore all other ctcp messages */
} else {
- file_write_target(network, "msg", l);
+ file_write_target(data, network, "msg", l);
}
} else if(!g_strcasecmp(l->args[0], "NOTICE")) {
- file_write_target(network, "notice", l);
+ file_write_target(data, network, "notice", l);
} else if(!g_strcasecmp(l->args[0], "MODE") && l->args[1] &&
is_channelname(l->args[1], network->state->info) && dir == FROM_SERVER) {
- file_write_target(network, "mode", l);
+ file_write_target(data, network, "mode", l);
} else if(!g_strcasecmp(l->args[0], "QUIT")) {
- file_write_channel_query(network, "quit", l);
+ file_write_channel_query(data, network, "quit", l);
} else if(!g_strcasecmp(l->args[0], "KICK") && l->args[1] && l->args[2] && dir == FROM_SERVER) {
if(!strchr(l->args[1], ',')) {
- file_write_channel_only(network, "kick", l);
+ file_write_channel_only(data, network, "kick", l);
} else {
char *channels = g_strdup(l->args[1]);
char *nicks = g_strdup(l->args[1]);
if(!n) cont = 0;
else *n = '\0';
- file_write_channel_only(network, "kick", l);
+ file_write_channel_only(data, network, "kick", l);
p = n+1;
_nick = strchr(_nick, ',');
g_free(nicks);
}
} else if(!g_strcasecmp(l->args[0], "TOPIC") && dir == FROM_SERVER && l->args[1]) {
- if(l->args[2]) file_write_channel_only(network, "topic", l);
- else file_write_channel_only(network, "notopic", l);
+ if(l->args[2]) file_write_channel_only(data, network, "topic", l);
+ else file_write_channel_only(data, network, "notopic", l);
} else if(!g_strcasecmp(l->args[0], "NICK") && dir == FROM_SERVER && l->args[1]) {
- file_write_channel_query(network, "nickchange", l);
+ file_write_channel_query(data, network, "nickchange", l);
}
+ g_free(nick);
+
return TRUE;
}
-static gboolean fini_plugin(struct plugin *p)
+static void free_file_info(void *_data)
{
- del_log_filter("log_custom");
- return TRUE;
+ struct file_info *data = _data;
+
+ fclose(data->file);
+ g_free(data);
}
-static gboolean load_config(struct plugin *p, xmlNodePtr node)
+static void load_config(struct global *global)
{
- xmlNodePtr cur;
-
- for (cur = node->children; cur; cur = cur->next)
- {
- if (cur->type != XML_ELEMENT_NODE) continue;
+ GKeyFile *kf = global->config->keyfile;
+ struct log_custom_data *data;
- if (!strcmp(cur->name, "logfilename")) {
- logfilename = xmlNodeGetContent(cur);
- } else {
- g_hash_table_insert(fmts, g_strdup(cur->name), xmlNodeGetContent(cur));
- }
+ if (!g_key_file_has_group(kf, "log-custom")) {
+ del_log_filter("log_custom");
+ return;
}
+ data = g_new0(struct log_custom_data, 1);
+
+ add_log_filter("log_custom", log_custom_data, data, 1000);
+
+ data->logfilename = g_key_file_get_string(kf, "log-custom", "logfilename", NULL);
+ data->kf = kf;
+}
+
+#define CLEANUP_THRESHOLD (60 * 60 * 24)
+
+static gboolean eval_remove(gpointer key, gpointer value, gpointer user_data)
+{
+ struct file_info *fi = value;
+
+ return (fi->last_used < time(NULL) - CLEANUP_THRESHOLD);
+}
+
+static gboolean cleanup(void *_data)
+{
+ g_hash_table_foreach_remove(files, eval_remove, NULL);
return TRUE;
}
-static gboolean init_plugin(struct plugin *p)
+static gboolean init_plugin(void)
{
- files = g_hash_table_new(g_str_hash, g_str_equal);
- fmts = g_hash_table_new(g_str_hash, g_str_equal);
- add_log_filter("log_custom", log_custom_data, NULL, 1000);
+ files = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, free_file_info);
+ g_timeout_add(60 * 60, cleanup, NULL);
+ register_load_config_notify(load_config);
return TRUE;
}
.name = "log_custom",
.version = 0,
.init = init_plugin,
- .fini = fini_plugin,
- .load_config = load_config,
};