2
ctrlproxy: A modular IRC proxy
3
(c) 2002-2003 Jelmer Vernooij <jelmer@nl.linux.org>
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
#include "ctrlproxy.h"
30
#include <sys/types.h>
34
#define G_LOG_DOMAIN "log_custom"
36
static xmlNodePtr xmlConf = NULL;
38
/* Translation table */
44
char *(*callback) (struct line *l);
47
char *get_hours(struct line *l) {
49
time_t ti = time(NULL);
50
struct tm *t = localtime(&ti);
51
asprintf(&ret, "%d", t->tm_hour);
55
char *get_minutes(struct line *l) {
57
time_t ti = time(NULL);
58
struct tm *t = localtime(&ti);
59
asprintf(&ret, "%d", t->tm_min);
63
char *get_seconds(struct line *l) {
65
time_t ti = time(NULL);
66
struct tm *t = localtime(&ti);
67
asprintf(&ret, "%d", t->tm_sec);
71
char *get_nick(struct line *l) { return strdup(line_get_nick(l)); }
72
char *get_network(struct line *l)
73
{ return xmlGetProp(l->network->xmlConf, "name"); }
74
char *get_server(struct line *l)
75
{ return xmlGetProp(l->network->current_server, "name"); }
77
char *get_percent(struct line *l) { return strdup("%"); }
79
static char *identifier = NULL;
81
char *get_identifier(struct line *l) { return strdup(identifier); }
83
struct log_mapping mappings[] = {
84
{NULL, '@', -1, get_identifier },
85
{NULL, 'h', -1, get_hours },
86
{NULL, 'M', -1, get_minutes },
87
{NULL, 's', -1, get_seconds },
88
{NULL, 'n', -1, get_nick },
89
{NULL, 'N', -1, get_network },
90
{NULL, 'S', -1, get_server },
91
{NULL, '%', -1, get_percent },
92
{"JOIN", 'c', 1, NULL },
93
{"PART", 'c', 1, NULL },
94
{"PART", 'm', 2, NULL },
95
{"KICK", 'c', 1, NULL },
96
{"KICK", 't', 2, NULL },
97
{"KICK", 'r', 3, NULL },
98
{"QUIT", 'm', 1, NULL },
99
{"NOTICE", 't', 1, NULL },
100
{"NOTICE", 'm', 2, NULL },
101
{"PRIVMSG", 't', 1, NULL },
102
{"PRIVMSG", 'm', 2, NULL },
103
{"TOPIC", 'c', 1, NULL },
104
{"TOPIC", 't', 2, NULL },
105
{"MODE", 't', 1, NULL },
106
{"MODE", 'p', 2, NULL },
107
{"NICK", 'r', 1, NULL },
108
{NULL, '0', 0, NULL },
109
{NULL, '1', 1, NULL },
110
{NULL, '2', 2, NULL },
111
{NULL, '3', 3, NULL },
112
{NULL, '4', 4, NULL },
113
{NULL, '5', 5, NULL },
114
{NULL, '6', 6, NULL },
115
{NULL, '7', 7, NULL },
116
{NULL, '8', 8, NULL },
117
{NULL, '9', 9, NULL },
121
static char *find_mapping(struct line *l, char c)
124
for(i = 0; mappings[i].subst; i++) {
125
if(mappings[i].command &&
126
strcmp(mappings[i].command, l->args[0])) continue;
127
if(mappings[i].subst != c)continue;
128
if(mappings[i].index == -1) return mappings[i].callback(l);
129
if(mappings[i].index < l->argc) return strdup(l->args[mappings[i].index]);
134
void custom_subst(char **_new, char *fmt, struct line *l, char *_identifier)
136
char *subst[MAX_SUBST];
138
size_t len, curpos = 0;
141
identifier = _identifier;
144
memset(subst, 0, sizeof(char *) * MAX_SUBST);
145
for(i = 0; i < strlen(fmt); i++) {
147
subst[(int)fmt[i+1]] = find_mapping(l, fmt[i+1]);
148
len += strlen(subst[(int)fmt[i+1]]);
152
len++; /* newline! */
155
for(i = 0; i < strlen(fmt); i++) {
158
strncat(new, subst[(int)fmt[i+1]], len);
159
curpos+=strlen(subst[(int)fmt[i+1]]);
162
new[curpos] = fmt[i];
167
for(i = 0; i < MAX_SUBST; i++) { if(subst[i])free(subst[i]); }
176
* %n -> initiating nick
177
* %u -> initiating user
187
-- KICK: %t (target nick), %r (reason)
189
-- NOTICE/PRIVMSG: %t (target nick/channel), %m
190
-- MODE: %p(mode change), %c, %t (target nick)
195
static GHashTable *files = NULL;
197
static FILE *find_channel_file(struct line *l, char *identifier) {
199
xmlNodePtr cur = xmlFindChildByElementName(xmlConf, "logfilename");
202
if(!cur) return NULL;
203
logfilename = xmlNodeGetContent(cur);
204
if(!logfilename)return NULL;
205
custom_subst(&n, logfilename, l, identifier);
207
f = g_hash_table_lookup(files, n);
212
static FILE *find_add_channel_file(struct line *l, char *identifier) {
213
char *n = NULL, *dn, *p;
215
xmlNodePtr cur = xmlFindChildByElementName(xmlConf, "logfilename");
217
if(!cur) return NULL;
219
logfilename = xmlNodeGetContent(cur);
220
if(!logfilename) return NULL;
221
custom_subst(&n, logfilename, l, identifier);
222
f = g_hash_table_lookup(files, n);
226
/* Only include directory-part */
227
p = strrchr(dn, '/');
230
/* Check if directory needs to be created */
231
if(access(dn, F_OK) != 0 && mkdir(dn, 0700) == -1) {
232
g_warning("Couldn't create directory %s for logging!", dn);
233
xmlFree(logfilename);
240
/* Then open the correct filename */
241
custom_subst(&n, logfilename, l, identifier);
244
g_warning("Couldn't open file %s for logging!", n);
245
xmlFree(logfilename);
249
g_hash_table_insert(files, n, f);
252
xmlFree(logfilename);
256
static void file_write_target(const char *n, struct line *l)
261
char *own_nick = xmlGetProp(l->network->xmlConf, "nick");
263
cur = xmlFindChildByElementName(xmlConf, n);
266
fmt = xmlNodeGetContent(cur);
270
if(!strcasecmp(own_nick, l->args[1])) {
271
if(line_get_nick(l)) { t = strdup(line_get_nick(l)); }
272
else { t = strdup("_messages_"); }
274
t = strdup(l->args[1]);
278
f = find_add_channel_file(l, t);
279
if(!f) { free(t); return; }
281
custom_subst(&s, fmt, l, t);
292
static void file_write_channel_only(const char *n, struct line *l)
298
cur = xmlFindChildByElementName(xmlConf, n);
301
fmt = xmlNodeGetContent(cur);
304
f = find_add_channel_file(l, l->args[1]);
307
custom_subst(&s, fmt, l, l->args[1]);
310
fputs(s, f); fputc('\n', f);
316
static void file_write_channel_query(const char *n, struct line *l)
324
nick = line_get_nick(l);
327
cur = xmlFindChildByElementName(xmlConf, n);
329
fmt = xmlNodeGetContent(cur);
332
/* check for the query first */
333
f = find_channel_file(l, nick);
336
custom_subst(&s, fmt, l, nick);
337
fputs(s, f); fputc('\n', f);
342
/* now, loop thru the channels and check if the user is there */
343
gl = l->network->channels;
345
struct channel *c = (struct channel *)gl->data;
346
if(find_nick(c, nick)) {
347
char *channame = xmlGetProp(c->xmlConf, "name");
348
f = find_add_channel_file(l, channame);
350
custom_subst(&s, fmt, l, channame);
351
fputs(s, f); fputc('\n', f);
363
gboolean log_custom_data(struct line *l)
368
if(!l->args || !l->args[0])return TRUE;
369
if(l->origin)nick = strdup(l->origin);
370
if(nick)user = strchr(nick, '!');
371
if(user){ *user = '\0';user++; }
372
if(!nick && xmlHasProp(l->network->xmlConf, "nick"))nick = xmlGetProp(l->network->xmlConf, "nick");
374
printf("Writing logs for line of %s\n", l->args[0]);
376
/* Loop thru possible values for %@ */
378
/* There are a few possibilities:
379
* - log to line_get_nick(l) or l->args[1] file depending on which
380
* was the current user (PRIVMSG, NOTICE, ACTION, MODE) (target)
381
* - log to all channels line_get_nick(l) was on, and to query, if applicable (NICK, QUIT) (channel_query)
382
* - log to channel only (KICK, PART, JOIN, TOPIC) (channel_only)
385
if(l->direction == FROM_SERVER && !strcasecmp(l->args[0], "JOIN")) {
386
file_write_target("join", l);
387
} else if(l->direction == FROM_SERVER && !strcasecmp(l->args[0], "PART")) {
388
file_write_channel_only("part", l);
389
} else if(!strcasecmp(l->args[0], "PRIVMSG")) {
390
if(l->args[2][0] == '') {
391
l->args[2][strlen(l->args[2])-1] = '\0';
392
if(!strncasecmp(l->args[2], "ACTION ", 8)) {
394
file_write_target("action", l);
397
l->args[2][strlen(l->args[2])] = '';
398
/* Ignore all other ctcp messages */
400
file_write_target("msg", l);
402
} else if(!strcasecmp(l->args[0], "NOTICE")) {
403
file_write_target("notice", l);
404
} else if(!strcasecmp(l->args[0], "MODE") && l->args[1] && (l->args[1][0] == '#' || l->args[1][0] == '&') && l->direction == FROM_SERVER) {
405
file_write_target("mode", l);
406
} else if(!strcasecmp(l->args[0], "QUIT")) {
407
file_write_channel_query("quit", l);
408
} else if(!strcasecmp(l->args[0], "KICK") && l->args[1] && l->args[2] && l->direction == FROM_SERVER) {
409
if(!strchr(l->args[1], ',')) {
410
file_write_channel_only("kick", l);
412
char *channels = strdup(l->args[1]);
413
char *nicks = strdup(l->args[1]);
414
char *p,*n; char cont = 1;
425
file_write_channel_only("kick", l);
428
_nick = strchr(_nick, ',');
436
} else if(!strcasecmp(l->args[0], "TOPIC") && l->direction == FROM_SERVER && l->args[1]) {
437
if(l->args[2]) file_write_channel_only("topic", l);
438
else file_write_channel_only("notopic", l);
439
} else if(!strcasecmp(l->args[0], "NICK") && l->direction == FROM_SERVER && l->args[1]) {
440
file_write_channel_query("nickchange", l);
450
gboolean fini_plugin(struct plugin *p)
452
del_filter(log_custom_data);
456
gboolean init_plugin(struct plugin *p)
458
xmlConf = p->xmlConf;
459
g_assert(p->xmlConf);
460
if(!xmlFindChildByElementName(xmlConf, "logfilename")) {
461
g_warning("No <logfilename> tag for log_custom module");
464
files = g_hash_table_new(g_str_hash, g_str_equal);
465
add_filter("log_custom", log_custom_data);