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.
20
#include "internals.h"
22
enum mode_type { REMOVE = 0, ADD = 1};
24
char *mode2string(char modes[255])
29
for(i = 0; i < 255; i++) {
30
if(modes[i]) { ret[pos] = (char)i; pos++; }
35
static void free_names(struct channel *c)
40
n = (struct nick *)g->data;
41
free(n->name); free(n);
44
g_list_free(c->nicks);
48
static void free_channel(struct channel *c)
51
if(c->topic)free(c->topic);
55
struct channel *find_channel(struct network *st, char *name)
57
GList *cl = st->channels;
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);
68
struct channel *find_add_channel(struct network *st, char *name) {
69
struct channel *c = find_channel(st, name);
71
c = malloc(sizeof(struct channel));
72
memset(c, 0, sizeof(struct channel));
73
st->channels = g_list_append(st->channels, c);
75
/* check if there is a XML node for this channel yet */
76
c->xmlConf = xmlFindChildByName(st->xmlConf, name);
79
c->xmlConf = xmlNewNode(NULL, "channel");
80
xmlSetProp(c->xmlConf, "name", name);
81
xmlAddChild(st->xmlConf, c->xmlConf);
87
struct nick *find_nick(struct channel *c, char *name) {
90
char *realname = name;
91
if(realname[0] == '@' || realname[0] == '+')realname++;
94
n = (struct nick *)l->data;
95
if(!strcasecmp(n->name, realname))return n;
102
struct nick *find_add_nick(struct channel *c, char *name) {
103
struct nick *n = find_nick(c, name);
104
char *realname = name;
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];
113
n->name = strdup(realname);
114
c->nicks = g_list_append(c->nicks, n);
119
static void handle_join(struct network *s, struct line *l)
124
char *name = strdup(l->args[1]), *p, *n;
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));
139
/* The user is joining a channel */
140
own_nick = xmlGetProp(s->xmlConf, "nick");
142
g_message("Joining channel %s", p);
144
if(!strcasecmp(line_get_nick(l), own_nick))
145
xmlSetProp(c->xmlConf, "autojoin", "1");
155
static void handle_part(struct network *s, struct line *l)
161
char *name = strdup(l->args[1]), *p, *m;
164
if(!line_get_nick(l))return;
171
c = find_channel(s, p);
173
/* The user is joining a channel */
174
own_nick = xmlGetProp(s->xmlConf, "nick");
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");
191
n = find_nick(c, line_get_nick(l));
194
c->nicks = g_list_remove(c->nicks, 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);
201
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't part or let other nick part %s(unknown channel)\n", p);
207
static void handle_kick(struct network *s, struct line *l) {
210
char *nicks = strdup(l->args[2]);
211
char *channels = strdup(l->args[1]);
212
char *curnick, *curchan, *nextchan, *nextnick;
215
curchan = channels; curnick = nicks;
217
nextnick = strchr(curnick, ',');
218
if(nextnick){ *nextnick = '\0'; nextnick++; }
220
nextchan = strchr(curchan, ',');
221
if(nextchan){ *nextchan = '\0'; nextchan++; }
223
if((!nextnick && nextchan) || (nextnick && !nextchan)) {
224
g_warning("KICK command has unequal number of channels and nicks\n");
227
if(nextnick && nextchan) cont = 1;
230
c = find_channel(s, curchan);
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;
237
n = find_nick(c, curnick);
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;
244
c->nicks = g_list_remove(c->nicks, n);
247
curchan = nextchan; curnick = nextnick;
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]);
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]);
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);
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]);
273
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Can't add names to %s: channel not found\n", l->args[3]);
276
c->mode = l->args[2][0];
277
if(c->namreply_started == 0) {
279
c->namreply_started = 1;
281
tmp = names = strdup(l->args[4]);
282
while((t = strchr(tmp, ' '))) {
284
find_add_nick(c, tmp);
287
find_add_nick(c, tmp);
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]);
297
static void handle_quit(struct network *s, struct line *l) {
298
GList *g = s->channels;
299
if(!line_get_nick(l))return;
301
struct channel *c = (struct channel *)g->data;
302
struct nick *n = find_nick(c, line_get_nick(l));
305
c->nicks = g_list_remove(c->nicks, n);
312
static void handle_mode(struct network *s, struct line *l)
315
* MODE %|<nick>|<channel> [<mode> [<mode parameters>]] */
317
enum mode_type t = ADD;
320
/* We only care about channel modes and our own mode */
321
nick = xmlGetProp(s->xmlConf, "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;
329
s->modes[(unsigned char)l->args[2][i]] = t;
334
} else if(l->args[1][0] == '#' || l->args[1][0] == '&') {
335
struct channel *c = find_channel(s, l->args[1]);
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
347
n = find_nick(c, l->args[++arg]);
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]);
352
n->mode = (t == ADD?'@':' ');
355
n = find_nick(c, l->args[++arg]);
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]);
360
n->mode = (t == ADD?'+':' ');
363
if(!l->args[++arg]) {
364
g_error("Mode l requires argument, but no argument found");
367
c->limit = atol(l->args[arg]);
371
if(!l->args[++arg]) {
372
g_error("Mode k requires argument, but no argument found");
376
if(t) xmlSetProp(c->xmlConf, "key", l->args[arg]);
377
else xmlUnsetProp(c->xmlConf, "key");
381
s->modes[(unsigned char)l->args[2][i]] = t;
390
static void handle_004(struct network *s, struct line *l)
392
if(l->direction == TO_SERVER)return;
394
s->supported_modes[0] = strdup(l->args[4]);
395
s->supported_modes[1] = strdup(l->args[5]);
398
static void handle_005(struct network *s, struct line *l)
401
if(l->direction == TO_SERVER)return;
403
s->features = malloc(sizeof(char *) * l->argc);
405
for(i = 3; i < l->argc-1; i++) {
406
s->features[j] = strdup(l->args[i]);
410
s->features[j] = NULL;
413
static void handle_nick(struct network *s, struct line *l)
416
GList *g = s->channels;
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;
421
struct channel *c = (struct channel *)g->data;
422
struct nick *n = find_nick(c, line_get_nick(l));
425
n->name = strdup(l->args[1]);
430
own_nick = xmlGetProp(s->xmlConf, "nick");
432
if(!strcasecmp(line_get_nick(l), own_nick)) {
433
xmlSetProp(s->xmlConf, "nick", l->args[1]);
439
void state_reconnect(struct network *s)
441
GList *l = s->channels;
443
/* Remove list of channels */
445
struct channel *ch = (struct channel *)l->data;
451
g_list_free(s->channels);
456
static struct irc_command {
459
void (*handler) (struct network *s, struct line *l);
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 },
477
void state_handle_data(struct network *s, struct line *l)
481
if(!s || !l->args || !l->args[0])return;
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;
488
irc_commands[i].handler(s,l);
496
GSList *gen_replication(struct network *s)
503
char *nick, *server_name, *channel_name;
506
server_name = xmlGetProp(s->xmlConf, "name");
507
nick = xmlGetProp(s->xmlConf, "nick");
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);
518
ret = g_slist_append(ret, irc_parse_linef(":%s JOIN %s%s%s\r\n", nick, channel_name, key?" ":"", key?key:""));
523
ret = g_slist_append(ret, irc_parse_linef(":%s 332 %s %s :%s\r\n", server_name, nick, channel_name, c->topic));
525
ret = g_slist_append(ret, irc_parse_linef(":%s 331 %s %s :No topic set\r\n", server_name, nick, channel_name));
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);
535
ret = g_slist_append(ret, irc_parse_linef(":%s 366 %s %s :End of /names list\r\n", server_name, nick, channel_name));
537
cl = g_list_next(cl);
538
xmlFree(channel_name);
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)));
544
xmlFree(server_name);