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 3 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"
23 static void free_network_nick(struct irc_network_state *, struct network_nick *);
24 static void free_channel(struct irc_channel_state *c);
26 enum mode_type { REMOVE = 0, ADD = 1 };
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"); \
35 void network_nick_set_data(struct network_nick *n, const char *nick,
36 const char *username, const char *host)
38 gboolean changed = FALSE;
43 if (!n->nick || strcmp(nick, n->nick) != 0) {
44 g_free(n->nick); n->nick = g_strdup(nick);
49 if (!n->username || strcmp(username, n->username) != 0) {
50 g_free(n->username); n->username = g_strdup(username);
55 if (!n->hostname || strcmp(host, n->hostname) != 0) {
56 g_free(n->hostname); n->hostname = g_strdup(host);
62 n->hostmask = g_strdup_printf("%s!%s@%s", nick, username, host);
66 gboolean network_nick_set_nick(struct network_nick *n, const char *nick)
71 if (n->nick != NULL && !strcmp(nick, n->nick))
75 n->nick = g_strdup(nick);
78 n->hostmask = g_strdup_printf("%s!%s@%s", nick, n->username, n->hostname);
83 gboolean network_nick_set_hostmask(struct network_nick *n, const char *hm)
93 if (n->hostmask && !strcmp(n->hostmask, hm))
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);
105 n->nick = g_strndup(hm, t-hm);
110 n->username = g_strndup(t+1, u-t-1);
112 n->hostname = g_strdup(u+1);
117 static void free_channel_nick(struct channel_nick *n)
121 g_assert(n->channel);
122 g_assert(n->global_nick);
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);
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);
130 g_free(n->last_flags);
134 static void free_invitelist(struct irc_channel_state *c)
142 g = g_list_remove(g, g->data);
144 c->invitelist = NULL;
148 static void free_exceptlist(struct irc_channel_state *c)
156 g = g_list_remove(g, g->data);
158 c->exceptlist = NULL;
161 static void free_banlist_entry(struct banlist_entry *be)
163 g_free(be->hostmask);
168 static char *find_exceptlist_entry(GList *entries, const char *entry)
171 for (gl = entries; gl; gl = gl->next) {
172 if (!strcmp(gl->data, entry))
178 static struct banlist_entry *find_banlist_entry(GList *entries, const char *hostmask)
181 for (gl = entries; gl; gl = gl->next) {
182 struct banlist_entry *be = gl->data;
183 if (!strcmp(be->hostmask, hostmask))
189 static void free_banlist(struct irc_channel_state *c)
196 struct banlist_entry *be = g->data;
197 g = g_list_remove(g, be);
198 free_banlist_entry(be);
203 static void free_names(struct irc_channel_state *c)
206 free_channel_nick((struct channel_nick *)c->nicks->data);
211 static void free_channel(struct irc_channel_state *c)
218 g_free(c->topic_set_by);
220 g_assert(c->network);
221 c->network->channels = g_list_remove(c->network->channels, c);
225 struct irc_channel_state *find_channel(struct irc_network_state *st, const char *name)
230 for (cl = st->channels; cl; cl = cl->next) {
231 struct irc_channel_state *c = (struct irc_channel_state *)cl->data;
233 if (!irccmp(st->info, c->name, name))
239 struct irc_channel_state *find_add_channel(struct irc_network_state *st, char *name)
241 struct irc_channel_state *c;
245 c = find_channel(st, name);
248 c = g_new0(struct irc_channel_state ,1);
250 st->channels = g_list_append(st->channels, c);
251 c->name = g_strdup(name);
257 * Find channel nick by name
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
263 struct channel_nick *find_channel_nick(struct irc_channel_state *c,
267 const char *realname = name;
271 g_assert(c->network);
272 if (is_prefix(realname[0], c->network->info))
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))
285 * Find channel nick by hostmask
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
291 struct channel_nick *find_channel_nick_hostmask(struct irc_channel_state *c,
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))
309 * Find network nick by name
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
315 struct network_nick *find_network_nick(struct irc_network_state *n,
323 if (!irccmp(n->info, n->me.nick, name))
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)) {
337 * Search for a network nick, or add it if not found.
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.
343 struct network_nick *find_add_network_nick(struct irc_network_state *n,
346 struct network_nick *nd;
348 g_assert(name != NULL);
351 nd = find_network_nick(n, name);
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);
361 n->nicks = g_list_append(n->nicks, nd);
366 * Search for a channel nick, or add it if not found.
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.
372 struct channel_nick *find_add_channel_nick(struct irc_channel_state *c,
375 struct channel_nick *n;
376 const char *realname = name;
381 g_assert(strlen(name) > 0);
382 g_assert(c->network);
384 if (is_prefix(realname[0], c->network->info)) {
385 prefix = realname[0];
389 n = find_channel_nick(c, realname);
393 n = g_new0(struct channel_nick,1);
396 n->global_nick = find_add_network_nick(c->network, realname);
398 modes_set_mode(n->modes, get_mode_by_prefix(prefix, c->network->info));
400 c->nicks = g_list_append(c->nicks, n);
401 n->global_nick->channel_nicks = g_list_append(n->global_nick->channel_nicks, n);
405 static void handle_join(struct irc_network_state *s, const struct irc_line *l)
407 struct irc_channel_state *c;
408 struct channel_nick *ni;
413 CHECK_ORIGIN(s,l,"NICK");
415 nick = line_get_nick(l);
417 channels = g_strsplit(l->args[1], ",", 0);
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);
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);
430 network_state_log(LOG_TRACE, s, "%s joins channel %s", nick,
435 g_strfreev(channels);
439 static void handle_part(struct irc_network_state *s, const struct irc_line *l)
441 struct irc_channel_state *c;
442 struct channel_nick *n;
447 CHECK_ORIGIN(s,l,"PART");
449 nick = line_get_nick(l);
454 channels = g_strsplit(l->args[1], ",", 0);
456 for (i = 0; channels[i]; i++) {
457 c = find_channel(s, channels[i]);
460 network_state_log(LOG_WARNING, s,
461 "Can't part or let other nick part %s(unknown channel)",
466 n = find_channel_nick(c, nick);
468 free_channel_nick(n);
470 network_state_log(LOG_WARNING, s,
471 "Can't remove nick %s from channel %s: nick not on channel",
475 if (!irccmp(s->info, nick, s->me.nick) && c) {
476 network_state_log(LOG_TRACE, s, "Leaving %s", channels[i]);
479 network_state_log(LOG_TRACE, s, "%s leaves %s", nick, channels[i]);
483 g_strfreev(channels);
486 static void handle_kick(struct irc_network_state *s, const struct irc_line *l)
488 struct irc_channel_state *c;
489 struct channel_nick *n;
490 char **channels, **nicks;
494 CHECK_ORIGIN(s,l,"KICK");
496 nick = line_get_nick(l);
498 channels = g_strsplit(l->args[1], ",", 0);
499 nicks = g_strsplit(l->args[2], ",", 0);
501 for (i = 0; channels[i] && nicks[i]; i++) {
502 c = find_channel(s, channels[i]);
505 network_state_log(LOG_WARNING, s, "Can't kick nick %s from %s", nicks[i], channels[i]);
509 n = find_channel_nick(c, nicks[i]);
511 network_state_log(LOG_WARNING, s, "Can't kick nick %s from channel %s: nick not on channel", nicks[i], channels[i]);
515 free_channel_nick(n);
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);
521 network_state_log(LOG_TRACE, s, "%s kicked off %s by %s", nicks[i], channels[i], nick);
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");
532 g_strfreev(channels);
535 static void handle_topic(struct irc_network_state *s, const struct irc_line *l)
537 struct irc_channel_state *c = find_channel(s, l->args[1]);
539 CHECK_ORIGIN(s, l, "TOPIC");
541 if (c->topic != NULL)
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);
550 static void handle_332(struct irc_network_state *s, const struct irc_line *l)
552 struct irc_channel_state *c = find_channel(s, l->args[2]);
555 network_state_log(LOG_WARNING, s,
556 "Can't set topic for unknown channel '%s'!", l->args[2]);
561 c->topic = g_strdup(l->args[3]);
564 static void handle_333(struct irc_network_state *s, const struct irc_line *l)
566 struct irc_channel_state *c = find_channel(s, l->args[2]);
569 network_state_log(LOG_WARNING, s,
570 "Can't set topic last set time for unknown channel '%s'!",
575 c->topic_set_time = strtoul(l->args[4], NULL, 0);
576 c->topic_set_by = g_strdup(l->args[3]);
579 static void handle_no_topic(struct irc_network_state *s, const struct irc_line *l)
581 struct irc_channel_state *c = find_channel(s, l->args[1]);
584 network_state_log(LOG_WARNING, s,
585 "Can't unset topic for unknown channel '%s'!", l->args[2]);
593 static void handle_namreply(struct irc_network_state *s, const struct irc_line *l)
597 struct irc_channel_state *c = find_channel(s, l->args[3]);
600 network_state_log(LOG_WARNING, s,
601 "Can't add names to %s: channel not found", l->args[3]);
605 c->mode = l->args[2][0];
606 if (!c->namreply_started) {
608 c->namreply_started = TRUE;
610 names = g_strsplit(l->args[4], " ", -1);
612 for (i = 0; names[i]; i++) {
613 if (strlen(names[i]) == 0) continue;
614 find_add_channel_nick(c, names[i]);
619 static void handle_end_names(struct irc_network_state *s, const struct irc_line *l)
621 struct irc_channel_state *c = find_channel(s, l->args[2]);
623 c->namreply_started = FALSE;
625 network_state_log(LOG_WARNING, s,
626 "Can't end /NAMES command for %s: channel not found",
630 static void handle_invitelist_entry(struct irc_network_state *s, const struct irc_line *l)
632 struct irc_channel_state *c = find_channel(s, l->args[2]);
635 network_state_log(LOG_WARNING, s,
636 "Can't add invitelist entries to %s: channel not found",
641 if (!c->invitelist_started) {
643 c->invitelist_started = TRUE;
646 c->invitelist = g_list_append(c->invitelist, g_strdup(l->args[3]));
649 static void handle_end_invitelist(struct irc_network_state *s, const struct irc_line *l)
651 struct irc_channel_state *c = find_channel(s, l->args[2]);
653 c->invitelist_started = FALSE;
655 network_state_log(LOG_WARNING, s,
656 "Can't end invitelist for %s: channel not found", l->args[2]);
659 static void handle_exceptlist_entry(struct irc_network_state *s, const struct irc_line *l)
661 struct irc_channel_state *c = find_channel(s, l->args[2]);
664 network_state_log(LOG_WARNING, s,
665 "Can't add exceptlist entries to %s: channel not found",
670 if (!c->exceptlist_started) {
672 c->exceptlist_started = TRUE;
675 c->exceptlist = g_list_append(c->exceptlist, g_strdup(l->args[3]));
678 static void handle_end_exceptlist(struct irc_network_state *s, const struct irc_line *l)
680 struct irc_channel_state *c = find_channel(s, l->args[2]);
682 c->exceptlist_started = FALSE;
684 network_state_log(LOG_WARNING, s,
685 "Can't end exceptlist for %s: channel not found", l->args[2]);
690 static void handle_banlist_entry(struct irc_network_state *s, const struct irc_line *l)
692 struct irc_channel_state *c = find_channel(s, l->args[2]);
693 struct banlist_entry *be;
696 network_state_log(LOG_WARNING, s,
697 "Can't add banlist entries to %s: channel not found",
702 if (!c->banlist_started) {
704 c->banlist_started = TRUE;
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]);
712 be->time_set = atol(l->args[5]);
715 c->banlist = g_list_append(c->banlist, be);
718 static void handle_end_banlist(struct irc_network_state *s, const struct irc_line *l)
720 struct irc_channel_state *c = find_channel(s, l->args[2]);
723 c->banlist_started = FALSE;
725 network_state_log(LOG_WARNING, s,
726 "Can't end banlist for %s: channel not found", l->args[2]);
729 static void handle_whoreply(struct irc_network_state *s, const struct irc_line *l)
731 struct irc_channel_state *cs;
732 struct network_nick *nn;
733 struct channel_nick *cn;
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]);
741 nn->hops = strtol(l->args[8], &fullname, 10);
744 if (nn->fullname == NULL) {
745 if (fullname[0] == ' ')
748 g_free(nn->fullname);
749 nn->fullname = g_strdup(fullname);
753 nn->server = g_strdup(l->args[5]);
755 cs = find_channel(s, l->args[2]);
759 cn = find_channel_nick(cs, nn->nick);
762 network_state_log(LOG_WARNING,
764 "User %s in WHO reply not in expected channel %s!",
765 nn->nick, l->args[2]);
769 g_free(cn->last_flags);
770 cn->last_flags = g_strdup(l->args[7]);
772 cn->last_update = time(NULL);
775 static void handle_end_who(struct irc_network_state *s, const struct irc_line *l)
779 static void handle_nowaway(struct irc_network_state *s, const struct irc_line *l)
784 static void handle_unaway(struct irc_network_state *s, const struct irc_line *l)
789 static void handle_quit(struct irc_network_state *s, const struct irc_line *l)
792 struct network_nick *nn;
794 CHECK_ORIGIN(s, l, "QUIT");
796 nick = line_get_nick(l);
797 nn = find_network_nick(s, nick);
801 while (nn->channel_nicks) {
802 struct channel_nick *n = nn->channel_nicks->data;
803 free_channel_nick(n);
805 } else if (nn != NULL)
806 free_network_nick(s, nn);
809 gboolean modes_change_mode(irc_modes_t modes, gboolean set, char newmode)
811 if (modes[(unsigned char)newmode] == set)
814 modes[(unsigned char)newmode] = set;
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)
821 struct irc_network_info *info = s->info;
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);
827 if (mode == 'b') { /* Ban */
828 struct banlist_entry *be;
830 if (opt_arg == NULL) {
831 network_state_log(LOG_WARNING, s, "Missing argument for ban MODE set/unset");
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);
842 be = find_banlist_entry(c->banlist, opt_arg);
844 network_state_log(LOG_WARNING, s, "Unable to remove nonpresent banlist entry '%s'", opt_arg);
847 c->banlist = g_list_remove(c->banlist, be);
848 free_banlist_entry(be);
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");
858 c->exceptlist = g_list_append(c->exceptlist, g_strdup(opt_arg));
860 char *be = find_exceptlist_entry(c->banlist, opt_arg);
862 network_state_log(LOG_WARNING, s, "Unable to remove nonpresent ban except list entry '%s'", opt_arg);
865 c->banlist = g_list_remove(c->exceptlist, be);
869 } else if (mode == 'l') { /* Limit */
870 modes_change_mode(c->modes, set, 'l');
873 network_state_log(LOG_WARNING, s, "Mode +l requires argument, but no argument found");
876 c->limit = atol(opt_arg);
881 } else if (mode == 'k') {
882 modes_change_mode(c->modes, set, 'k');
884 if (opt_arg == NULL) {
885 network_state_log(LOG_WARNING, s, "Mode k requires argument, but no argument found");
890 c->key = g_strdup(opt_arg);
896 } else if (is_prefix_mode(info, mode)) {
897 struct channel_nick *n;
899 if (opt_arg == NULL) {
900 network_state_log(LOG_WARNING, s, "Mode %c requires nick argument, but no argument found", mode);
904 n = find_channel_nick(c, opt_arg);
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);
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);
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);
920 modes_change_mode(c->modes, set, mode);
925 static void handle_mode(struct irc_network_state *s, const struct irc_line *l)
928 * MODE %|<nick>|<channel> [<mode> [<mode parameters>]] */
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;
943 network_state_log(LOG_WARNING, s,
944 "Unable to change mode for unknown channel '%s'", l->args[1]);
948 by_name = line_get_nick(l);
949 by = find_network_nick(s, by_name);
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;
957 ret = channel_state_change_mode(s, by, c, t,
969 struct network_nick *nn = find_add_network_nick(s, l->args[1]);
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;
976 modes_change_mode(nn->modes, t, l->args[2][i]);
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],
990 static void handle_001(struct irc_network_state *s, const struct irc_line *l)
993 s->me.nick = g_strdup(l->args[1]);
996 static void handle_004(struct irc_network_state *s, const struct irc_line *l)
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]);
1003 static void handle_privmsg(struct irc_network_state *s, const struct irc_line *l)
1005 struct network_nick *nn;
1008 CHECK_ORIGIN(s,l,"PRIVMSG");
1010 if (irccmp(s->info, l->args[1], s->me.nick) != 0) return;
1012 nick = line_get_nick(l);
1013 nn = find_add_network_nick(s, nick);
1018 static void handle_nick(struct irc_network_state *s, const struct irc_line *l)
1020 struct network_nick *nn;
1023 CHECK_ORIGIN(s,l,"NICK");
1025 nick = line_get_nick(l);
1026 nn = find_add_network_nick(s, nick);
1028 network_nick_set_nick(nn, l->args[1]);
1031 static void handle_umodeis(struct irc_network_state *s, const struct irc_line *l)
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;
1040 static void handle_324(struct irc_network_state *s, const struct irc_line *l)
1042 struct irc_channel_state *ch = find_channel(s, l->args[2]);
1045 network_state_log(LOG_WARNING, s,
1046 "Can't store modes for %s: channel not found", l->args[2]);
1050 string2mode(l->args[3], ch->modes);
1052 ch->mode_received = TRUE;
1055 static void handle_329(struct irc_network_state *s, const struct irc_line *l)
1057 struct irc_channel_state *ch = find_channel(s, l->args[2]);
1060 network_state_log(LOG_WARNING, s,
1061 "Can't store creationtime for %s: channel not found", l->args[2]);
1065 ch->creation_time = atol(l->args[3]);
1068 static void handle_302(struct irc_network_state *s, const struct irc_line *l)
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) {
1077 struct network_nick *nn = find_add_network_nick(s, tmp302[0]);
1079 hm = g_strdup_printf("%s!%s", tmp302[0], tmp302[1]);
1080 network_nick_set_hostmask(nn, hm);
1089 extern void handle_005(struct irc_network_state *s, const struct irc_line *l);
1091 static struct irc_command {
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 },
1129 gboolean state_handle_data(struct irc_network_state *s, const struct irc_line *l)
1133 if (s == NULL || l == NULL || l->args == NULL || l->args[0] == NULL)
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)
1142 irc_commands[i].handler(s,l);
1150 struct irc_network_state *network_state_init(const char *nick,
1151 const char *username,
1152 const char *hostname)
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();
1162 void free_network_nick(struct irc_network_state *st, struct network_nick *nn)
1164 g_assert(nn != &st->me);
1167 /* No recursion please... */
1170 while (nn->channel_nicks) {
1171 struct channel_nick *n = nn->channel_nicks->data;
1172 free_channel_nick(n);
1175 g_free(nn->hostmask);
1176 g_free(nn->username);
1177 g_free(nn->hostname);
1178 g_free(nn->fullname);
1181 st->nicks = g_list_remove(st->nicks, nn);
1185 void free_network_state(struct irc_network_state *state)
1190 while (state->channels != NULL)
1191 free_channel((struct irc_channel_state *)state->channels->data);
1193 g_free(state->me.nick);
1194 g_free(state->me.username);
1195 g_free(state->me.hostname);
1196 g_free(state->me.hostmask);
1198 while (state->nicks != NULL)
1200 struct network_nick *nn = state->nicks->data;
1201 free_network_nick(state, nn);
1204 free_network_info(state->info);
1208 void network_state_log(enum log_level l,
1209 const struct irc_network_state *st, const char *fmt, ...)
1214 if (st->log == NULL)
1221 ret = g_strdup_vprintf(fmt, ap);
1224 st->log(l, st->userdata, ret);
1229 void network_state_set_log_fn(struct irc_network_state *st,
1230 void (*fn) (enum log_level, void *, const char *),
1234 st->userdata = userdata;
1237 void string2mode(const char *modes, irc_modes_t ar)
1239 memset(ar, 0, sizeof(ar));
1244 g_assert(modes[0] == '+');
1246 for (; *modes; modes++) {
1247 ar[(unsigned char)(*modes)] = 1;
1251 char *mode2string(irc_modes_t modes)
1257 for(i = 0; i < sizeof(modes); i++) {
1258 if (modes[i]) { ret[pos] = (char)i; pos++; }
1262 if (strlen(ret) == 0) {
1265 return g_strdup_printf("+%s", ret);
1269 gboolean is_prefix_mode(const struct irc_network_info *info, char mode)
1271 return get_prefix_by_mode(mode, info) != ' ';