~ctrlproxy/ctrlproxy/trunk

« back to all changes in this revision

Viewing changes to state.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
#include "internals.h"
 
21
 
 
22
enum mode_type { REMOVE = 0, ADD = 1};
 
23
 
 
24
char *mode2string(char modes[255])
 
25
{
 
26
        static char ret[255];
 
27
        unsigned char i;
 
28
        int pos = 0;
 
29
        for(i = 0; i < 255; i++) {
 
30
                if(modes[i]) { ret[pos] = (char)i; pos++; }
 
31
        }
 
32
        return ret;
 
33
}
 
34
 
 
35
static void free_names(struct channel *c) 
 
36
{
 
37
        struct nick *n;
 
38
        GList *g = c->nicks;
 
39
        while(g) {
 
40
                n = (struct nick *)g->data;
 
41
                free(n->name); free(n);
 
42
                g = g_list_next(g);
 
43
        }
 
44
        g_list_free(c->nicks);
 
45
        c->nicks = NULL;
 
46
}
 
47
 
 
48
static void free_channel(struct channel *c)
 
49
{
 
50
        free_names(c);
 
51
        if(c->topic)free(c->topic);
 
52
        c->topic = NULL;
 
53
}
 
54
 
 
55
struct channel *find_channel(struct network *st, char *name) 
 
56
{
 
57
        GList *cl = st->channels;
 
58
        while(cl) {
 
59
                struct channel *c = (struct channel *)cl->data;
 
60
                char *channel_name = xmlGetProp(c->xmlConf, "name");
 
61
                if(!strcasecmp(channel_name, name)) { xmlFree(channel_name); return c; }
 
62
                xmlFree(channel_name);
 
63
                cl = g_list_next(cl);
 
64
        }
 
65
        return NULL;
 
66
}
 
67
 
 
68
struct channel *find_add_channel(struct network *st, char *name) {
 
69
        struct channel *c = find_channel(st, name);
 
70
        if(c)return c;
 
71
        c = malloc(sizeof(struct channel));
 
72
        memset(c, 0, sizeof(struct channel));
 
73
        st->channels = g_list_append(st->channels, c);
 
74
 
 
75
        /* check if there is a XML node for this channel yet */
 
76
        c->xmlConf = xmlFindChildByName(st->xmlConf, name);
 
77
        
 
78
        if(!c->xmlConf) {
 
79
                c->xmlConf = xmlNewNode(NULL, "channel");
 
80
                xmlSetProp(c->xmlConf, "name", name);
 
81
                xmlAddChild(st->xmlConf, c->xmlConf);
 
82
        }
 
83
        
 
84
        return c;
 
85
}
 
86
 
 
87
struct nick *find_nick(struct channel *c, char *name) {
 
88
        GList *l = c->nicks;
 
89
        struct nick *n;
 
90
        char *realname = name;
 
91
        if(realname[0] == '@' || realname[0] == '+')realname++;
 
92
 
 
93
        while(l) {
 
94
                n = (struct nick *)l->data;
 
95
                if(!strcasecmp(n->name, realname))return n;
 
96
                l = g_list_next(l);
 
97
        }
 
98
 
 
99
        return NULL;
 
100
}
 
101
 
 
102
struct nick *find_add_nick(struct channel *c, char *name) {
 
103
        struct nick *n = find_nick(c, name);
 
104
        char *realname = name;
 
105
        if(n)return n;
 
106
        if(strlen(name) == 0)return NULL;
 
107
        n = malloc(sizeof(struct nick));
 
108
        memset(n, 0, sizeof(struct nick));
 
109
        if(realname[0] == '@' || realname[0] == '+'){
 
110
                n->mode = realname[0];
 
111
                realname++;
 
112
        }
 
113
        n->name = strdup(realname);
 
114
        c->nicks = g_list_append(c->nicks, n);
 
115
        return n;
 
116
}
 
117
 
 
118
 
 
119
static void handle_join(struct network *s, struct line *l) 
 
120
{
 
121
        struct channel *c;
 
122
        int cont = 1;
 
123
        char *own_nick;
 
124
        char *name = strdup(l->args[1]), *p, *n;
 
125
 
 
126
        p = name;
 
127
 
 
128
        while(cont) {
 
129
                n = strchr(p, ',');
 
130
 
 
131
                if(!n) cont = 0;
 
132
                else *n = '\0'; 
 
133
 
 
134
                /* Someone is joining a channel the user is on */
 
135
                if(l->direction == FROM_SERVER && line_get_nick(l)) {
 
136
                        c = find_add_channel(s, p);
 
137
                        find_add_nick(c, line_get_nick(l));
 
138
 
 
139
                        /* The user is joining a channel */
 
140
                        own_nick = xmlGetProp(s->xmlConf, "nick");
 
141
 
 
142
                        g_message("Joining channel %s", p);
 
143
                
 
144
                        if(!strcasecmp(line_get_nick(l), own_nick))
 
145
                                xmlSetProp(c->xmlConf, "autojoin", "1");
 
146
                        
 
147
                        xmlFree(own_nick);
 
148
                }
 
149
                p = n+1;
 
150
        }
 
151
        free(name);
 
152
}
 
153
 
 
154
 
 
155
static void handle_part(struct network *s, struct line *l) 
 
156
{
 
157
        struct channel *c;
 
158
        struct nick *n;
 
159
        int cont = 1;
 
160
        char *own_nick;
 
161
        char *name = strdup(l->args[1]), *p, *m;
 
162
 
 
163
        p = name;
 
164
        if(!line_get_nick(l))return;
 
165
 
 
166
        while(cont) {
 
167
                m = strchr(p, ',');
 
168
                if(!m) cont = 0;
 
169
                else *m = '\0';
 
170
                
 
171
                c = find_channel(s, p);
 
172
 
 
173
                /* The user is joining a channel */
 
174
                own_nick = xmlGetProp(s->xmlConf, "nick");
 
175
                
 
176
                if(!strcasecmp(line_get_nick(l), own_nick) && c) {
 
177
                        s->channels = g_list_remove(s->channels, c);
 
178
                        g_message("Leaving %s", p);
 
179
                        xmlSetProp(c->xmlConf, "autojoin", "0");
 
180
                        free_channel(c);
 
181
                        free(c);
 
182
                        c = NULL;
 
183
                        xmlFree(own_nick);
 
184
                        p = m + 1;
 
185
                        continue;
 
186
                }
 
187
                        
 
188
                xmlFree(own_nick);
 
189
 
 
190
                if(c){
 
191
                        n = find_nick(c, line_get_nick(l));
 
192
                        if(n) {
 
193
                                free(n->name);
 
194
                                c->nicks = g_list_remove(c->nicks, n);
 
195
                                free(n);
 
196
                        } else g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't remove nick %s from channel %s: nick not on channel\n", line_get_nick(l), p);
 
197
        
 
198
                        return;
 
199
                } 
 
200
        
 
201
                g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't part or let other nick part %s(unknown channel)\n", p);
 
202
                p = m + 1;
 
203
        }
 
204
        free(name);
 
205
}
 
206
 
 
207
static void handle_kick(struct network *s, struct line *l) {
 
208
        struct channel *c;
 
209
        struct nick *n;
 
210
        char *nicks = strdup(l->args[2]);
 
211
        char *channels = strdup(l->args[1]);
 
212
        char *curnick, *curchan, *nextchan, *nextnick;
 
213
        char cont = 1;
 
214
 
 
215
        curchan = channels; curnick = nicks;
 
216
        while(cont) {
 
217
                nextnick = strchr(curnick, ',');
 
218
                if(nextnick){ *nextnick = '\0'; nextnick++; }
 
219
                
 
220
                nextchan = strchr(curchan, ',');
 
221
                if(nextchan){ *nextchan = '\0'; nextchan++; }
 
222
 
 
223
                if((!nextnick && nextchan) || (nextnick && !nextchan)) {
 
224
                        g_warning("KICK command has unequal number of channels and nicks\n");
 
225
                }
 
226
 
 
227
                if(nextnick && nextchan) cont = 1;
 
228
                else cont = 0;
 
229
 
 
230
                c = find_channel(s, curchan);
 
231
                if(!c){
 
232
                        g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't kick nick %s from %s\n", curnick, curchan);
 
233
                        curchan = nextchan; curnick = nextnick;
 
234
                        continue;
 
235
                }
 
236
 
 
237
                n = find_nick(c, curnick);
 
238
                if(!n) {
 
239
                        g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't kick nick %s from channel %s: nick not on channel\n", curnick, curchan);
 
240
                        curchan = nextchan; curnick = nextnick;
 
241
                        continue;
 
242
                }
 
243
                free(n->name);
 
244
                c->nicks = g_list_remove(c->nicks, n);
 
245
                free(n);
 
246
 
 
247
                curchan = nextchan; curnick = nextnick;
 
248
        }
 
249
}
 
250
 
 
251
static void handle_topic(struct network *s, struct line *l) {
 
252
        struct channel *c = find_channel(s, l->args[1]);
 
253
        if(c->topic)free(c->topic);
 
254
        c->topic = strdup(l->args[2]);
 
255
}
 
256
 
 
257
static void handle_332(struct network *s, struct line *l) {
 
258
        struct channel *c = find_channel(s, l->args[2]);
 
259
        if(c->topic)free(c->topic);
 
260
        c->topic = strdup(l->args[3]);
 
261
}
 
262
 
 
263
static void handle_no_topic(struct network *s, struct line *l) {
 
264
        struct channel *c = find_channel(s, l->args[1]);
 
265
        if(c->topic)free(c->topic);
 
266
        c->topic = NULL;
 
267
}
 
268
 
 
269
static void handle_namreply(struct network *s, struct line *l) {
 
270
        char *names, *tmp, *t;
 
271
        struct channel *c = find_channel(s, l->args[3]);
 
272
        if(!c) {
 
273
                g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't add names to %s: channel not found\n", l->args[3]);
 
274
                return;
 
275
        }
 
276
        c->mode = l->args[2][0];
 
277
        if(c->namreply_started == 0) {
 
278
                free_names(c);
 
279
                c->namreply_started = 1;
 
280
        }
 
281
        tmp = names = strdup(l->args[4]);
 
282
        while((t = strchr(tmp, ' '))) {
 
283
                *t = '\0';
 
284
                find_add_nick(c, tmp);
 
285
                tmp = t+1;
 
286
        }
 
287
        find_add_nick(c, tmp);
 
288
        free(names);
 
289
}
 
290
 
 
291
static void handle_end_names(struct network *s, struct line *l) {
 
292
        struct channel *c = find_channel(s, l->args[2]);
 
293
        if(c)c->namreply_started = 0;
 
294
        else g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't end /NAMES command for %s: channel not found\n", l->args[2]);
 
295
}
 
296
 
 
297
static void handle_quit(struct network *s, struct line *l) {
 
298
        GList *g = s->channels;
 
299
        if(!line_get_nick(l))return;
 
300
        while(g) {
 
301
                struct channel *c = (struct channel *)g->data;
 
302
                struct nick *n = find_nick(c, line_get_nick(l));
 
303
                if(n) {
 
304
                        free(n->name);
 
305
                        c->nicks = g_list_remove(c->nicks, n);
 
306
                        free(n);
 
307
                }
 
308
                g = g_list_next(g);
 
309
        }
 
310
}
 
311
 
 
312
static void handle_mode(struct network *s, struct line *l) 
 
313
{
 
314
        /* Format:
 
315
         * MODE %|<nick>|<channel> [<mode> [<mode parameters>]] */
 
316
        char *nick;
 
317
        enum mode_type t = ADD;
 
318
        int i;
 
319
 
 
320
        /* We only care about channel modes and our own mode */
 
321
        nick = xmlGetProp(s->xmlConf, "nick");
 
322
        g_assert(nick);
 
323
        if(!strcmp(l->args[1], nick)) {
 
324
                for(i = 0; l->args[2][i]; i++) {
 
325
                        switch(l->args[2][i]) {
 
326
                                case '+': t = ADD;break;
 
327
                                case '-': t = REMOVE; break;
 
328
                                default:
 
329
                                          s->modes[(unsigned char)l->args[2][i]] = t;
 
330
                                          break;
 
331
                        }
 
332
                }
 
333
 
 
334
        } else if(l->args[1][0] == '#' || l->args[1][0] == '&') {
 
335
                struct channel *c = find_channel(s, l->args[1]);
 
336
                struct nick *n;
 
337
                int arg = 2;
 
338
                for(i = 0; l->args[2][i]; i++) {
 
339
                        switch(l->args[2][i]) {
 
340
                                case '+': t = ADD; break;
 
341
                                case '-': t = REMOVE; break;
 
342
                                case 'b': /* Don't do anything (like store, etc) with ban 
 
343
                                                         for now */
 
344
                                                  arg++;
 
345
                                                  break;
 
346
                                case 'o': 
 
347
                                        n = find_nick(c, l->args[++arg]);
 
348
                                        if(!n) {
 
349
                                                g_error("Can't set mode %c%c on nick %s on channel %s, because nick does not exist!", t == ADD?'+':'-', l->args[2][i], l->args[arg], l->args[1]);
 
350
                                                break;
 
351
                                        }
 
352
                                        n->mode = (t == ADD?'@':' ');
 
353
                                        break;
 
354
                                case 'v':
 
355
                                        n = find_nick(c, l->args[++arg]);
 
356
                                        if(!n) {
 
357
                                                g_error("Can't set mode %c%c on nick %s on channel %s, because nick does not exist!", t == ADD?'+':'-', l->args[2][i], l->args[arg], l->args[1]);
 
358
                                                break;
 
359
                                        }
 
360
                                        n->mode = (t == ADD?'+':' ');
 
361
                                        break;
 
362
                                case 'l':
 
363
                                        if(!l->args[++arg]) {
 
364
                                                g_error("Mode l requires argument, but no argument found");
 
365
                                                break;
 
366
                                        }
 
367
                                        c->limit = atol(l->args[arg]);
 
368
                                        s->modes['l'] = t;
 
369
                                        break;
 
370
                                case 'k':
 
371
                                        if(!l->args[++arg]) {
 
372
                                                g_error("Mode k requires argument, but no argument found");
 
373
                                                break;
 
374
                                        }
 
375
 
 
376
                                        if(t) xmlSetProp(c->xmlConf, "key", l->args[arg]);
 
377
                                        else xmlUnsetProp(c->xmlConf, "key");
 
378
                                        s->modes['k'] = t;
 
379
                                        break;
 
380
                                default:
 
381
                                          s->modes[(unsigned char)l->args[2][i]] = t;
 
382
                                          break;
 
383
                        }
 
384
                }
 
385
        } 
 
386
 
 
387
        xmlFree(nick);
 
388
}
 
389
 
 
390
static void handle_004(struct network *s, struct line *l)
 
391
{
 
392
        if(l->direction == TO_SERVER)return;
 
393
 
 
394
        s->supported_modes[0] = strdup(l->args[4]);
 
395
        s->supported_modes[1] = strdup(l->args[5]);
 
396
}
 
397
 
 
398
static void handle_005(struct network *s, struct line *l)
 
399
{
 
400
        int i, j = 0;
 
401
        if(l->direction == TO_SERVER)return;
 
402
 
 
403
        s->features = malloc(sizeof(char *) * l->argc);
 
404
 
 
405
        for(i = 3; i < l->argc-1; i++) {
 
406
                s->features[j] = strdup(l->args[i]);
 
407
                j++;
 
408
        }
 
409
        
 
410
        s->features[j] = NULL;
 
411
}
 
412
 
 
413
static void handle_nick(struct network *s, struct line *l)
 
414
{
 
415
        char *own_nick;
 
416
        GList *g = s->channels;
 
417
 
 
418
        /* Server confirms messages client sends, so let's only handle those */
 
419
        if(l->direction != FROM_SERVER || !l->args[1] || !line_get_nick(l)) return;
 
420
        while(g) {
 
421
                struct channel *c = (struct channel *)g->data;
 
422
                struct nick *n = find_nick(c, line_get_nick(l));
 
423
                if(n) {
 
424
                        free(n->name);
 
425
                        n->name = strdup(l->args[1]);
 
426
                }
 
427
                g = g_list_next(g);
 
428
        }
 
429
        
 
430
        own_nick = xmlGetProp(s->xmlConf, "nick");
 
431
        
 
432
        if(!strcasecmp(line_get_nick(l), own_nick)) {
 
433
                xmlSetProp(s->xmlConf, "nick", l->args[1]);                             
 
434
        }
 
435
        
 
436
        xmlFree(own_nick);
 
437
}
 
438
 
 
439
void state_reconnect(struct network *s) 
 
440
{
 
441
        GList *l = s->channels;
 
442
 
 
443
        /* Remove list of channels */
 
444
        while(l) {
 
445
                struct channel *ch = (struct channel *)l->data;
 
446
                free_channel(ch);
 
447
                free(ch);
 
448
                l = g_list_next(l);
 
449
        }
 
450
 
 
451
        g_list_free(s->channels);
 
452
 
 
453
        s->channels = NULL;
 
454
}
 
455
 
 
456
static struct irc_command {
 
457
        char *command;
 
458
        int min_args;
 
459
        void (*handler) (struct network *s, struct line *l);
 
460
} irc_commands[] = {
 
461
        { "JOIN", 1, handle_join },
 
462
        { "PART", 1, handle_part },
 
463
        { "KICK", 2, handle_kick },
 
464
        { "QUIT", 0, handle_quit },
 
465
        { "TOPIC", 2, handle_topic },
 
466
        { "004", 5, handle_004 },
 
467
        { "005", 3, handle_005 },
 
468
        { "332",  3, handle_332 },
 
469
        { "331", 1, handle_no_topic },
 
470
        { "353", 4, handle_namreply },
 
471
        { "366", 2, handle_end_names },
 
472
        { "NICK", 1, handle_nick },
 
473
        { "MODE", 2, handle_mode },
 
474
        { NULL }
 
475
};
 
476
 
 
477
void state_handle_data(struct network *s, struct line *l)
 
478
{
 
479
        int i,j;
 
480
        
 
481
        if(!s || !l->args || !l->args[0])return;
 
482
 
 
483
        for(i = 0; irc_commands[i].command; i++) {
 
484
                if(!strcasecmp(irc_commands[i].command, l->args[0])) {
 
485
                        for(j = 0; j <= irc_commands[i].min_args; j++) {
 
486
                                if(!l->args[j])return;
 
487
                        }
 
488
                        irc_commands[i].handler(s,l);
 
489
                        return;
 
490
                }
 
491
        }
 
492
        return;
 
493
}
 
494
 
 
495
 
 
496
GSList *gen_replication(struct network *s)
 
497
{
 
498
        struct nick *n;
 
499
        GList *cl, *nl;
 
500
        struct channel *c;
 
501
        GSList *ret = NULL;
 
502
        char *key;
 
503
        char *nick, *server_name, *channel_name;
 
504
        if(!s) return NULL;
 
505
        cl = s->channels,
 
506
        server_name = xmlGetProp(s->xmlConf, "name");
 
507
        nick = xmlGetProp(s->xmlConf, "nick");
 
508
 
 
509
        while(cl) {
 
510
                c = (struct channel *)cl->data;
 
511
                channel_name = xmlGetProp(c->xmlConf, "name");
 
512
                key = xmlGetProp(c->xmlConf, "key");
 
513
                if(channel_name[0] != '#' && channel_name[0] != '&') {
 
514
                        cl = g_list_next(cl);
 
515
                        xmlFree(channel_name);
 
516
                        continue;
 
517
                }
 
518
                ret = g_slist_append(ret, irc_parse_linef(":%s JOIN %s%s%s\r\n", nick, channel_name, key?" ":"", key?key:""));
 
519
 
 
520
                xmlFree(key);
 
521
 
 
522
                if(c->topic) {
 
523
                        ret = g_slist_append(ret, irc_parse_linef(":%s 332 %s %s :%s\r\n", server_name, nick, channel_name, c->topic));
 
524
                } else {
 
525
                        ret = g_slist_append(ret, irc_parse_linef(":%s 331 %s %s :No topic set\r\n", server_name, nick, channel_name));
 
526
                }
 
527
 
 
528
                nl = c->nicks;
 
529
                while(nl) {
 
530
                        n = (struct nick *)nl->data;
 
531
                        if(n->mode && n->mode != ' ') { ret = g_slist_append(ret, irc_parse_linef(":%s 353 %s %c %s :%c%s\r\n", server_name, nick, c->mode, channel_name, n->mode, n->name)); }
 
532
                        else { ret = g_slist_append(ret, irc_parse_linef(":%s 353 %s %c %s :%s\r\n", server_name, nick, c->mode, channel_name, n->name)); }
 
533
                        nl = g_list_next(nl);
 
534
                }
 
535
                ret = g_slist_append(ret, irc_parse_linef(":%s 366 %s %s :End of /names list\r\n", server_name, nick, channel_name));
 
536
                c->introduced = 3;
 
537
                cl = g_list_next(cl);
 
538
                xmlFree(channel_name);
 
539
        }
 
540
 
 
541
        if(strlen(mode2string(s->modes)))
 
542
                ret = g_slist_append(ret, irc_parse_linef(":%s MODE %s +%s\r\n", server_name, nick, mode2string(s->modes)));
 
543
 
 
544
        xmlFree(server_name);
 
545
        xmlFree(nick);
 
546
        
 
547
        return ret;
 
548
}