/* * names.c: This here is used to maintain a list of all the people currently * on your channel. Seems to work * * Written By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2006 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" IRCII_RCSID("@(#)$eterna: names.c,v 1.109 2006/07/22 03:50:10 mrg Exp $"); #include "ircaux.h" #include "names.h" #include "window.h" #include "screen.h" #include "server.h" #include "lastlog.h" #include "list.h" #include "output.h" #include "notify.h" #include "vars.h" /* from names.h */ static u_char mode_str[] = MODE_STRING; static int same_channel(ChannelList *, u_char *); static void free_channel(ChannelList **); static void show_channel(ChannelList *); static void clear_channel(ChannelList *); static u_char *recreate_mode(ChannelList *); static int decifer_mode(u_char *, u_long *, int *, NickList **, u_char **); static int switch_channels_backend(ChannelList *); /* clear_channel: erases all entries in a nick list for the given channel */ static void clear_channel(ChannelList *chan) { NickList *tmp, *next; for (tmp = chan->nicks; tmp; tmp = next) { next = tmp->next; new_free(&(tmp->nick)); new_free(&tmp); } chan->nicks = (NickList *) 0; chan->status &= ~CHAN_NAMES; } /* * we need this to deal with !channels. */ static int same_channel(ChannelList *chan, u_char *channel) { size_t len, len2; /* take the easy way out */ if (*chan->channel != '!' && *channel != '!') return (!my_stricmp(chan->channel, channel)); /* * OK, so what we have is chan->channel = "!!foo" and channel = "!JUNKfoo". */ len = my_strlen(chan->channel); len2 = my_strlen(channel); /* bail out on known stuff */ if (len > len2) return (0); if (len == len2) return (!my_stricmp(chan->channel, channel)); /* * replace the channel name if we are the same! */ if (!my_stricmp(chan->channel + 2, channel + 2 + (len2 - len))) { malloc_strcpy(&chan->channel, channel); return 1; } return 0; } ChannelList * lookup_channel(u_char *channel, int server, int do_unlink) { ChannelList *chan, *last = (ChannelList *) 0; if (!channel || !*channel || (server == -1 && (server = primary_server) == -1)) return (ChannelList *) 0; chan = server_list[server].chan_list; while (chan) { if (chan->server == server && same_channel(chan, channel)) { if (do_unlink == CHAN_UNLINK) { if (last) last->next = chan->next; else server_list[server].chan_list = chan->next; } break; } last = chan; chan = chan->next; } return chan; } /* * add_channel: adds the named channel to the channel list. If the channel * is already in the list, its attributes are modified accordingly with the * connected and copy parameters. */ void add_channel(u_char *channel, u_char *key, int server, int connected, ChannelList *copy) { ChannelList *new; int do_add = 0; /* * avoid adding channel "0" */ if (channel[0] == '0' && channel[1] == '\0') return; if ((new = lookup_channel(channel, server, CHAN_NOUNLINK)) == (ChannelList *) 0) { new = (ChannelList *) new_malloc(sizeof(ChannelList)); new->channel = (u_char *) 0; new->status = 0; new->key = 0; if (key) malloc_strcpy(&new->key, key); new->nicks = (NickList *) 0; new->s_mode = empty_string; malloc_strcpy(&new->channel, channel); new->mode = 0; new->limit = 0; if ((new->window = is_bound(channel, server)) == (Window *) 0) new->window = curr_scr_win; do_add = 1; add_to_list((List **)(void *)&server_list[server].chan_list, (List *) new); } else { if (new->connected != CHAN_LIMBO && new->connected != CHAN_JOINING) yell("--- add_channel: add_channel found channel not CHAN_LIMBO/JOINING: %s", new->channel); } if (do_add || (connected == CHAN_JOINED)) { new->server = server; clear_channel(new); } if (copy) { new->mode = copy->mode; new->limit = copy->limit; new->window = copy->window; malloc_strcpy(&new->key, copy->key); } new->connected = connected; if ((connected == CHAN_JOINED) && !is_current_channel(channel, server, 0)) { int flag = 1; Window *tmp, *expected, *possible = (Window *) 0; expected = new->window; while ((tmp = traverse_all_windows(&flag))) { if (tmp->server == server) { if (tmp == expected) { set_channel_by_refnum(tmp->refnum, channel); new->window = tmp; update_all_status(); return; } else if (!possible) possible = tmp; } } if (possible) { set_channel_by_refnum(possible->refnum, channel); new->window = possible; update_all_status(); return; } set_channel_by_refnum(0, channel); new->window = curr_scr_win; } update_all_windows(); } void rename_channel(u_char *oldchan, u_char *newchan, int server) { ChannelList *new; if (!oldchan || !*oldchan || !newchan || !*newchan) return; new = lookup_channel(oldchan, server, CHAN_NOUNLINK); if (!new) return; malloc_strcpy(&new->channel, newchan); if (new->window) set_channel_by_refnum(new->window->refnum, newchan); /* are these necessary? */ update_all_status(); update_all_windows(); } /* * add_to_channel: adds the given nickname to the given channel. If the * nickname is already on the channel, nothing happens. If the channel is * not on the channel list, nothing happens (although perhaps the channel * should be addded to the list? but this should never happen) */ void add_to_channel(u_char *channel, u_char *nick, int server, int oper, int voice) { NickList *new; ChannelList *chan; int ischop = oper; int hasvoice = voice; if ((chan = lookup_channel(channel, server, CHAN_NOUNLINK))) { if (*nick == '+') { hasvoice = 1; nick++; } if (*nick == '@') { nick++; if (!my_stricmp(nick, get_server_nickname(server)) && !((chan->status & CHAN_NAMES) && (chan->status & CHAN_MODE))) { u_char *mode = recreate_mode(chan); if (*mode) { int old_server = from_server; from_server = server; send_to_server("MODE %s %s", chan->channel, mode); from_server = old_server; } chan->status |= CHAN_CHOP; } ischop = 1; } if ((new = (NickList *) remove_from_list((List **)(void *)&(chan->nicks), nick))) { new_free(&(new->nick)); new_free(&new); } new = (NickList *) new_malloc(sizeof(NickList)); new->nick = (u_char *) 0; new->chanop = ischop; new->hasvoice = hasvoice; malloc_strcpy(&(new->nick), nick); add_to_list((List **)(void *)&(chan->nicks), (List *) new); } notify_mark(nick, 1, 0); } /* * recreate_mode: converts the bitmap representation of a channels mode into * a string */ static u_char * recreate_mode(ChannelList *chan) { int mode_pos = 0, mode, showkey; static u_char *s; u_char buffer[BIG_BUFFER_SIZE], modes[33], limit[33]; limit[0] = '\0'; s = modes; mode = chan->mode; while (mode) { if (mode % 2) *s++ = mode_str[mode_pos]; mode /= 2; mode_pos++; } *s = '\0'; showkey = chan->key && *chan->key && !get_int_var(HIDE_CHANNEL_KEYS_VAR); if (chan->limit) snprintf(CP(limit), sizeof limit, " %d", chan->limit); snprintf(CP(buffer), sizeof buffer, "%s%s%s%s", modes, showkey ? UP(" ") : empty_string, showkey ? chan->key : empty_string, limit); malloc_strcpy(&chan->s_mode, buffer); return chan->s_mode; } /* * decifer_mode: This will figure out the mode string as returned by mode * commands and convert that mode string into a one byte bit map of modes */ static int decifer_mode(u_char *mode_string, u_long *mode, int *chop, NickList **nicks, u_char **key) { u_char *limit = 0; u_char *person; int add = 0; int limit_set = 0; int limit_reset = 0; u_char *rest, *the_key; NickList *ThisNick; u_long value = 0; if (!(mode_string = next_arg(mode_string, &rest))) return -1; for (; *mode_string; mode_string++) { switch (*mode_string) { case '+': add = 1; value = 0; break; case '-': add = 0; value = 0; break; case 'a': value = MODE_ANONYMOUS; break; case 'c': value = MODE_COLOURLESS; break; case 'i': value = MODE_INVITE; break; case 'k': value = MODE_KEY; the_key = next_arg(rest, &rest); if (add) malloc_strcpy(key, the_key); else new_free(key); break; case 'l': value = MODE_LIMIT; if (add) { limit_set = 1; if (!(limit = next_arg(rest, &rest))) limit = empty_string; else if (0 == my_strncmp(limit, zero, 1)) limit_reset = 1, limit_set = 0, add = 0; } else limit_reset = 1; break; case 'm': value = MODE_MODERATED; break; case 'o': if ((person = next_arg(rest, &rest)) && !my_stricmp(person, get_server_nickname(from_server))) { if (add) *chop |= CHAN_CHOP; else *chop &= ~CHAN_CHOP; } ThisNick = (NickList *) list_lookup((List **)(void *)nicks, person, !USE_WILDCARDS, !REMOVE_FROM_LIST); if (ThisNick) ThisNick->chanop = add; break; case 'n': value = MODE_MSGS; break; case 'p': value = MODE_PRIVATE; break; case 'q': value = MODE_QUIET; break; case 'r': value = MODE_REOP; break; case 's': value = MODE_SECRET; break; case 't': value = MODE_TOPIC; break; case 'v': person = next_arg(rest, &rest); ThisNick = (NickList *) list_lookup((List **) nicks, person, !USE_WILDCARDS, !REMOVE_FROM_LIST); if (ThisNick) ThisNick->hasvoice = add; break; case 'b': case 'e': case 'I': case 'O': /* this is a weird special case */ (void) next_arg(rest, &rest); break; case 'R': value = MODE_REGONLY; break; } if (add) *mode |= value; else *mode &= ~value; } if (limit_set) return (my_atoi(limit)); else if (limit_reset) return(0); else return(-1); } /* * get_channel_mode: returns the current mode string for the given channel */ u_char * get_channel_mode(u_char *channel, int server) { ChannelList *tmp; if ((tmp = lookup_channel(channel, server, CHAN_NOUNLINK)) && (tmp->status & CHAN_MODE)) return recreate_mode(tmp); return empty_string; } /* * update_channel_mode: This will modify the mode for the given channel * according the the new mode given. */ void update_channel_mode(u_char *channel, int server, u_char *mode) { ChannelList *tmp; int limit; if ((tmp = lookup_channel(channel, server, CHAN_NOUNLINK)) && (limit = decifer_mode(mode, &(tmp->mode), &(tmp->status), &(tmp->nicks), &(tmp->key))) != -1) tmp->limit = limit; } /* * is_channel_mode: returns the logical AND of the given mode with the * channels mode. Useful for testing a channels mode */ int is_channel_mode(u_char *channel, int mode, int server_index) { ChannelList *tmp; if ((tmp = lookup_channel(channel, server_index, CHAN_NOUNLINK))) return (tmp->mode & mode); return 0; } static void free_channel(ChannelList **channel) { clear_channel(*channel); new_free(&(*channel)->channel); new_free(&(*channel)->key); new_free(&(*channel)->s_mode); new_free(&(*channel)); } /* * remove_channel: removes the named channel from the * server_list[server].chan_list. If the channel is not on the * server_list[server].chan_list, nothing happens. If the channel was * the current channel, this will select the top of the * server_list[server].chan_list to be the current_channel, or 0 if the * list is empty. */ void remove_channel(u_char *channel, int server) { ChannelList *tmp; if (channel) { int refnum = -1; if ((tmp = lookup_channel(channel, server, CHAN_NOUNLINK))) { tmp = lookup_channel(channel, server, CHAN_UNLINK); if (tmp->window) refnum = tmp->window->refnum; free_channel(&tmp); } (void)is_current_channel(channel, server, refnum); } else { ChannelList *next; for (tmp = server_list[server].chan_list; tmp; tmp = next) { next = tmp->next; free_channel(&tmp); } server_list[server].chan_list = (ChannelList *) 0; } update_all_windows(); } /* * remove_from_channel: removes the given nickname from the given channel. If * the nickname is not on the channel or the channel doesn't exist, nothing * happens. */ void remove_from_channel(u_char *channel, u_char *nick, int server) { ChannelList *chan; NickList *tmp; if (channel) { if ((chan = lookup_channel(channel, server, CHAN_NOUNLINK))) { if ((tmp = (NickList *) list_lookup((List **)(void *)&(chan->nicks), nick, !USE_WILDCARDS, REMOVE_FROM_LIST))) { new_free(&(tmp->nick)); new_free(&tmp); } } } else { for (chan = server_list[server].chan_list; chan; chan = chan->next) { if ((tmp = (NickList *) list_lookup((List **)(void *)&(chan->nicks), nick, !USE_WILDCARDS, REMOVE_FROM_LIST))) { new_free(&(tmp->nick)); new_free(&tmp); } } } } /* * rename_nick: in response to a changed nickname, this looks up the given * nickname on all you channels and changes it the new_nick */ void rename_nick(u_char *old_nick, u_char *new_nick, int server) { ChannelList *chan; NickList *tmp; for (chan = server_list[server].chan_list; chan; chan = chan->next) { if ((chan->server == server) != 0) { if ((tmp = (NickList *) list_lookup((List **)(void *)&chan->nicks, old_nick, !USE_WILDCARDS, !REMOVE_FROM_LIST))) { new_free(&tmp->nick); malloc_strcpy(&tmp->nick, new_nick); } } } } /* * is_on_channel: returns true if the given nickname is in the given channel, * false otherwise. Also returns false if the given channel is not on the * channel list. */ int is_on_channel(u_char *channel, int server, u_char *nick) { ChannelList *chan; chan = lookup_channel(channel, server, CHAN_NOUNLINK); if (chan && (chan->connected == CHAN_JOINED) && list_lookup((List **)(void *)&(chan->nicks), nick, !USE_WILDCARDS, !REMOVE_FROM_LIST)) return 1; return 0; } int is_chanop(u_char *channel, u_char *nick) { ChannelList *chan; NickList *Nick; if ((chan = lookup_channel(channel, from_server, CHAN_NOUNLINK)) && (chan->connected == CHAN_JOINED) && /* channel may be "surviving" from a disconnect/connect check here too -Sol */ (Nick = (NickList *) list_lookup((List **)(void *)&(chan->nicks), nick, !USE_WILDCARDS, !REMOVE_FROM_LIST)) && Nick->chanop) return 1; return 0; } int has_voice(u_char *channel, u_char *nick, int server) { ChannelList *chan; NickList *Nick; if (channel && *channel && (chan = lookup_channel(channel, server, CHAN_NOUNLINK)) && chan->connected == CHAN_JOINED && /* channel may be "surviving" from a disconnect/connect check here too -Sol */ (Nick = (NickList *) list_lookup((List **)(void *)&(chan->nicks), nick, !USE_WILDCARDS, !REMOVE_FROM_LIST)) && (Nick->chanop || Nick->hasvoice)) return 1; return 0; } static void show_channel(ChannelList *chan) { NickList *tmp; int buffer_len, len; u_char *nicks = (u_char *) 0; u_char *s; u_char buffer[BIG_BUFFER_SIZE]; s = recreate_mode(chan); *buffer = (u_char) 0; buffer_len = 0; for (tmp = chan->nicks; tmp; tmp = tmp->next) { len = my_strlen(tmp->nick); if (buffer_len + len >= (sizeof(buffer) / 2)) { malloc_strcpy(&nicks, buffer); say("\t%s +%s (%s): %s", chan->channel, s, get_server_name(chan->server), nicks); *buffer = (u_char) 0; buffer_len = 0; } my_strmcat(buffer, tmp->nick, sizeof buffer); my_strmcat(buffer, " ", sizeof buffer); buffer_len += len + 1; } malloc_strcpy(&nicks, buffer); say("\t%s +%s (%s): %s", chan->channel, s, get_server_name(chan->server), nicks); new_free(&nicks); } /* list_channels: displays your current channel and your channel list */ void list_channels(void) { ChannelList *tmp; int server, no = 1; int first; if (connected_to_server < 1) { say("You are not connected to a server, use /SERVER to connect."); return; } if (get_channel_by_refnum(0)) say("Current channel %s", get_channel_by_refnum(0)); else say("No current channel for this window"); first = 1; for (tmp = server_list[get_window_server(0)].chan_list; tmp; tmp = tmp->next) { if (tmp->connected != CHAN_JOINED) continue; if (first) say("You are on the following channels:"); show_channel(tmp); first = 0; no = 0; } if (connected_to_server > 1) { for (server = 0; server < number_of_servers; server++) { if (server == get_window_server(0)) continue; first = 1; for (tmp = server_list[server].chan_list; tmp; tmp = tmp->next) { if (tmp->connected != CHAN_JOINED) continue; if (first) say("Other servers:"); show_channel(tmp); first = 0; no = 0; } } } if (no) say("You are not on any channels"); } /* * backend of switch_channels(). */ static int switch_channels_backend(ChannelList * chan) { u_char *newchan; for (; chan; chan = chan->next) { newchan = chan->channel; if (!is_current_channel(newchan, from_server, 0) && !(is_bound(newchan, from_server) && curr_scr_win != chan->window) && (get_int_var(SWITCH_TO_QUIET_CHANNELS) || !(chan->mode & MODE_QUIET))) { set_channel_by_refnum(0, newchan); update_all_windows(); return 1; } } return 0; } void switch_channels(u_int key, u_char *ptr) { ChannelList * tmp; if (server_list[from_server].chan_list && ((get_channel_by_refnum(0) && (tmp = lookup_channel(get_channel_by_refnum(0), from_server, CHAN_NOUNLINK)) && switch_channels_backend(tmp->next)) || switch_channels_backend(server_list[from_server].chan_list))) return; } void change_server_channels(int old, int new) { ChannelList *tmp; if (new == old) return; if (old > -1) { for (tmp = server_list[old].chan_list; tmp ;tmp = tmp->next) tmp->server = new; server_list[new].chan_list = server_list[old].chan_list; } else server_list[new].chan_list = (ChannelList *) 0; } void clear_channel_list(int server) { ChannelList *tmp, *next; Window *ptr; int flag = 1; while ((ptr = traverse_all_windows(&flag))) if (ptr->server == server && ptr->current_channel) new_free(&ptr->current_channel); for (tmp = server_list[server].chan_list; tmp; tmp = next) { next = tmp->next; free_channel(&tmp); } server_list[server].chan_list = (ChannelList *) 0; return; } /* * reconnect_all_channels: used after you get disconnected from a server, * clear each channel nickname list and re-JOINs each channel in the * channel_list .. */ void reconnect_all_channels(int server) { ChannelList *tmp = (ChannelList *) 0; u_char *mode, *chan; for (tmp = server_list[server].chan_list; tmp; tmp = tmp->next) { mode = recreate_mode(tmp); chan = tmp->channel; if (get_server_version(server) >= Server2_8) send_to_server("JOIN %s%s%s", tmp->channel, tmp->key ? " " : "", tmp->key ? tmp->key : (u_char *) ""); else send_to_server("JOIN %s%s%s", tmp->channel, mode ? " " : "", mode ? mode : (u_char *) ""); tmp->connected = CHAN_JOINING; } } u_char * what_channel(u_char *nick, int server) { ChannelList *tmp; if (curr_scr_win->current_channel && is_on_channel(curr_scr_win->current_channel, curr_scr_win->server, nick)) return curr_scr_win->current_channel; for (tmp = server_list[from_server].chan_list; tmp; tmp = tmp->next) if (list_lookup((List **)(void *)&(tmp->nicks), nick, !USE_WILDCARDS, !REMOVE_FROM_LIST)) return tmp->channel; return (u_char *) 0; } u_char * walk_channels(u_char *nick, int init, int server) { static ChannelList *tmp = (ChannelList *) 0; if (server < 0) return (u_char *) 0; if (init) tmp = server_list[server].chan_list; else if (tmp) tmp = tmp->next; for (;tmp ; tmp = tmp->next) if ((tmp->server == from_server) && (list_lookup((List **)(void *)&(tmp->nicks), nick, !USE_WILDCARDS, !REMOVE_FROM_LIST))) return (tmp->channel); return (u_char *) 0; } int get_channel_oper(u_char *channel, int server) { ChannelList *chan; if ((chan = lookup_channel(channel, server, CHAN_NOUNLINK))) return (chan->status & CHAN_CHOP); else return 1; } extern void set_channel_window(Window *window, u_char *channel, int server) { ChannelList *tmp; if (!channel || server < 0) return; for (tmp = server_list[server].chan_list; tmp; tmp = tmp->next) if (!my_stricmp(channel, tmp->channel)) { tmp->window = window; return; } } extern u_char * create_channel_list(Window *window) { ChannelList *tmp; u_char *value = (u_char *) 0; u_char buffer[BIG_BUFFER_SIZE]; *buffer = '\0'; if (window->server >= 0) for (tmp = server_list[window->server].chan_list; tmp; tmp = tmp->next) { my_strmcat(buffer, tmp->channel, sizeof buffer); my_strmcat(buffer, " ", sizeof buffer); } malloc_strcpy(&value, buffer); return value; } extern void channel_server_delete(int server) { ChannelList *tmp; int i; for (i = server + 1; i < number_of_servers; i++) for (tmp = server_list[i].chan_list ; tmp; tmp = tmp->next) if (tmp->server >= server) tmp->server--; } extern int chan_is_connected(u_char *channel, int server) { ChannelList * cp = lookup_channel(channel, server, CHAN_NOUNLINK); if (!cp) return 0; return (cp->connected == CHAN_JOINED); } void mark_not_connected(int server) { ChannelList *tmp; if (server >= 0) for (tmp = server_list[server].chan_list; tmp; tmp = tmp->next) { tmp->status = 0; tmp->connected = CHAN_LIMBO; } }