~ctrlproxy/ctrlproxy/trunk

« back to all changes in this revision

Viewing changes to mods/log_custom.c

  • Committer: jelmer
  • Date: 2003-10-18 22:02:02 UTC
  • Revision ID: jelmer@samba.org-20031018220202-6801a76318fb4d13
Update

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
        ctrlproxy: A modular IRC proxy
 
3
        (c) 2002-2003 Jelmer Vernooij <jelmer@nl.linux.org>
 
4
 
 
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.
 
9
 
 
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.
 
14
 
 
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.
 
18
*/
 
19
 
 
20
#define _GNU_SOURCE
 
21
#include "ctrlproxy.h"
 
22
#include <stdio.h>
 
23
#include <stdarg.h>
 
24
#include <string.h>
 
25
#include <malloc.h>
 
26
#include <stdio.h>
 
27
#include <time.h>
 
28
#include <glib.h>
 
29
#include <sys/stat.h>
 
30
#include <sys/types.h>
 
31
 
 
32
#define MAX_SUBST 256
 
33
#undef G_LOG_DOMAIN
 
34
#define G_LOG_DOMAIN "log_custom"
 
35
 
 
36
static xmlNodePtr xmlConf = NULL;
 
37
 
 
38
/* Translation table */
 
39
struct log_mapping {
 
40
        char *command;
 
41
        char subst;
 
42
        int index;
 
43
        /* If index is -1 */
 
44
        char *(*callback) (struct line *l);
 
45
};
 
46
 
 
47
char *get_hours(struct line *l) { 
 
48
        char *ret;
 
49
        time_t ti = time(NULL);
 
50
        struct tm *t = localtime(&ti);
 
51
        asprintf(&ret, "%d", t->tm_hour);
 
52
        return ret;
 
53
}
 
54
 
 
55
char *get_minutes(struct line *l) { 
 
56
        char *ret;
 
57
        time_t ti = time(NULL);
 
58
        struct tm *t = localtime(&ti);
 
59
        asprintf(&ret, "%d", t->tm_min);
 
60
        return ret;
 
61
}
 
62
 
 
63
char *get_seconds(struct line *l) { 
 
64
        char *ret;
 
65
        time_t ti = time(NULL);
 
66
        struct tm *t = localtime(&ti);
 
67
        asprintf(&ret, "%d", t->tm_sec);
 
68
        return ret;
 
69
}
 
70
 
 
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"); }
 
76
 
 
77
char *get_percent(struct line *l) { return strdup("%"); }
 
78
 
 
79
static char *identifier = NULL;
 
80
 
 
81
char *get_identifier(struct line *l) { return strdup(identifier); }
 
82
 
 
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 },
 
118
        { NULL }
 
119
};
 
120
 
 
121
static char *find_mapping(struct line *l, char c)
 
122
{
 
123
        int i;
 
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]);
 
130
        }
 
131
        return strdup("");
 
132
}
 
133
 
 
134
void custom_subst(char **_new, char *fmt, struct line *l, char *_identifier)
 
135
{
 
136
        char *subst[MAX_SUBST];
 
137
        char *new;
 
138
        size_t len, curpos = 0;
 
139
        int i;
 
140
 
 
141
        identifier = _identifier;
 
142
 
 
143
        len = strlen(fmt);
 
144
        memset(subst, 0, sizeof(char *) * MAX_SUBST);
 
145
        for(i = 0; i < strlen(fmt); i++) {
 
146
                if(fmt[i] == '%') {
 
147
                        subst[(int)fmt[i+1]] = find_mapping(l, fmt[i+1]);       
 
148
                        len += strlen(subst[(int)fmt[i+1]]);
 
149
                }
 
150
        }
 
151
 
 
152
        len++; /* newline! */
 
153
 
 
154
        new = malloc(len);
 
155
        for(i = 0; i < strlen(fmt); i++) {
 
156
                if(fmt[i] == '%') {
 
157
                        new[curpos] = '\0';
 
158
                        strncat(new, subst[(int)fmt[i+1]], len);
 
159
                        curpos+=strlen(subst[(int)fmt[i+1]]);
 
160
                        i++;
 
161
                } else {
 
162
                        new[curpos] = fmt[i];
 
163
                        curpos++;
 
164
                }
 
165
        }
 
166
 
 
167
        for(i = 0; i < MAX_SUBST; i++) { if(subst[i])free(subst[i]); }
 
168
        *_new = new;
 
169
}
 
170
 
 
171
/* Syntax:
 
172
Always:
 
173
 * %h -> hours
 
174
 * %m -> minutes
 
175
 * %s -> seconds
 
176
 * %n -> initiating nick
 
177
 * %u -> initiating user
 
178
 * %N -> network name
 
179
 * %S -> server name
 
180
 * %% -> percent sign
 
181
If appropriate:
 
182
 * %c -> channel name
 
183
 * %m -> message
 
184
 
 
185
 -- JOIN: %c
 
186
 -- PART: %c, %m
 
187
 -- KICK: %t (target nick), %r (reason)
 
188
 -- QUIT: %m
 
189
 -- NOTICE/PRIVMSG: %t (target nick/channel), %m
 
190
 -- MODE: %p(mode change), %c, %t (target nick)
 
191
 -- TOPIC: %t
 
192
 -- NICK: %r
 
193
 */
 
194
 
 
195
static GHashTable *files = NULL;
 
196
 
 
197
static FILE *find_channel_file(struct line *l, char *identifier) {
 
198
        char *n = NULL;
 
199
        xmlNodePtr cur = xmlFindChildByElementName(xmlConf, "logfilename");
 
200
        FILE *f;
 
201
        char *logfilename;
 
202
        if(!cur) return NULL;
 
203
        logfilename = xmlNodeGetContent(cur);
 
204
        if(!logfilename)return NULL;
 
205
        custom_subst(&n, logfilename, l, identifier);
 
206
        free(logfilename);
 
207
        f = g_hash_table_lookup(files, n);
 
208
        free(n);
 
209
        return f;
 
210
}
 
211
 
 
212
static FILE *find_add_channel_file(struct line *l, char *identifier) {
 
213
        char *n = NULL, *dn, *p;
 
214
        FILE *f;
 
215
        xmlNodePtr cur = xmlFindChildByElementName(xmlConf, "logfilename");
 
216
        char *logfilename;
 
217
        if(!cur) return NULL;
 
218
 
 
219
        logfilename = xmlNodeGetContent(cur);
 
220
        if(!logfilename) return NULL;
 
221
        custom_subst(&n, logfilename, l, identifier);
 
222
        f = g_hash_table_lookup(files, n);
 
223
        if(!f) {
 
224
                dn = strdup(n);
 
225
                
 
226
                /* Only include directory-part */
 
227
                p = strrchr(dn, '/');
 
228
                if(p) *p = '\0';
 
229
 
 
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);
 
234
                        free(dn);
 
235
                        free(n);
 
236
                        return NULL;
 
237
                }
 
238
                free(dn);
 
239
                
 
240
                /* Then open the correct filename */
 
241
                custom_subst(&n, logfilename, l, identifier);
 
242
                f = fopen(n, "a+");
 
243
                if(!f) {
 
244
                        g_warning("Couldn't open file %s for logging!", n);
 
245
                        xmlFree(logfilename);
 
246
                        free(n);
 
247
                        return NULL;
 
248
                }
 
249
                g_hash_table_insert(files, n, f);
 
250
                free(n);
 
251
        } else free(n);
 
252
        xmlFree(logfilename);
 
253
        return f;
 
254
}
 
255
 
 
256
static void file_write_target(const char *n, struct line *l) 
 
257
{
 
258
        char *t, *s, *fmt;
 
259
        xmlNodePtr cur;
 
260
        FILE *f;
 
261
        char *own_nick = xmlGetProp(l->network->xmlConf, "nick");
 
262
 
 
263
        cur = xmlFindChildByElementName(xmlConf, n);
 
264
        if(!cur) return;
 
265
 
 
266
        fmt = xmlNodeGetContent(cur);
 
267
        if(!fmt) return;
 
268
 
 
269
 
 
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_"); }
 
273
        } else {
 
274
                t = strdup(l->args[1]);
 
275
        }
 
276
        xmlFree(own_nick);
 
277
 
 
278
        f = find_add_channel_file(l, t);
 
279
        if(!f) { free(t); return; }
 
280
        
 
281
        custom_subst(&s, fmt, l, t);
 
282
        free(t);
 
283
        xmlFree(fmt);
 
284
 
 
285
    fputs(s, f);
 
286
        fputc('\n', f);
 
287
        fflush(f);
 
288
 
 
289
        free(s);
 
290
}
 
291
 
 
292
static void file_write_channel_only(const char *n, struct line *l)
 
293
{
 
294
        char *s, *fmt;
 
295
        xmlNodePtr cur;
 
296
        FILE *f;
 
297
 
 
298
        cur = xmlFindChildByElementName(xmlConf, n);
 
299
        if(!cur) return;
 
300
 
 
301
        fmt = xmlNodeGetContent(cur);
 
302
        if(!fmt)return;
 
303
 
 
304
        f = find_add_channel_file(l, l->args[1]);
 
305
        if(!f) return; 
 
306
 
 
307
        custom_subst(&s, fmt, l, l->args[1]);
 
308
        xmlFree(fmt);
 
309
 
 
310
        fputs(s, f); fputc('\n', f);
 
311
        fflush(f);
 
312
 
 
313
        free(s);
 
314
}
 
315
 
 
316
static void file_write_channel_query(const char *n, struct line *l)
 
317
{
 
318
        char *s, *fmt;
 
319
        xmlNodePtr cur;
 
320
        char *nick;
 
321
        FILE *f;
 
322
        GList *gl;
 
323
 
 
324
        nick = line_get_nick(l);
 
325
        if(!nick)return;
 
326
 
 
327
        cur = xmlFindChildByElementName(xmlConf, n);
 
328
        if(!cur)return;
 
329
        fmt = xmlNodeGetContent(cur);
 
330
        if(!fmt)return;
 
331
 
 
332
        /* check for the query first */
 
333
        f = find_channel_file(l, nick);
 
334
 
 
335
        if(f) {
 
336
                custom_subst(&s, fmt, l, nick);
 
337
                fputs(s, f); fputc('\n', f);
 
338
                fflush(f);
 
339
                free(s);
 
340
        }
 
341
        
 
342
        /* now, loop thru the channels and check if the user is there */
 
343
        gl = l->network->channels;
 
344
        while(gl) {
 
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);
 
349
                        if(f) {
 
350
                                custom_subst(&s, fmt, l, channame);
 
351
                                fputs(s, f); fputc('\n', f);
 
352
                                fflush(f);
 
353
                                free(s);
 
354
                        }
 
355
                        xmlFree(channame);
 
356
                }
 
357
                gl = gl->next;
 
358
        }
 
359
        
 
360
        xmlFree(fmt);
 
361
}
 
362
 
 
363
gboolean log_custom_data(struct line *l)
 
364
{
 
365
        char *nick = NULL;
 
366
        char *user = NULL;
 
367
        FILE *f = NULL;
 
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");
 
373
 
 
374
        printf("Writing logs for line of %s\n", l->args[0]);
 
375
        
 
376
        /* Loop thru possible values for %@ */
 
377
 
 
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)
 
383
         */
 
384
 
 
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)) { 
 
393
                                l->args[2]+=8;
 
394
                                file_write_target("action", l);
 
395
                                l->args[2]-=8;
 
396
                        }
 
397
                        l->args[2][strlen(l->args[2])] = '';
 
398
                        /* Ignore all other ctcp messages */
 
399
                } else {
 
400
                        file_write_target("msg", l);
 
401
                }
 
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);
 
411
                } else { 
 
412
                        char *channels = strdup(l->args[1]);
 
413
                        char *nicks = strdup(l->args[1]);
 
414
                        char *p,*n; char cont = 1;
 
415
                        char *_nick;
 
416
 
 
417
                        p = channels;
 
418
                        _nick = nicks;
 
419
                        while(cont) {
 
420
                                n = strchr(p, ',');
 
421
 
 
422
                                if(!n) cont = 0;
 
423
                                else *n = '\0';
 
424
 
 
425
                                file_write_channel_only("kick", l);
 
426
 
 
427
                                p = n+1;
 
428
                                _nick = strchr(_nick, ',');
 
429
                                if(!_nick)break;
 
430
                                _nick++;
 
431
                        }
 
432
                        
 
433
                        free(channels);
 
434
                        free(nicks);
 
435
                }
 
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);
 
441
        }
 
442
 
 
443
        if(f) fflush(f);
 
444
 
 
445
        if(nick)free(nick);
 
446
 
 
447
        return TRUE;
 
448
}
 
449
 
 
450
gboolean fini_plugin(struct plugin *p)
 
451
{
 
452
        del_filter(log_custom_data);
 
453
        return TRUE;
 
454
}
 
455
 
 
456
gboolean init_plugin(struct plugin *p)
 
457
{
 
458
        xmlConf = p->xmlConf;
 
459
        g_assert(p->xmlConf);
 
460
        if(!xmlFindChildByElementName(xmlConf, "logfilename")) {
 
461
                g_warning("No <logfilename> tag for log_custom module");
 
462
                return FALSE;
 
463
        }
 
464
        files = g_hash_table_new(g_str_hash, g_str_equal);
 
465
        add_filter("log_custom", log_custom_data);
 
466
        return TRUE;
 
467
}