/*
 * 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;
		}
}


syntax highlighted by Code2HTML, v. 0.9.1