Add const.
[jelmer/ctrlproxy.git] / lib / state.c
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 3 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 #include "irc.h"
22
23 static void free_network_nick(struct irc_network_state *, struct network_nick *);
24 static void free_channel(struct irc_channel_state *c);
25
26 enum mode_type { REMOVE = 0, ADD = 1 };
27
28 #define CHECK_ORIGIN(s,l,name) \
29         if ((l)->origin == NULL) { \
30                 network_state_log(LOG_WARNING, (s), \
31                                                   "Received "name" line without origin"); \
32                 return; \
33         }
34
35 void network_nick_set_data(struct network_nick *n, const char *nick, 
36                                                    const char *username, const char *host)
37 {
38         gboolean changed = FALSE;
39
40         g_assert(n);
41         g_assert(nick);
42         
43         if (!n->nick || strcmp(nick, n->nick) != 0) {
44                 g_free(n->nick); n->nick = g_strdup(nick);
45                 changed = TRUE;
46         }
47
48         g_assert(username);
49         if (!n->username || strcmp(username, n->username) != 0) {
50                 g_free(n->username); n->username = g_strdup(username);
51                 changed = TRUE;
52         }
53         
54         g_assert(host);
55         if (!n->hostname || strcmp(host, n->hostname) != 0) {
56                 g_free(n->hostname); n->hostname = g_strdup(host);
57                 changed = TRUE;
58         }
59         
60         if (changed) {
61                 g_free(n->hostmask);
62                 n->hostmask = g_strdup_printf("%s!%s@%s", nick, username, host);
63         }
64 }
65
66 gboolean network_nick_set_nick(struct network_nick *n, const char *nick)
67 {
68         if (n == NULL)
69                 return FALSE;
70
71         if (n->nick != NULL && !strcmp(nick, n->nick)) 
72                 return TRUE;
73
74         g_free(n->nick);
75         n->nick = g_strdup(nick);
76         
77         g_free(n->hostmask);
78         n->hostmask = g_strdup_printf("%s!%s@%s", nick, n->username, n->hostname);
79
80         return TRUE;
81 }
82
83 gboolean network_nick_set_hostmask(struct network_nick *n, const char *hm)
84 {
85         char *t, *u;
86
87         if (n == NULL)
88                 return FALSE;
89
90         if (hm == NULL)
91                 return FALSE;
92
93         if (n->hostmask && !strcmp(n->hostmask, hm))
94                 return TRUE;
95
96         g_free(n->hostmask);
97         g_free(n->nick); n->nick = NULL;
98         g_free(n->username); n->username = NULL;
99         g_free(n->hostname); n->hostname = NULL;
100         n->hostmask = g_strdup(hm);
101
102         t = strchr(hm, '!');
103         if (!t) 
104                 return FALSE;
105         n->nick = g_strndup(hm, t-hm);
106         
107         u = strchr(t, '@');
108         if (!u) 
109                 return FALSE;
110         n->username = g_strndup(t+1, u-t-1);
111
112         n->hostname = g_strdup(u+1);
113
114         return TRUE;
115 }
116
117 static void free_channel_nick(struct channel_nick *n)
118 {
119         g_assert(n);
120
121         g_assert(n->channel);
122         g_assert(n->global_nick);
123
124         n->channel->nicks = g_list_remove(n->channel->nicks, n);
125         n->global_nick->channel_nicks = g_list_remove(n->global_nick->channel_nicks, n);
126
127         if (g_list_length(n->global_nick->channel_nicks) == 0 && n->global_nick->query == 0) 
128                 free_network_nick(n->channel->network, n->global_nick);
129
130         g_free(n->last_flags);
131         g_free(n);
132 }
133
134 static void free_invitelist(struct irc_channel_state *c)
135 {
136         GList *g;
137         g_assert(c);
138
139         g = c->invitelist;
140         while(g) {
141                 g_free(g->data);
142                 g = g_list_remove(g, g->data);
143         }
144         c->invitelist = NULL;
145 }
146
147
148 static void free_exceptlist(struct irc_channel_state *c)
149 {
150         GList *g;
151         g_assert(c);
152
153         g = c->exceptlist;
154         while(g) {
155                 g_free(g->data);
156                 g = g_list_remove(g, g->data);
157         }
158         c->exceptlist = NULL;
159 }
160
161 static void free_banlist_entry(struct banlist_entry *be)
162 {
163         g_free(be->hostmask);
164         g_free(be->by);
165         g_free(be);
166 }
167
168 static char *find_exceptlist_entry(GList *entries, const char *entry)
169 {
170         GList *gl;
171         for (gl = entries; gl; gl = gl->next) {
172                 if (!strcmp(gl->data, entry))
173                         return gl->data;
174         }
175         return NULL;
176 }
177
178 static struct banlist_entry *find_banlist_entry(GList *entries, const char *hostmask)
179 {
180         GList *gl;
181         for (gl = entries; gl; gl = gl->next) {
182                 struct banlist_entry *be = gl->data;
183                 if (!strcmp(be->hostmask, hostmask))
184                         return be;
185         }
186         return NULL;
187 }
188
189 static void free_banlist(struct irc_channel_state *c)
190 {
191         GList *g;
192         
193         g_assert(c);
194         g = c->banlist;
195         while(g) {
196                 struct banlist_entry *be = g->data;
197                 g = g_list_remove(g, be);
198                 free_banlist_entry(be);
199         }
200         c->banlist = NULL;
201 }
202
203 static void free_names(struct irc_channel_state *c)
204 {
205         while(c->nicks) {
206                 free_channel_nick((struct channel_nick *)c->nicks->data);
207         }
208         c->nicks = NULL;
209 }
210
211 static void free_channel(struct irc_channel_state *c)
212 {
213         if (c == NULL)
214                 return;
215         free_names(c);
216         g_free(c->name);
217         g_free(c->topic);
218         g_free(c->topic_set_by);
219         g_free(c->key);
220         g_assert(c->network);
221         c->network->channels = g_list_remove(c->network->channels, c);
222         g_free(c);
223 }
224
225 struct irc_channel_state *find_channel(struct irc_network_state *st, const char *name)
226 {
227         GList *cl;
228         g_assert(st);
229         g_assert(name);
230         for (cl = st->channels; cl; cl = cl->next) {
231                 struct irc_channel_state *c = (struct irc_channel_state *)cl->data;
232
233                 if (!irccmp(st->info, c->name, name)) 
234                         return c;
235         }
236         return NULL;
237 }
238
239 struct irc_channel_state *find_add_channel(struct irc_network_state *st, char *name) 
240 {
241         struct irc_channel_state *c;
242         g_assert(st);
243         g_assert(name);
244         
245         c = find_channel(st, name);
246         if (c)
247                 return c;
248         c = g_new0(struct irc_channel_state ,1);
249         c->network = st;
250         st->channels = g_list_append(st->channels, c);
251         c->name = g_strdup(name);
252
253         return c;
254 }
255
256 /**
257  * Find channel nick by name
258  *
259  * @param c Channel state to search
260  * @param name Name of the nick to search for
261  * @return NULL if not found, channel_nick if found
262  */
263 struct channel_nick *find_channel_nick(struct irc_channel_state *c, 
264                                                                            const char *name) 
265 {
266         GList *l;
267         const char *realname = name;
268         g_assert(name);
269         g_assert(c);
270
271         g_assert(c->network);
272         if (is_prefix(realname[0], c->network->info))
273                 realname++;
274
275         for (l = c->nicks; l; l = l->next) {
276                 struct channel_nick *n = (struct channel_nick *)l->data;
277                 if (!irccmp(c->network->info, n->global_nick->nick, realname))
278                         return n;
279         }
280
281         return NULL;
282 }
283
284 /**
285  * Find channel nick by hostmask
286  *
287  * @param c Channel state to search
288  * @param hm Hostmask of the nick to search for
289  * @return NULL if not found, channel_nick if found
290  */
291 struct channel_nick *find_channel_nick_hostmask(struct irc_channel_state *c, 
292                                                                                             const char *hm) 
293 {
294         GList *l;
295         g_assert(hm);
296         g_assert(c);
297
298         g_assert(c->network);
299         for (l = c->nicks; l; l = l->next) {
300                 struct channel_nick *n = (struct channel_nick *)l->data;
301                 if (!irccmp(c->network->info, n->global_nick->hostmask, hm))
302                         return n;
303         }
304
305         return NULL;
306 }
307
308 /**
309  * Find network nick by name
310  * 
311  * @param n Network state to search
312  * @param name Name of the nick to search for
313  * @return NULL if not found, network_nick otherwise
314  */
315 struct network_nick *find_network_nick(struct irc_network_state *n, 
316                                                                            const char *name)
317 {
318         GList *gl;
319
320         g_assert(name);
321         g_assert(n);
322
323         if (!irccmp(n->info, n->me.nick, name))
324                 return &n->me;
325
326         for (gl = n->nicks; gl; gl = gl->next) {
327                 struct network_nick *ndd = (struct network_nick*)gl->data;
328                 if (!irccmp(n->info, ndd->nick, name)) {
329                         return ndd;
330                 }
331         }
332
333         return NULL;
334 }
335
336 /**
337  * Search for a network nick, or add it if not found.
338  *
339  * @param n Network state to search
340  * @param name Name of the nick to search for
341  * @return network_nick structure, or NULL if out of memory.
342  */
343 struct network_nick *find_add_network_nick(struct irc_network_state *n, 
344                                                                                    const char *name)
345 {
346         struct network_nick *nd;
347
348         g_assert(name != NULL);
349         g_assert(n != NULL);
350
351         nd = find_network_nick(n, name);
352         if (nd != NULL) 
353                 return nd;
354
355         /* create one, if it doesn't exist */
356         nd = g_new0(struct network_nick,1);
357         g_assert(!is_prefix(name[0], n->info));
358         nd->nick = g_strdup(name);
359         nd->hops = -1;
360         
361         n->nicks = g_list_append(n->nicks, nd);
362         return nd;
363 }
364
365 /**
366  * Search for a channel nick, or add it if not found.
367  *
368  * @param n Channel state to search
369  * @param name Name of the nick to search for
370  * @return channel_nick structure, or NULL if out of memory.
371  */
372 struct channel_nick *find_add_channel_nick(struct irc_channel_state *c, 
373                                                                                    const char *name) 
374 {
375         struct channel_nick *n;
376         const char *realname = name;
377         char prefix = 0;
378
379         g_assert(c);
380         g_assert(name);
381         g_assert(strlen(name) > 0);
382         g_assert(c->network);
383
384         if (is_prefix(realname[0], c->network->info)) {
385                 prefix = realname[0];
386                 realname++;
387         }
388
389         n = find_channel_nick(c, realname);
390         if (n != NULL) 
391                 return n;
392
393         n = g_new0(struct channel_nick,1);
394         
395         n->channel = c;
396         n->global_nick = find_add_network_nick(c->network, realname);
397         if (prefix != 0) {
398                 modes_set_mode(n->modes, get_mode_by_prefix(prefix, c->network->info));
399     }
400         c->nicks = g_list_append(c->nicks, n);
401         n->global_nick->channel_nicks = g_list_append(n->global_nick->channel_nicks, n);
402         return n;
403 }
404
405 static void handle_join(struct irc_network_state *s, const struct irc_line *l)
406 {
407         struct irc_channel_state *c;
408         struct channel_nick *ni;
409         int i;
410         char **channels;
411         char *nick;
412
413         CHECK_ORIGIN(s,l,"NICK");
414
415         nick = line_get_nick(l);
416         
417         channels = g_strsplit(l->args[1], ",", 0);
418
419         for (i = 0; channels[i]; i++) {
420                 /* Someone is joining a channel the user is on */
421                 c = find_add_channel(s, channels[i]);
422                 g_assert(s->channels != NULL);
423                 ni = find_add_channel_nick(c, nick);
424                 network_nick_set_hostmask(ni->global_nick, l->origin);
425
426                 /* The user is joining a channel */
427                 if (!irccmp(s->info, nick, s->me.nick)) {
428                         network_state_log(LOG_TRACE, s, "Joining channel %s", c->name);
429                 } else {
430                         network_state_log(LOG_TRACE, s, "%s joins channel %s", nick, 
431                                                           c->name);
432                 }
433         }
434         g_free(nick);
435         g_strfreev(channels);
436 }
437
438
439 static void handle_part(struct irc_network_state *s, const struct irc_line *l)
440 {
441         struct irc_channel_state *c;
442         struct channel_nick *n;
443         char **channels;
444         int i;
445         char *nick;
446
447         CHECK_ORIGIN(s,l,"PART");
448         
449         nick = line_get_nick(l);
450
451         if (nick == NULL) 
452                 return;
453
454         channels = g_strsplit(l->args[1], ",", 0);
455
456         for (i = 0; channels[i]; i++) {
457                 c = find_channel(s, channels[i]);
458
459                 if (c == NULL) {
460                         network_state_log(LOG_WARNING, s, 
461                                         "Can't part or let other nick part %s(unknown channel)", 
462                                         channels[i]);
463                         continue;
464                 }
465
466                 n = find_channel_nick(c, nick);
467                 if (n != NULL) {
468                         free_channel_nick(n);
469                 } else {
470                         network_state_log(LOG_WARNING, s, 
471                                 "Can't remove nick %s from channel %s: nick not on channel", 
472                                 nick, channels[i]);
473                 }
474
475                 if (!irccmp(s->info, nick, s->me.nick) && c) {
476                         network_state_log(LOG_TRACE, s, "Leaving %s", channels[i]);
477                         free_channel(c);
478                 } else {
479                         network_state_log(LOG_TRACE, s, "%s leaves %s", nick, channels[i]);
480                 }
481         }
482         g_free(nick);
483         g_strfreev(channels);
484 }
485
486 static void handle_kick(struct irc_network_state *s, const struct irc_line *l) 
487 {
488         struct irc_channel_state *c;
489         struct channel_nick *n;
490         char **channels, **nicks;
491         int i;
492         char *nick;
493
494         CHECK_ORIGIN(s,l,"KICK");
495         
496         nick = line_get_nick(l);
497
498         channels = g_strsplit(l->args[1], ",", 0);
499         nicks = g_strsplit(l->args[2], ",", 0);
500
501         for (i = 0; channels[i] && nicks[i]; i++) {
502                 c = find_channel(s, channels[i]);
503
504                 if (c == NULL){
505                         network_state_log(LOG_WARNING, s, "Can't kick nick %s from %s", nicks[i], channels[i]);
506                         continue;
507                 }
508
509                 n = find_channel_nick(c, nicks[i]);
510                 if (n == NULL) {
511                         network_state_log(LOG_WARNING, s, "Can't kick nick %s from channel %s: nick not on channel", nicks[i], channels[i]);
512                         continue;
513                 }
514
515                 free_channel_nick(n);
516
517                 if (!irccmp(s->info, nicks[i], s->me.nick)) {
518                         network_state_log(LOG_INFO, s, "Kicked off %s by %s", c->name, nick);
519                         free_channel(c);
520                 } else {
521                         network_state_log(LOG_TRACE, s, "%s kicked off %s by %s", nicks[i], channels[i], nick);
522                 }
523         }
524
525         if (channels[i] != NULL || nicks[i] != NULL) {
526                 network_state_log(LOG_WARNING, s, 
527                                           "KICK command has unequal number of channels and nicks");
528         }
529
530         g_free(nick);
531         g_strfreev(nicks);
532         g_strfreev(channels);
533 }
534
535 static void handle_topic(struct irc_network_state *s, const struct irc_line *l) 
536 {
537         struct irc_channel_state *c = find_channel(s, l->args[1]);
538         
539         CHECK_ORIGIN(s, l, "TOPIC");
540
541         if (c->topic != NULL)
542                 g_free(c->topic);
543         if (c->topic_set_by != NULL)
544                 g_free(c->topic_set_by);
545         c->topic = g_strdup(l->args[2]);
546         c->topic_set_time = time(NULL);
547         c->topic_set_by = line_get_nick(l);
548 }
549
550 static void handle_332(struct irc_network_state *s, const struct irc_line *l) 
551 {
552         struct irc_channel_state *c = find_channel(s, l->args[2]);
553
554         if (c == NULL) {
555                 network_state_log(LOG_WARNING, s, 
556                                         "Can't set topic for unknown channel '%s'!", l->args[2]);
557                 return;
558         }
559
560         g_free(c->topic);
561         c->topic = g_strdup(l->args[3]);
562 }
563
564 static void handle_333(struct irc_network_state *s, const struct irc_line *l) 
565 {
566         struct irc_channel_state *c = find_channel(s, l->args[2]);
567
568         if (!c) {
569                 network_state_log(LOG_WARNING, s, 
570                                 "Can't set topic last set time for unknown channel '%s'!", 
571                                 l->args[2]);
572                 return;
573         }
574
575         c->topic_set_time = strtoul(l->args[4], NULL, 0);
576         c->topic_set_by = g_strdup(l->args[3]);
577 }
578
579 static void handle_no_topic(struct irc_network_state *s, const struct irc_line *l) 
580 {
581         struct irc_channel_state *c = find_channel(s, l->args[1]);
582
583         if (c == NULL) {
584                 network_state_log(LOG_WARNING, s, 
585                                         "Can't unset topic for unknown channel '%s'!", l->args[2]);
586                 return;
587         }
588
589         g_free(c->topic);
590         c->topic = NULL;
591 }
592
593 static void handle_namreply(struct irc_network_state *s, const struct irc_line *l) 
594 {
595         gchar **names;
596         int i;
597         struct irc_channel_state *c = find_channel(s, l->args[3]);
598
599         if (c == NULL) {
600                 network_state_log(LOG_WARNING, s, 
601                                         "Can't add names to %s: channel not found", l->args[3]);
602                 return;
603         }
604
605         c->mode = l->args[2][0];
606         if (!c->namreply_started) {
607                 free_names(c);
608                 c->namreply_started = TRUE;
609         }
610         names = g_strsplit(l->args[4], " ", -1);
611
612         for (i = 0; names[i]; i++) {
613                 if (strlen(names[i]) == 0) continue;
614                 find_add_channel_nick(c, names[i]);
615         }
616         g_strfreev(names);
617 }
618
619 static void handle_end_names(struct irc_network_state *s, const struct irc_line *l) 
620 {
621         struct irc_channel_state *c = find_channel(s, l->args[2]);
622         if (c != NULL)
623                 c->namreply_started = FALSE;
624         else 
625                 network_state_log(LOG_WARNING, s, 
626                                   "Can't end /NAMES command for %s: channel not found", 
627                                   l->args[2]);
628 }
629
630 static void handle_invitelist_entry(struct irc_network_state *s, const struct irc_line *l) 
631 {
632         struct irc_channel_state *c = find_channel(s, l->args[2]);
633         
634         if (c == NULL) {
635                 network_state_log(LOG_WARNING, s, 
636                                 "Can't add invitelist entries to %s: channel not found", 
637                                 l->args[2]);
638                 return;
639         }
640
641         if (!c->invitelist_started) {
642                 free_invitelist(c);
643                 c->invitelist_started = TRUE;
644         }
645
646         c->invitelist = g_list_append(c->invitelist, g_strdup(l->args[3]));
647 }
648
649 static void handle_end_invitelist(struct irc_network_state *s, const struct irc_line *l) 
650 {
651         struct irc_channel_state *c = find_channel(s, l->args[2]);
652         if (c != NULL)
653                 c->invitelist_started = FALSE;
654         else 
655                 network_state_log(LOG_WARNING, s, 
656                           "Can't end invitelist for %s: channel not found", l->args[2]);
657 }
658
659 static void handle_exceptlist_entry(struct irc_network_state *s, const struct irc_line *l) 
660 {
661         struct irc_channel_state *c = find_channel(s, l->args[2]);
662         
663         if (c == NULL) {
664                 network_state_log(LOG_WARNING, s, 
665                                 "Can't add exceptlist entries to %s: channel not found", 
666                                 l->args[2]);
667                 return;
668         }
669
670         if (!c->exceptlist_started) {
671                 free_exceptlist(c);
672                 c->exceptlist_started = TRUE;
673         }
674
675         c->exceptlist = g_list_append(c->exceptlist, g_strdup(l->args[3]));
676 }
677
678 static void handle_end_exceptlist(struct irc_network_state *s, const struct irc_line *l) 
679 {
680         struct irc_channel_state *c = find_channel(s, l->args[2]);
681         if (c != NULL)
682                 c->exceptlist_started = FALSE;
683         else 
684                 network_state_log(LOG_WARNING, s, 
685                         "Can't end exceptlist for %s: channel not found", l->args[2]);
686 }
687
688
689
690 static void handle_banlist_entry(struct irc_network_state *s, const struct irc_line *l) 
691 {
692         struct irc_channel_state *c = find_channel(s, l->args[2]);
693         struct banlist_entry *be;
694         
695         if (c == NULL) {
696                 network_state_log(LOG_WARNING, s, 
697                                         "Can't add banlist entries to %s: channel not found", 
698                                         l->args[2]);
699                 return;
700         }
701
702         if (!c->banlist_started) {
703                 free_banlist(c);
704                 c->banlist_started = TRUE;
705         }
706
707         be = g_new0(struct banlist_entry, 1);
708         be->hostmask = g_strdup(l->args[3]);
709         if (l->args[4] != NULL) {
710                 be->by = g_strdup(l->args[4]);
711                 if (l->args[5]) 
712                         be->time_set = atol(l->args[5]);
713         }
714
715         c->banlist = g_list_append(c->banlist, be);
716 }
717
718 static void handle_end_banlist(struct irc_network_state *s, const struct irc_line *l) 
719 {
720         struct irc_channel_state *c = find_channel(s, l->args[2]);
721
722         if (c != NULL)
723                 c->banlist_started = FALSE;
724         else 
725                 network_state_log(LOG_WARNING, s, 
726                                 "Can't end banlist for %s: channel not found", l->args[2]);
727 }
728
729 static void handle_whoreply(struct irc_network_state *s, const struct irc_line *l) 
730 {
731         struct irc_channel_state *cs;
732         struct network_nick *nn;
733         struct channel_nick *cn;
734         char *fullname;
735
736         nn = find_add_network_nick(s, l->args[6]);
737         g_assert(nn != NULL);
738         network_nick_set_data(nn, l->args[6], l->args[3], l->args[4]);
739
740         fullname = NULL;
741         nn->hops = strtol(l->args[8], &fullname, 10);
742         g_assert(fullname);
743
744         if (nn->fullname == NULL) {
745                 if (fullname[0] == ' ')
746                         fullname++;
747
748                 g_free(nn->fullname);
749                 nn->fullname = g_strdup(fullname);
750         }
751
752         g_free(nn->server);
753         nn->server = g_strdup(l->args[5]);
754
755         cs = find_channel(s, l->args[2]);
756         if (cs == NULL) 
757                 return;
758
759         cn = find_channel_nick(cs, nn->nick);
760         
761         if (cn == NULL) {
762                 network_state_log(LOG_WARNING, 
763                                                   s,
764                                                   "User %s in WHO reply not in expected channel %s!", 
765                                                   nn->nick, l->args[2]);
766                 return;
767         }
768
769         g_free(cn->last_flags);
770         cn->last_flags = g_strdup(l->args[7]);
771
772         cn->last_update = time(NULL);
773 }
774
775 static void handle_end_who(struct irc_network_state *s, const struct irc_line *l) 
776 {
777 }
778
779 static void handle_nowaway(struct irc_network_state *s, const struct irc_line *l)
780 {
781         s->is_away = TRUE;
782 }
783
784 static void handle_unaway(struct irc_network_state *s, const struct irc_line *l)
785 {
786         s->is_away = FALSE;
787 }
788
789 static void handle_quit(struct irc_network_state *s, const struct irc_line *l) 
790 {
791         char *nick;
792         struct network_nick *nn;
793
794         CHECK_ORIGIN(s, l, "QUIT");
795
796         nick = line_get_nick(l);
797         nn = find_network_nick(s, nick);
798         g_free(nick);
799
800         if (nn == &s->me) {
801                 while (nn->channel_nicks) {
802                         struct channel_nick *n = nn->channel_nicks->data;
803                         free_channel_nick(n);
804                 }
805         } else if (nn != NULL) 
806                 free_network_nick(s, nn);
807 }
808
809 gboolean modes_change_mode(irc_modes_t modes, gboolean set, char newmode)
810 {
811         if (modes[(unsigned char)newmode] == set)
812                 return FALSE;
813
814         modes[(unsigned char)newmode] = set;
815
816         return TRUE;
817 }
818
819 static int channel_state_change_mode(struct irc_network_state *s, struct network_nick *by, struct irc_channel_state *c, gboolean set, char mode, const char *opt_arg)
820 {
821         struct irc_network_info *info = s->info;
822
823         if (!is_channel_mode(info, mode)) {
824                 network_state_log(LOG_WARNING, s, "Mode '%c' set on channel %s is not in the supported list of channel modes from the server", mode, c->name);
825         }
826
827         if (mode == 'b') { /* Ban */
828                 struct banlist_entry *be;
829
830                 if (opt_arg == NULL) {
831                         network_state_log(LOG_WARNING, s, "Missing argument for ban MODE set/unset");
832                         return -1;
833                 }
834
835                 if (set) {
836                         be = g_new0(struct banlist_entry, 1);
837                         be->time_set = time(NULL);
838                         be->hostmask = g_strdup(opt_arg);
839                         be->by = (by?g_strdup(by->nick):NULL);
840                         c->banlist = g_list_append(c->banlist, be);
841                 } else {
842                         be = find_banlist_entry(c->banlist, opt_arg);
843                         if (be == NULL) {
844                                 network_state_log(LOG_WARNING, s, "Unable to remove nonpresent banlist entry '%s'", opt_arg);
845                                 return 1;
846                         }
847                         c->banlist = g_list_remove(c->banlist, be);
848                         free_banlist_entry(be);
849                 }
850                 return 1;
851         } else if (mode == 'e') { /* Ban exemption */ 
852                 if (opt_arg == NULL) {
853                         network_state_log(LOG_WARNING, s, "Missing argument for ban exception MODE set/unset");
854                         return -1;
855                 }
856
857                 if (set) {
858                         c->exceptlist = g_list_append(c->exceptlist, g_strdup(opt_arg));
859                 } else {
860                         char *be = find_exceptlist_entry(c->banlist, opt_arg);
861                         if (be == NULL) {
862                                 network_state_log(LOG_WARNING, s, "Unable to remove nonpresent ban except list entry '%s'", opt_arg);
863                                 return 1;
864                         }
865                         c->banlist = g_list_remove(c->exceptlist, be);
866                         g_free(be);
867                 }
868                 return 1;
869         } else if (mode == 'l') { /* Limit */
870                 modes_change_mode(c->modes, set, 'l');
871                 if (set) {
872                         if (!opt_arg) {
873                                 network_state_log(LOG_WARNING, s, "Mode +l requires argument, but no argument found");
874                                 return -1;
875                         }
876                         c->limit = atol(opt_arg);
877                 } else {
878                         c->limit = 0;
879                 }
880                 return 1;
881         } else if (mode == 'k') {
882                 modes_change_mode(c->modes, set, 'k');
883                 if (set) {
884                         if (opt_arg == NULL) {
885                                 network_state_log(LOG_WARNING, s, "Mode k requires argument, but no argument found");
886                                 return -1;
887                         }
888
889                         g_free(c->key);
890                         c->key = g_strdup(opt_arg);
891                 } else {
892                         g_free(c->key);
893                         c->key = NULL;
894                 }
895                 return 1;
896         } else if (is_prefix_mode(info, mode)) {
897                 struct channel_nick *n;
898                 
899                 if (opt_arg == NULL) {
900                         network_state_log(LOG_WARNING, s, "Mode %c requires nick argument, but no argument found", mode);
901                         return -1;
902                 }
903
904                 n = find_channel_nick(c, opt_arg); 
905                 if (!n) {
906                         network_state_log(LOG_WARNING, s, "Can't set mode %c%c on nick %s on channel %s, because nick does not exist!", set?'+':'-', mode, opt_arg, c->name);
907                         return -1;
908                 }
909                 if (set) {
910                         if (!modes_set_mode(n->modes, mode)) {
911                                 network_state_log(LOG_WARNING, s, "Unable to add mode '%c' to modes %s on nick %s on channel %s", mode, n->modes, opt_arg, c->name);
912                         }
913                 } else {
914                         if (!modes_unset_mode(n->modes, mode)) {
915                                 network_state_log(LOG_WARNING, s, "Unable to remove mode '%c' from modes %s on nick %s on channel %s", mode, n->modes, opt_arg, c->name);
916                         }
917                 }
918                 return 1;
919         } else {
920                 modes_change_mode(c->modes, set, mode);
921                 return 0;
922         }
923 }
924
925 static void handle_mode(struct irc_network_state *s, const struct irc_line *l)
926 {
927         /* Format:
928          * MODE %|<nick>|<channel> [<mode> [<mode parameters>]] */
929         gboolean t = TRUE;
930         int i;
931         int arg = 3;
932
933         g_assert(s != NULL);
934
935         /* Channel modes */
936         if (is_channelname(l->args[1], s->info)) {
937                 struct irc_channel_state *c = find_channel(s, l->args[1]);
938                 struct network_nick *by;
939                 int ret;
940                 char *by_name;
941
942                 if (c == NULL) {
943                         network_state_log(LOG_WARNING, s, 
944                                 "Unable to change mode for unknown channel '%s'", l->args[1]);
945                         return;
946                 }
947
948                 by_name = line_get_nick(l);
949                 by = find_network_nick(s, by_name);
950                 g_free(by_name);
951                 
952                 for(i = 0; l->args[2][i]; i++) {
953                         switch(l->args[2][i]) {
954                                 case '+': t = TRUE; break;
955                                 case '-': t = FALSE; break;
956                                 default:
957                                           ret = channel_state_change_mode(s, by, c, t, 
958                                                                                                           l->args[2][i],
959                                                                                                           l->args[arg]);
960                                           if (ret == -1)
961                                                   return;
962                                           arg += ret;
963                                           break;
964                         }
965                 }
966
967                 /* User modes */
968         } else {
969                 struct network_nick *nn = find_add_network_nick(s, l->args[1]);
970
971                 for(i = 0; l->args[2][i]; i++) {
972                         switch(l->args[2][i]) {
973                                 case '+': t = TRUE;break;
974                                 case '-': t = FALSE; break;
975                                 default:
976                                           modes_change_mode(nn->modes, t, l->args[2][i]);
977                                           break;
978                         }
979                 }
980         }
981
982         if (l->args[arg] != NULL && strcmp(l->args[arg], "") != 0) {
983                 network_state_log(LOG_WARNING, s, 
984                                                   "mode %s %s argument not consumed: %s", l->args[2], 
985                                                   l->args[1], 
986                                                   l->args[arg]);
987         }
988 }
989
990 static void handle_001(struct irc_network_state *s, const struct irc_line *l)
991 {
992         g_free(s->me.nick);
993         s->me.nick = g_strdup(l->args[1]);
994 }
995
996 static void handle_004(struct irc_network_state *s, const struct irc_line *l)
997 {
998         s->info->supported_user_modes = g_strdup(l->args[4]);
999         s->info->supported_channel_modes = g_strdup(l->args[5]);
1000         s->info->server = g_strdup(l->args[2]);
1001 }
1002
1003 static void handle_privmsg(struct irc_network_state *s, const struct irc_line *l)
1004 {
1005         struct network_nick *nn;
1006         char *nick;
1007
1008         CHECK_ORIGIN(s,l,"PRIVMSG");
1009
1010         if (irccmp(s->info, l->args[1], s->me.nick) != 0) return;
1011
1012         nick = line_get_nick(l);
1013         nn = find_add_network_nick(s, nick);
1014         nn->query = 1;
1015         g_free(nick);
1016 }
1017
1018 static void handle_nick(struct irc_network_state *s, const struct irc_line *l)
1019 {
1020         struct network_nick *nn;
1021         char *nick;
1022
1023         CHECK_ORIGIN(s,l,"NICK");
1024         
1025         nick = line_get_nick(l);
1026         nn = find_add_network_nick(s, nick);
1027         g_free(nick);
1028         network_nick_set_nick(nn, l->args[1]);
1029 }
1030
1031 static void handle_umodeis(struct irc_network_state *s, const struct irc_line *l)
1032 {
1033         int i;
1034         memset(s->me.modes, 0, sizeof(s->me.modes));
1035         for (i = 0; i < strlen(l->args[1]); i++) {
1036                 s->me.modes[(unsigned char)l->args[1][i]] = 1;
1037         }
1038 }
1039
1040 static void handle_324(struct irc_network_state *s, const struct irc_line *l)
1041 {
1042         struct irc_channel_state *ch = find_channel(s, l->args[2]);
1043
1044         if (ch == NULL) {
1045                 network_state_log(LOG_WARNING, s, 
1046                         "Can't store modes for %s: channel not found", l->args[2]);
1047                 return;
1048         }
1049
1050         string2mode(l->args[3], ch->modes);
1051
1052         ch->mode_received = TRUE;
1053 }
1054
1055 static void handle_329(struct irc_network_state *s, const struct irc_line *l)
1056 {
1057         struct irc_channel_state *ch = find_channel(s, l->args[2]);
1058
1059         if (ch == NULL) {
1060                 network_state_log(LOG_WARNING, s, 
1061                         "Can't store creationtime for %s: channel not found", l->args[2]);
1062                 return;
1063         }
1064
1065         ch->creation_time = atol(l->args[3]);
1066 }
1067
1068 static void handle_302(struct irc_network_state *s, const struct irc_line *l)
1069 {
1070         int i;
1071         gchar **users = g_strsplit(g_strstrip(l->args[2]), " ", 0);
1072         for (i = 0; users[i]; i++) {
1073                 /* We got a USERHOST response, split it into nick and user@host, and check the nick */
1074                 gchar** tmp302 = g_strsplit(users[i], "=+", 2);
1075                 if (g_strv_length(tmp302) > 1) {
1076                         char *hm;
1077                         struct network_nick *nn = find_add_network_nick(s, tmp302[0]);
1078                         
1079                         hm = g_strdup_printf("%s!%s", tmp302[0], tmp302[1]);
1080                         network_nick_set_hostmask(nn, hm);
1081                         g_free(hm);
1082                 }
1083                 g_strfreev(tmp302);
1084         }
1085
1086         g_strfreev(users);
1087 }
1088
1089 extern void handle_005(struct irc_network_state *s, const struct irc_line *l);
1090
1091 static struct irc_command {
1092         char *command;
1093         int min_args;
1094         void (*handler) (struct irc_network_state *s, const struct irc_line *l);
1095 } irc_commands[] = {
1096         { "JOIN", 1, handle_join },
1097         { "PART", 1, handle_part },
1098         { "KICK", 2, handle_kick },
1099         { "QUIT", 0, handle_quit },
1100         { "TOPIC", 2, handle_topic },
1101         { "NICK", 1, handle_nick },
1102         { "PRIVMSG", 2, handle_privmsg },
1103         { "MODE", 2, handle_mode },
1104         { "001", 1, handle_001 },
1105         { "004", 5, handle_004 },
1106         { "005", 3, handle_005 },
1107         { "221", 1, handle_umodeis },
1108         { "302", 2, handle_302 },
1109         { "324", 3, handle_324 },
1110         { "329", 3, handle_329 },
1111         { "332", 3, handle_332 },
1112         { "333", 3, handle_333 },
1113         { "331", 1, handle_no_topic },
1114         { "353", 4, handle_namreply },
1115         { "366", 2, handle_end_names },
1116         { "367", 2, handle_banlist_entry },
1117         { "368", 2, handle_end_banlist },
1118         { "346", 2, handle_invitelist_entry },
1119         { "347", 2, handle_end_invitelist },
1120         { "348", 2, handle_exceptlist_entry },
1121         { "349", 2, handle_end_exceptlist },
1122         { "352", 8, handle_whoreply },
1123         { "315", 1, handle_end_who },
1124         { "306", 1, handle_nowaway },
1125         { "305", 1, handle_unaway },
1126         { NULL }
1127 };
1128
1129 gboolean state_handle_data(struct irc_network_state *s, const struct irc_line *l)
1130 {
1131         int i,j;
1132
1133         if (s == NULL || l == NULL || l->args == NULL || l->args[0] == NULL)
1134                 return FALSE;
1135
1136         for (i = 0; irc_commands[i].command; i++) {
1137                 if (!g_strcasecmp(irc_commands[i].command, l->args[0])) {
1138                         for (j = 0; j <= irc_commands[i].min_args; j++) {
1139                                 if (l->args[j] == NULL)
1140                                         return FALSE;
1141                         }
1142                         irc_commands[i].handler(s,l);
1143                         return TRUE;
1144                 }
1145         }
1146
1147         return FALSE;
1148 }
1149
1150 struct irc_network_state *network_state_init(const char *nick, 
1151                                                                                  const char *username, 
1152                                                                                  const char *hostname)
1153 {
1154         struct irc_network_state *state = g_new0(struct irc_network_state, 1);
1155         state->me.query = 1;
1156         network_nick_set_data(&state->me, nick, username, hostname);
1157         state->info = network_info_init();
1158
1159         return state;
1160 }
1161
1162 void free_network_nick(struct irc_network_state *st, struct network_nick *nn)
1163 {
1164         g_assert(nn != &st->me);
1165         g_assert(nn);
1166
1167         /* No recursion please... */
1168         nn->query = 1;
1169
1170         while (nn->channel_nicks) {
1171                 struct channel_nick *n = nn->channel_nicks->data;
1172                 free_channel_nick(n);
1173         }
1174
1175         g_free(nn->hostmask);
1176         g_free(nn->username);
1177         g_free(nn->hostname);
1178         g_free(nn->fullname);
1179         g_free(nn->server);
1180         g_free(nn->nick);
1181         st->nicks = g_list_remove(st->nicks, nn);
1182         g_free(nn);
1183 }
1184
1185 void free_network_state(struct irc_network_state *state)
1186 {
1187         if (state == NULL)
1188                 return;
1189
1190         while (state->channels != NULL)
1191                 free_channel((struct irc_channel_state *)state->channels->data);
1192
1193         g_free(state->me.nick);
1194         g_free(state->me.username);
1195         g_free(state->me.hostname);
1196         g_free(state->me.hostmask);
1197
1198         while (state->nicks != NULL)
1199         {
1200                 struct network_nick *nn = state->nicks->data;
1201                 free_network_nick(state, nn);
1202         }
1203
1204         free_network_info(state->info);
1205         g_free(state);
1206 }
1207
1208 void network_state_log(enum log_level l, 
1209                                            const struct irc_network_state *st, const char *fmt, ...)
1210 {
1211         char *ret;
1212         va_list ap;
1213
1214         if (st->log == NULL)
1215                 return;
1216
1217         g_assert(st);
1218         g_assert(fmt);
1219
1220         va_start(ap, fmt);
1221         ret = g_strdup_vprintf(fmt, ap);
1222         va_end(ap);
1223
1224         st->log(l, st->userdata, ret);
1225
1226         g_free(ret);
1227 }
1228
1229 void network_state_set_log_fn(struct irc_network_state *st, 
1230                                                           void (*fn) (enum log_level, void *, const char *),
1231                                                           void *userdata)
1232 {
1233         st->log = fn;
1234         st->userdata = userdata;
1235 }
1236
1237 void string2mode(const char *modes, irc_modes_t ar)
1238 {
1239         memset(ar, 0, sizeof(ar));
1240
1241         if (modes == NULL)
1242                 return;
1243
1244         g_assert(modes[0] == '+');
1245         modes++;
1246         for (; *modes; modes++) {
1247                 ar[(unsigned char)(*modes)] = 1;
1248         }
1249 }
1250
1251 char *mode2string(irc_modes_t modes)
1252 {
1253         char ret[256];
1254         unsigned char i;
1255         int pos = 0;
1256         ret[0] = '\0';
1257         for(i = 0; i < sizeof(modes); i++) {
1258                 if (modes[i]) { ret[pos] = (char)i; pos++; }
1259         }
1260         ret[pos] = '\0';
1261
1262         if (strlen(ret) == 0) {
1263                 return NULL;
1264         } else {
1265                 return g_strdup_printf("+%s", ret);
1266         }
1267 }
1268
1269 gboolean is_prefix_mode(const struct irc_network_info *info, char mode)
1270 {
1271         return get_prefix_by_mode(mode, info) != ' ';
1272 }
1273