/* channel_events.c: handle channel events * * Copyright (C) 2002, 2003, 2004 Eggheads Development Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef lint static const char rcsid[] = "$Id: channel_events.c,v 1.6 2008-10-17 15:57:43 sven Exp $"; #endif #include "server.h" static bind_list_t channel_raw_binds[]; /* Prototypes. */ static void clear_masklist(channel_mask_list_t *l); static int got_list_item(void *client_data, char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]); static int got_list_end(void *client_data, char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]); static void clear_masklists(channel_t *chan); void channel_events_init() { bind_table_t *table; bind_add_list("raw", channel_raw_binds); table = bind_table_lookup_or_fake("raw"); if (!table) return; /* FIXME Putting these here for now... but they should probably be * configurable or moved to an addon module/script. */ bind_entry_add(table, NULL, "367", "banlistitem", BIND_WANTS_CD, got_list_item, (void *)'b', NULL); bind_entry_add(table, NULL, "346", "invitelistitem", BIND_WANTS_CD, got_list_item, (void *)'I', NULL); bind_entry_add(table, NULL, "348", "exceptlistitem", BIND_WANTS_CD, got_list_item, (void *)'e', NULL); bind_entry_add(table, NULL, "368", "banlistend", BIND_WANTS_CD, got_list_end, (void *)'b', NULL); bind_entry_add(table, NULL, "347", "invitelistend", BIND_WANTS_CD, got_list_end, (void *)'I', NULL); bind_entry_add(table, NULL, "349", "exceptlistend", BIND_WANTS_CD, got_list_end, (void *)'e', NULL); } static inline void free_member(channel_member_t *m) { if (m->nick) free(m->nick); if (m->uhost) free(m->uhost); free(m); } static void clear_masklists(channel_t *chan) { int i; for (i = 0; i < chan->nlists; i++) { clear_masklist(chan->lists[i]); } if (chan->lists) free(chan->lists); chan->nlists = 0; chan->lists = NULL; } static void clear_masklist(channel_mask_list_t *l) { channel_mask_t *m, *next; for (m = l->head; m; m = next) { next = m->next; if (m->mask) free(m->mask); if (m->set_by) free(m->set_by); free(m); } memset(l, 0, sizeof(l)); } /* Free online data like nicklist, banmasks, etc.. */ void channel_free_online(channel_t *chan) { channel_member_t *m, *next_mem; int i; for (m = chan->member_head; m; m = next_mem) { next_mem = m->next; uhost_cache_decref(m->nick); free_member(m); } if (chan->topic) free(chan->topic); if (chan->topic_nick) free(chan->topic_nick); if (chan->key) free(chan->key); clear_masklists(chan); for (i = 0; i < chan->nargs; i++) { if (chan->args[i].value) free(chan->args[i].value); } if (chan->args) free(chan->args); chan->topic = chan->topic_nick = chan->key = NULL; chan->args = NULL; chan->member_head = NULL; chan->status = chan->nargs = chan->nmembers = 0; chan->limit = -1; } void channel_events_destroy() { bind_rem_list("raw", channel_raw_binds); } static channel_member_t *channel_add_member(channel_t *chan, const char *nick, const char *uhost) { channel_member_t *m; /* See if this member is already added. */ for (m = chan->member_head; m; m = m->next) { if (!(current_server.strcmp)(m->nick, nick)) break; } /* Do we need to create a new member? */ if (!m) { m = calloc(1, sizeof(*m)); m->nick = strdup(nick); timer_get_now_sec(&m->join_time); m->next = chan->member_head; chan->member_head = m; chan->nmembers++; } /* Do we need to fill in the uhost? */ if (uhost && !m->uhost) { m->uhost = strdup(uhost); } uhost_cache_addref(nick, uhost); return(m); } channel_member_t *channel_lookup_member(channel_t *chan, const char *nick) { channel_member_t *m; for (m = chan->member_head; m; m = m->next) { if (!(current_server.strcmp)(m->nick, nick)) break; } return(m); } /* We just finished connecting to the server, so join our channels. */ void channel_on_connect() { channel_t *chan; char *key; int inactive; for (chan = channel_head; chan; chan = chan->next) { channel_get_int(chan, &inactive, "inactive", 0, NULL); if (inactive) continue; channel_get(chan, &key, "key", 0, NULL); if (key && strlen(key)) printserv(SERVER_NORMAL, "JOIN %s %s", chan->name, key); else printserv(SERVER_NORMAL, "JOIN %s", chan->name); } } void channel_on_join(const char *chan_name, const char *nick, const char *uhost) { channel_t *chan; channel_member_t *m; /* Lookup channel or create. */ chan = channel_probe(chan_name, 1); if (!chan) return; /* Add a new member. */ m = channel_add_member(chan, nick, uhost); /* Is it me? If so, request channel lists. */ if (match_my_nick(nick)) { int i; chan->bot = m; chan->status |= (CHANNEL_WHOLIST | CHANNEL_JOINED); printserv(SERVER_NORMAL, "WHO %s\r\n", chan_name); printserv(SERVER_NORMAL, "MODE %s\r\n", chan_name); for (i = 0; i < chan->nlists; i++) { chan->lists[i]->loading = 1; } printserv(SERVER_NORMAL, "MODE %s %s\r\n", chan_name, current_server.type1modes); } } void channel_on_leave(const char *chan_name, const char *nick, const char *uhost, user_t *u) { channel_t *chan; channel_member_t *prev = NULL, *m; chan = channel_probe(chan_name, 0); if (!chan) return; uhost_cache_decref(nick); for (m = chan->member_head; m; m = m->next) { if (!(current_server.strcmp)(m->nick, nick)) break; prev = m; } if (!m) return; if (prev) prev->next = m->next; else chan->member_head = m->next; free_member(m); chan->nmembers--; bind_check(BT_leave, u ? &u->settings[0].flags : NULL, chan_name, nick, uhost, u, chan_name); /* Are we the ones leaving? */ if (match_my_nick(nick)) { if (chan->flags & CHANNEL_STATIC) channel_free_online(chan); else channel_free(chan); } } void channel_on_quit(const char *nick, const char *uhost, user_t *u) { channel_t *chan; for (chan = channel_head; chan; chan = chan->next) { channel_on_leave(chan->name, nick, uhost, u); } } void channel_on_nick(const char *old_nick, const char *new_nick) { channel_t *chan; channel_member_t *m; uhost_cache_swap(old_nick, new_nick); for (chan = channel_head; chan; chan = chan->next) { for (m = chan->member_head; m; m = m->next) { if (!(current_server.strcmp)(m->nick, old_nick)) { str_redup(&m->nick, new_nick); break; } } } } /* :server 352 [*][@|+] : */ static int got352(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name, *nick, *uhost, *flags, *ptr, changestr[3]; int i; channel_member_t *m; channel_t *chan = NULL; chan_name = args[1]; chan = channel_probe(chan_name, 0); if (!chan) return(0); nick = args[5]; flags = args[6]; uhost = egg_mprintf("%s@%s", args[2], args[3]); m = channel_add_member(chan, nick, uhost); changestr[0] = '+'; changestr[2] = 0; flags++; while (*flags) { ptr = strchr(current_server.whoprefix, *flags); if (ptr) { i = ptr - current_server.whoprefix; changestr[1] = current_server.modeprefix[i]; flag_merge_str(&m->mode, changestr); } flags++; } free(uhost); return(0); } /* :server 315 :End of /WHO list */ static int got315(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name = args[1]; channel_t *chan = NULL; channel_probe(chan_name, 0); if (chan) chan->status &= ~CHANNEL_WHOLIST; return(0); } /* :server 353 = : ... */ static int got353(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name, *nick, *nicks, *flags, *ptr, *prefixptr, changestr[3]; int i; channel_member_t *m; channel_t *chan = NULL; chan_name = args[2]; chan = channel_probe(chan_name, 0); if (!chan) return(0); nicks = strdup(args[3]); ptr = nicks; changestr[0] = '+'; changestr[2] = 0; while (ptr && *ptr) { while (isspace(*ptr)) ptr++; flags = ptr; while (strchr(current_server.whoprefix, *ptr)) ptr++; nick = ptr; while (*ptr && !isspace(*ptr)) ptr++; if (*ptr) *ptr = 0; else ptr = NULL; m = channel_add_member(chan, nick, NULL); *nick = 0; while (*flags) { prefixptr = strchr(current_server.whoprefix, *flags); if (prefixptr) { i = prefixptr - current_server.whoprefix; changestr[1] = current_server.modeprefix[i]; flag_merge_str(&m->mode, changestr); } flags++; } if (ptr) ptr++; } free(nicks); return(0); } /* :server 366 :End of /NAMES list */ static int got366(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name = args[1]; channel_t *chan = NULL; chan = channel_probe(chan_name, 0); if (chan) chan->status &= ~CHANNEL_NAMESLIST; return(0); } /* :origin TOPIC :topic */ static int gottopic(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name = args[0]; char *topic = args[1]; channel_t *chan = NULL; chan = channel_probe(chan_name, 0); if (!chan) return(0); str_redup(&chan->topic, topic); if (chan->topic_nick) free(chan->topic_nick); if (from_uhost) chan->topic_nick = egg_mprintf("%s!%s", from_nick, from_uhost); else chan->topic_nick = strdup(from_nick); timer_get_now_sec(&chan->topic_time); return(0); } /* :server 331 :No topic is set */ static int got331(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name = args[1]; channel_t *chan = NULL; chan = channel_probe(chan_name, 0); if (!chan) return(0); str_redup(&chan->topic, ""); return(0); } /* :server 332 : */ static int got332(char *from_nick, char *from_uhost, user_t *u, char *cmd, int nargs, char *args[]) { char *chan_name = args[1]; char *topic = args[2]; channel_t *chan = NULL; chan = channel_probe(chan_name, 0); if (!chan) return(0); str_redup(&chan->topic, topic); return(0); } /* :server 333