From: Florian Westphal <fw@strlen.de>
Date: Wed, 27 Feb 2008 00:03:02 +0000 (+0100)
Subject: hash table implementation. replace several linked lists with hash tables.
X-Git-Url: http://git.breakpoint.cc/cgi-bin/gitweb.cgi?p=fw%2Fngircd-fw.git;a=commitdiff_plain;h=d120325769a8a41c903b8a125b8501f8338bb3a4

hash table implementation. replace several linked lists with hash tables.

FIXME: hash tables should be re-done with a power-of-two
size; jenkins hash already provides a good distribution; no need to
use modulo-by-prime.
---

diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am
index 1a5119f..80d4b79 100644
--- a/src/ngircd/Makefile.am
+++ b/src/ngircd/Makefile.am
@@ -20,7 +20,7 @@ LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
 
 sbin_PROGRAMS = ngircd
 
-ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \
+ngircd_SOURCES = ngircd.c array.c channel.c cl2chan.c client.c conf.c conn.c conn-func.c \
 	conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \
 	irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \
 	match.c numeric.c parse.c rendezvous.c resolve.c
@@ -29,7 +29,7 @@ ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
 
-noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conn.h conn-func.h \
+noinst_HEADERS = ngircd.h array.h channel.h cl2chan.h client.h conf.h conn.h conn-func.h \
 	conn-zip.h hash.h io.h irc.h irc-channel.h irc-info.h irc-login.h \
 	irc-mode.h irc-op.h irc-oper.h irc-server.h irc-write.h lists.h log.h \
 	match.h numeric.h parse.h rendezvous.h resolve.h \
diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c
index 5caf7d0..30aab85 100644
--- a/src/ngircd/channel.c
+++ b/src/ngircd/channel.c
@@ -41,32 +41,50 @@ static char UNUSED id[] = "$Id: channel.c,v 1.65 2008/02/05 16:31:35 fw Exp $";
 #include "lists.h"
 #include "log.h"
 #include "messages.h"
+#include "cl2chan.h"
 
 #include "exp.h"
 
-
 #define REMOVE_PART 0
 #define REMOVE_QUIT 1
 #define REMOVE_KICK 2
 
+static hashtable My_Channels;	/* get channel struct by Channel Name */
 
-static CHANNEL *My_Channels;
-static CL2CHAN *My_Cl2Chan;
-
-
-static CL2CHAN *Get_Cl2Chan PARAMS(( CHANNEL *Chan, CLIENT *Client ));
-static CL2CHAN *Add_Client PARAMS(( CHANNEL *Chan, CLIENT *Client ));
 static bool Remove_Client PARAMS(( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const char *Reason, bool InformServer ));
-static CL2CHAN *Get_First_Cl2Chan PARAMS(( CLIENT *Client, CHANNEL *Chan ));
-static CL2CHAN *Get_Next_Cl2Chan PARAMS(( CL2CHAN *Start, CLIENT *Client, CHANNEL *Chan ));
 static bool Delete_Channel PARAMS(( CHANNEL *Chan ));
 
+static void channelfree(void *p)
+{
+	CHANNEL *c = p;
+	LogDebug("Freeing channel structure for \"%s\".", c->name);
+	array_free(&c->topic);
+	Lists_Free(&c->list_bans);
+	Lists_Free(&c->list_invites);
+	assert(hashtable_getfillcount(&c->members) == 0);
+	hashtable_free(&c->members);
+	free(c);
+}
+
+static int channelcmp(const void *k, const void *candidate)
+{
+	return strcmp(k, candidate);
+}
+
+
+static size_t channelhash(const void *k)
+{
+	return Hash(k);
+}
+
 
 GLOBAL void
 Channel_Init( void )
 {
-	My_Channels = NULL;
-	My_Cl2Chan = NULL;
+	if (!hashtable_init(&My_Channels, channelfree, channelcmp, channelhash)) {
+		Log(LOG_ALERT, "Cannot allocate Channel Table!");
+		exit(1);
+	}
 } /* Channel_Init */
 
 
@@ -109,8 +127,8 @@ Channel_InitPredefined( void )
 		}
 
 		/* Check if the channel name is already in use */
-		chan = Channel_Search( Conf_Channel[i].name );
-		if( chan )
+		chan = hashtable_get(&My_Channels, Conf_Channel[i].name);
+		if ( chan )
 		{
 			Log( LOG_INFO, "Can't create pre-defined channel \"%s\": name already in use.", Conf_Channel[i].name );
 			array_free(&Conf_Channel[i].topic);
@@ -139,6 +157,7 @@ Channel_InitPredefined( void )
 		}
 		else Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!",
 							Conf_Channel[i].name );
+		array_free(&Conf_Channel[i].topic);
 	}
 } /* Channel_InitPredefined */
 
@@ -146,27 +165,7 @@ Channel_InitPredefined( void )
 GLOBAL void
 Channel_Exit( void )
 {
-	CHANNEL *c, *c_next;
-	CL2CHAN *cl2chan, *cl2chan_next;
-
-	/* free struct Channel */
-	c = My_Channels;
-	while( c )
-	{
-		c_next = c->next;
-		array_free(&c->topic);
-		free( c );
-		c = c_next;
-	}
-
-	/* Free Channel allocation table */
-	cl2chan = My_Cl2Chan;
-	while( c )
-	{
-		cl2chan_next = cl2chan->next;
-		free( cl2chan );
-		cl2chan = cl2chan_next;
-	}
+	hashtable_free(&My_Channels);
 } /* Channel_Exit */
 
 
@@ -193,20 +192,12 @@ Channel_Join( CLIENT *Client, char *Name )
 	}
 
 	chan = Channel_Search( Name );
-	if( chan ) {
-		/* Check if the client is already in the channel */
-		if( Get_Cl2Chan( chan, Client )) return false;
-	}
-	else
-	{
-		/* If the specified channel doesn't exist, the channel is created */
+	if(!chan) {
+		/* create channel */
 		chan = Channel_Create( Name );
 		if (!chan) return false;
 	}
-
-	/* Add user to Channel */
-	if( ! Add_Client( chan, Client )) return false;
-	else return true;
+	return Cl2Chan_Join( chan, Client );
 } /* Channel_Join */
 
 
@@ -226,26 +217,14 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea
 	assert(Name != NULL);
 	assert(Reason != NULL);
 
-	/* Check that specified channel exists */
-	chan = Channel_Search(Name);
-	if (!chan) {
-		IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG,
-				   Client_ID(Client), Name);
-		return false;
-	}
-
-	/* Check that the client is in the channel */
-	if (!Get_Cl2Chan(chan, Client)) {
-		IRC_WriteStrClient(Client, ERR_NOTONCHANNEL_MSG,
-				   Client_ID(Client), Name);
+	chan = Channel_Search( Name );
+	if(( ! chan ) || ( ! Cl2Chan_Get( chan, Client )))
+	{
+		IRC_WriteStrClient( Client, ERR_NOSUCHCHANNEL_MSG, Client_ID( Client ), Name );
 		return false;
 	}
 
-	/* Part client from channel */
-	if (!Remove_Client(REMOVE_PART, chan, Client, Origin, Reason, true))
-		return false;
-	else
-		return true;
+	return Remove_Client( REMOVE_PART, chan, Client, Origin, Reason, true);
 } /* Channel_Part */
 
 
@@ -297,78 +276,37 @@ Channel_Kick( CLIENT *Client, CLIENT *Origin, const char *Name, const char *Reas
 GLOBAL void
 Channel_Quit( CLIENT *Client, char *Reason )
 {
-	CHANNEL *c, *next_c;
+	CHANNEL *c;
 
 	assert( Client != NULL );
 	assert( Reason != NULL );
 
 	IRC_WriteStrRelatedPrefix( Client, Client, false, "QUIT :%s", Reason );
 
-	c = My_Channels;
-	while( c )
-	{
-		next_c = c->next;
+	hashtable_iterator_reset(&My_Channels);
+	while ((c = hashtable_iterator_getnext(&My_Channels)))
 		Remove_Client( REMOVE_QUIT, c, Client, Client, Reason, false );
-		c = next_c;
-	}
+
+	hashtable_iterator_reset(&My_Channels);
 } /* Channel_Quit */
 
 
 GLOBAL unsigned long
 Channel_Count( void )
 {
-	CHANNEL *c;
-	unsigned long count = 0;
-
-	c = My_Channels;
-	while( c )
-	{
-		count++;
-		c = c->next;
-	}
-	return count;
+	return (unsigned long) hashtable_getfillcount(&My_Channels);
 } /* Channel_Count */
 
 
 GLOBAL unsigned long
 Channel_MemberCount( CHANNEL *Chan )
 {
-	CL2CHAN *cl2chan;
-	unsigned long count = 0;
-
 	assert( Chan != NULL );
 
-	cl2chan = My_Cl2Chan;
-	while( cl2chan )
-	{
-		if( cl2chan->channel == Chan ) count++;
-		cl2chan = cl2chan->next;
-	}
-	return count;
+	return (unsigned long) hashtable_getfillcount(&Chan->members);
 } /* Channel_MemberCount */
 
 
-GLOBAL int
-Channel_CountForUser( CLIENT *Client )
-{
-	/* Count number of channels a user is member of. */
-
-	CL2CHAN *cl2chan;
-	int count = 0;
-
-	assert( Client != NULL );
-
-	cl2chan = My_Cl2Chan;
-	while( cl2chan )
-	{
-		if( cl2chan->client == Client ) count++;
-		cl2chan = cl2chan->next;
-	}
-
-	return count;
-} /* Channel_CountForUser */
-
-
 GLOBAL const char *
 Channel_Name( const CHANNEL *Chan )
 {
@@ -404,91 +342,23 @@ Channel_MaxUsers( CHANNEL *Chan )
 GLOBAL CHANNEL *
 Channel_First( void )
 {
-	return My_Channels;
+	hashtable_iterator_reset(&My_Channels);
+	return hashtable_iterator_getnext(&My_Channels);
 } /* Channel_First */
 
 
 GLOBAL CHANNEL *
-Channel_Next( CHANNEL *Chan )
+Channel_Next( void )
 {
-	assert( Chan != NULL );
-	return Chan->next;
+	return hashtable_iterator_getnext(&My_Channels);
 } /* Channel_Next */
 
 
 GLOBAL CHANNEL *
 Channel_Search( const char *Name )
 {
-	/* Channel-Struktur suchen */
-
-	CHANNEL *c;
-	UINT32 search_hash;
-
-	assert( Name != NULL );
-
-	search_hash = Hash( Name );
-	c = My_Channels;
-	while( c )
-	{
-		if( search_hash == c->hash )
-		{
-			/* lt. Hash-Wert: Treffer! */
-			if( strcasecmp( Name, c->name ) == 0 ) return c;
-		}
-		c = c->next;
-	}
-	return NULL;
-} /* Channel_Search */
-
-
-GLOBAL CL2CHAN *
-Channel_FirstMember( CHANNEL *Chan )
-{
-	assert( Chan != NULL );
-	return Get_First_Cl2Chan( NULL, Chan );
-} /* Channel_FirstMember */
-
-
-GLOBAL CL2CHAN *
-Channel_NextMember( CHANNEL *Chan, CL2CHAN *Cl2Chan )
-{
-	assert( Chan != NULL );
-	assert( Cl2Chan != NULL );
-	return Get_Next_Cl2Chan( Cl2Chan->next, NULL, Chan );
-} /* Channel_NextMember */
-
-
-GLOBAL CL2CHAN *
-Channel_FirstChannelOf( CLIENT *Client )
-{
-	assert( Client != NULL );
-	return Get_First_Cl2Chan( Client, NULL );
-} /* Channel_FirstChannelOf */
-
-
-GLOBAL CL2CHAN *
-Channel_NextChannelOf( CLIENT *Client, CL2CHAN *Cl2Chan )
-{
-	assert( Client != NULL );
-	assert( Cl2Chan != NULL );
-	return Get_Next_Cl2Chan( Cl2Chan->next, Client, NULL );
-} /* Channel_NextChannelOf */
-
-
-GLOBAL CLIENT *
-Channel_GetClient( CL2CHAN *Cl2Chan )
-{
-	assert( Cl2Chan != NULL );
-	return Cl2Chan->client;
-} /* Channel_GetClient */
-
-
-GLOBAL CHANNEL *
-Channel_GetChannel( CL2CHAN *Cl2Chan )
-{
-	assert( Cl2Chan != NULL );
-	return Cl2Chan->channel;
-} /* Channel_GetChannel */
+	return hashtable_get(&My_Channels, Name);
+}
 
 
 GLOBAL bool
@@ -566,7 +436,7 @@ Channel_UserModeAdd( CHANNEL *Chan, CLIENT *Client, char Mode )
 	assert( Chan != NULL );
 	assert( Client != NULL );
 
-	cl2chan = Get_Cl2Chan( Chan, Client );
+	cl2chan = Cl2Chan_Get( Chan, Client );
 	assert( cl2chan != NULL );
 
 	x[0] = Mode; x[1] = '\0';
@@ -594,7 +464,7 @@ Channel_UserModeDel( CHANNEL *Chan, CLIENT *Client, char Mode )
 	assert( Chan != NULL );
 	assert( Client != NULL );
 
-	cl2chan = Get_Cl2Chan( Chan, Client );
+	cl2chan = Cl2Chan_Get(Chan, Client);
 	assert( cl2chan != NULL );
 
 	p = strchr( cl2chan->modes, Mode );
@@ -620,7 +490,7 @@ Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
 	assert( Chan != NULL );
 	assert( Client != NULL );
 
-	cl2chan = Get_Cl2Chan( Chan, Client );
+	cl2chan = Cl2Chan_Get(Chan, Client);
 	assert( cl2chan != NULL );
 
 	return cl2chan->modes;
@@ -634,7 +504,7 @@ Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
 
 	assert( Chan != NULL );
 	assert( Client != NULL );
-	return Get_Cl2Chan(Chan, Client) != NULL;
+	return Cl2Chan_Get(Chan, Client);
 } /* Channel_IsMemberOf */
 
 
@@ -792,7 +662,7 @@ Channel_Notice(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Text)
 GLOBAL CHANNEL *
 Channel_Create( char *Name )
 {
-	/* Create new CHANNEL structure and add it to linked list */
+	/* Create new CHANNEL structure and add it to hash table */
 	CHANNEL *c;
 
 	assert( Name != NULL );
@@ -803,91 +673,36 @@ Channel_Create( char *Name )
 		Log( LOG_EMERG, "Can't allocate memory! [New_Chan]" );
 		return NULL;
 	}
-	memset( c, 0, sizeof( CHANNEL ));
-	strlcpy( c->name, Name, sizeof( c->name ));
-	c->hash = Hash( c->name );
-	c->next = My_Channels;
-	My_Channels = c;
-	LogDebug("Created new channel structure for \"%s\".", Name);
-	return c;
-} /* Channel_Create */
-
 
-static CL2CHAN *
-Get_Cl2Chan( CHANNEL *Chan, CLIENT *Client )
-{
-	CL2CHAN *cl2chan;
-
-	assert( Chan != NULL );
-	assert( Client != NULL );
-
-	cl2chan = My_Cl2Chan;
-	while( cl2chan )
-	{
-		if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) return cl2chan;
-		cl2chan = cl2chan->next;
-	}
-	return NULL;
-} /* Get_Cl2Chan */
-
-
-static CL2CHAN *
-Add_Client( CHANNEL *Chan, CLIENT *Client )
-{
-	CL2CHAN *cl2chan;
-
-	assert( Chan != NULL );
-	assert( Client != NULL );
-
-	/* neue CL2CHAN-Struktur anlegen */
-	cl2chan = (CL2CHAN *)malloc( sizeof( CL2CHAN ));
-	if( ! cl2chan )
-	{
-		Log( LOG_EMERG, "Can't allocate memory! [Add_Client]" );
+	memset( c, 0, sizeof( CHANNEL ));
+	if (!Cl2Chan_InitChannel(c)) {
+		Log( LOG_EMERG, "Can't initialize Member hash table!");
+		free(c);
 		return NULL;
 	}
-	cl2chan->channel = Chan;
-	cl2chan->client = Client;
-	strcpy( cl2chan->modes, "" );
-
-	/* Verketten */
-	cl2chan->next = My_Cl2Chan;
-	My_Cl2Chan = cl2chan;
 
-	Log( LOG_DEBUG, "User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name );
-
-	return cl2chan;
-} /* Add_Client */
+	strlcpy( c->name, Name, sizeof( c->name ));
+	LogDebug("Created new channel structure for \"%s\".", c->name);
+	if (!hashtable_add(&My_Channels, c->name, c)) {
+		Log( LOG_EMERG, "Can't insert channel into hash table!: %s", strerror(errno));
+		hashtable_free(&c->members);
+		free(c);
+		c = NULL;
+	}
+	return c;
+} /* Channel_Create */
 
 
 static bool
 Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const char *Reason, bool InformServer )
 {
-	CL2CHAN *cl2chan, *last_cl2chan;
-	CHANNEL *c;
-
 	assert( Chan != NULL );
 	assert( Client != NULL );
 	assert( Origin != NULL );
 	assert( Reason != NULL );
 
-	last_cl2chan = NULL;
-	cl2chan = My_Cl2Chan;
-	while( cl2chan )
-	{
-		if(( cl2chan->channel == Chan ) && ( cl2chan->client == Client )) break;
-		last_cl2chan = cl2chan;
-		cl2chan = cl2chan->next;
-	}
-	if( ! cl2chan ) return false;
-
-	c = cl2chan->channel;
-	assert( c != NULL );
-
-	/* Aus Verkettung loesen und freigeben */
-	if( last_cl2chan ) last_cl2chan->next = cl2chan->next;
-	else My_Cl2Chan = cl2chan->next;
-	free( cl2chan );
+	if (!Cl2Chan_Destroy(Chan, Client))
+		return false;
 
 	switch( Type )
 	{
@@ -896,46 +711,43 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch
 			 * so only inform other clients in same channel. */
 			assert( InformServer == false );
 			LogDebug("User \"%s\" left channel \"%s\" (%s).",
-					Client_Mask( Client ), c->name, Reason );
+					Client_Mask( Client ), Chan->name, Reason );
 			break;
 		case REMOVE_KICK:
 			/* User was KICKed: inform other servers and all users in channel */
 			if( InformServer )
 				IRC_WriteStrServersPrefix( Client_NextHop( Origin ),
-					Origin, "KICK %s %s :%s", c->name, Client_ID( Client ), Reason);
-			IRC_WriteStrChannelPrefix(Client, c, Origin, false, "KICK %s %s :%s",
-							c->name, Client_ID( Client ), Reason );
+					Origin, "KICK %s %s :%s", Chan->name, Client_ID( Client ), Reason);
+			IRC_WriteStrChannelPrefix(Client, Chan, Origin, false, "KICK %s %s :%s",
+							Chan->name, Client_ID( Client ), Reason );
 			if ((Client_Conn(Client) > NONE) &&
 					(Client_Type(Client) == CLIENT_USER))
 			{
 				IRC_WriteStrClientPrefix(Client, Origin, "KICK %s %s :%s",
-								c->name, Client_ID( Client ), Reason);
+								Chan->name, Client_ID( Client ), Reason);
 			}
 			LogDebug("User \"%s\" has been kicked off \"%s\" by \"%s\": %s.",
-				Client_Mask( Client ), c->name, Client_ID(Origin), Reason);
+				Client_Mask( Client ), Chan->name, Client_ID(Origin), Reason);
 			break;
 		default: /* PART */
 			if (InformServer)
-				IRC_WriteStrServersPrefix(Origin, Client, "PART %s :%s", c->name, Reason);
+				IRC_WriteStrServersPrefix(Origin, Client, "PART %s :%s", Chan->name, Reason);
 
-			IRC_WriteStrChannelPrefix(Origin, c, Client, false, "PART %s :%s",
-									c->name, Reason);
+			IRC_WriteStrChannelPrefix(Origin, Chan, Client, false, "PART %s :%s",
+									Chan->name, Reason);
 
 			if ((Client_Conn(Origin) > NONE) &&
 					(Client_Type(Origin) == CLIENT_USER))
 			{
-				IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", c->name, Reason);
+				IRC_WriteStrClientPrefix( Origin, Client, "PART %s :%s", Chan->name, Reason);
 				LogDebug("User \"%s\" left channel \"%s\" (%s).",
-					Client_Mask(Client), c->name, Reason);
+					Client_Mask(Client), Chan->name, Reason);
 			}
 	}
 
-	/* Wenn Channel nun leer und nicht pre-defined: loeschen */
-	if( ! strchr( Channel_Modes( Chan ), 'P' ))
-	{
-		if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
-	}
-
+	/* if channel empty and not predefined -> remove */
+	if ((hashtable_getfillcount(&Chan->members) == 0) && !strchr(Channel_Modes( Chan ), 'P'))
+		hashtable_delete(&My_Channels, Chan->name);
 	return true;
 } /* Remove_Client */
 
@@ -1001,62 +813,4 @@ Channel_ShowInvites( CLIENT *Client, CHANNEL *Channel )
 	return ShowInvitesBans(h, Client, Channel, true);
 }
 
-
-static CL2CHAN *
-Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
-{
-	return Get_Next_Cl2Chan( My_Cl2Chan, Client, Chan );
-} /* Get_First_Cl2Chan */
-
-
-static CL2CHAN *
-Get_Next_Cl2Chan( CL2CHAN *Start, CLIENT *Client, CHANNEL *Channel )
-{
-	CL2CHAN *cl2chan;
-
-	assert( Client != NULL || Channel != NULL );
-
-	cl2chan = Start;
-	while( cl2chan )
-	{
-		if(( Client ) && ( cl2chan->client == Client )) return cl2chan;
-		if(( Channel ) && ( cl2chan->channel == Channel )) return cl2chan;
-		cl2chan = cl2chan->next;
-	}
-	return NULL;
-} /* Get_Next_Cl2Chan */
-
-
-static bool
-Delete_Channel( CHANNEL *Chan )
-{
-	/* Channel-Struktur loeschen */
-
-	CHANNEL *chan, *last_chan;
-
-	last_chan = NULL;
-	chan = My_Channels;
-	while( chan )
-	{
-		if( chan == Chan ) break;
-		last_chan = chan;
-		chan = chan->next;
-	}
-	if( ! chan ) return false;
-
-	Log( LOG_DEBUG, "Freed channel structure for \"%s\".", Chan->name );
-
-	/* Invite- und Ban-Lists aufraeumen */
-	Lists_Free( &chan->list_bans );
-	Lists_Free( &chan->list_invites );
-
-	/* Neu verketten und freigeben */
-	if( last_chan ) last_chan->next = chan->next;
-	else My_Channels = chan->next;
-	free( chan );
-
-	return true;
-} /* Delete_Channel */
-
-
 /* -eof- */
diff --git a/src/ngircd/channel.h b/src/ngircd/channel.h
index 1f78bf8..e29f141 100644
--- a/src/ngircd/channel.h
+++ b/src/ngircd/channel.h
@@ -17,18 +17,19 @@
 #ifndef __channel_h__
 #define __channel_h__
 
+#include "hash.h"
 
-#if defined(__channel_c__) | defined(S_SPLINT_S)
+#if defined(__channel_c__) | defined(cl2chan_c__) | defined(S_SPLINT_S)
 
 #include "lists.h"
 #include "defines.h"
 #include "array.h"
+#include "client.h"
 
 typedef struct _CHANNEL
 {
 	struct _CHANNEL *next;
 	char name[CHANNEL_NAME_LEN];	/* Name of the channel */
-	UINT32 hash;			/* Hash of the (lowecase!) name */
 	char modes[CHANNEL_MODE_LEN];	/* Channel modes */
 	array topic;			/* Topic of the channel */
 #ifndef STRICT_RFC
@@ -37,23 +38,12 @@ typedef struct _CHANNEL
 #endif
 	char key[CLIENT_PASS_LEN];	/* Channel key ("password", mode "k" ) */
 	unsigned long maxusers;		/* Maximum number of members (mode "l") */
+	hashtable members;		/* members in this channel */
 	struct list_head list_bans;	/* list head of banned users */
 	struct list_head list_invites;	/* list head of invited users */
 } CHANNEL;
-
-typedef struct _CLIENT2CHAN
-{
-	struct _CLIENT2CHAN *next;
-	CLIENT *client;
-	CHANNEL *channel;
-	char modes[CHANNEL_MODE_LEN];	/* User-Modes in Channel */
-} CL2CHAN;
-
 #else
-
 typedef POINTER CHANNEL;
-typedef POINTER CL2CHAN;
-
 #endif
 
 GLOBAL struct list_head *Channel_GetListBans PARAMS((CHANNEL *c));
@@ -72,7 +62,6 @@ GLOBAL void Channel_Kick PARAMS((  CLIENT *Client, CLIENT *Origin, const char *N
 
 GLOBAL unsigned long Channel_Count PARAMS(( void ));
 GLOBAL unsigned long Channel_MemberCount PARAMS(( CHANNEL *Chan ));
-GLOBAL int Channel_CountForUser PARAMS(( CLIENT *Client ));
 
 GLOBAL const char *Channel_Name PARAMS(( const CHANNEL *Chan ));
 GLOBAL char *Channel_Modes PARAMS(( CHANNEL *Chan ));
@@ -88,15 +77,7 @@ GLOBAL void Channel_SetMaxUsers PARAMS(( CHANNEL *Chan, unsigned long Count ));
 GLOBAL CHANNEL *Channel_Search PARAMS(( const char *Name ));
 
 GLOBAL CHANNEL *Channel_First PARAMS(( void ));
-GLOBAL CHANNEL *Channel_Next PARAMS(( CHANNEL *Chan ));
-
-GLOBAL CL2CHAN *Channel_FirstMember PARAMS(( CHANNEL *Chan ));
-GLOBAL CL2CHAN *Channel_NextMember PARAMS(( CHANNEL *Chan, CL2CHAN *Cl2Chan ));
-GLOBAL CL2CHAN *Channel_FirstChannelOf PARAMS(( CLIENT *Client ));
-GLOBAL CL2CHAN *Channel_NextChannelOf PARAMS(( CLIENT *Client, CL2CHAN *Cl2Chan ));
-
-GLOBAL CLIENT *Channel_GetClient PARAMS(( CL2CHAN *Cl2Chan ));
-GLOBAL CHANNEL *Channel_GetChannel PARAMS(( CL2CHAN *Cl2Chan ));
+GLOBAL CHANNEL *Channel_Next PARAMS(( void ));
 
 GLOBAL bool Channel_IsValidName PARAMS(( const char *Name ));
 
diff --git a/src/ngircd/cl2chan.c b/src/ngircd/cl2chan.c
new file mode 100644
index 0000000..18722c2
--- /dev/null
+++ b/src/ngircd/cl2chan.c
@@ -0,0 +1,264 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de)
+ *
+ * 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.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#include "portab.h"
+
+static char UNUSED id[] = "$Id$";
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "defines.h"
+
+#define cl2chan_c__
+#include "conn-func.h"
+#include "cl2chan.h"
+
+#include "hash.h"
+#include "log.h"
+
+/* #define CL2CHAN_DEBUG */
+
+/* Helper function that determines if key k and candidate point to the same client structure. */
+static int cl2chandiff_client(const void *k, const void *candidate)
+{
+	const CL2CHAN *key_cl2chan, *cand_cl2chan;
+	const char *a, *b;
+	key_cl2chan = k;
+	cand_cl2chan = candidate;
+
+	assert(key_cl2chan != NULL);
+	assert(cand_cl2chan != NULL);
+	assert(key_cl2chan->client != NULL);
+	assert(cand_cl2chan->client != NULL);
+
+	a = (const char *) key_cl2chan->client;
+	b = (const char *) cand_cl2chan->client;
+#ifdef CL2CHAN_DEBUG
+	LogDebug("cl2chandiff_client(): Want client %s (addr %x), got %s (addr: %x): res %u",
+			Client_ID( key_cl2chan->client ),a, Client_ID(cand_cl2chan->client),b, a-b);
+#endif
+	return a - b;
+}
+
+
+/* compare callback to determine if key k and candidate are equal */
+static int cl2chandiff(const void *k, const void *candidate)
+{
+	int diff;
+	const CL2CHAN *key_cl2chan, *cand_cl2chan;
+
+	key_cl2chan = k;
+	cand_cl2chan = candidate;
+
+	diff = cl2chandiff_client(k, candidate);
+	if (diff) return diff;
+#ifdef CL2CHAN_DEBUG
+	if (key_cl2chan->channel) {
+		LogDebug("cl2chandiff(): Want channel %s (addr %x), got %s (addr: %x): res %u",
+			key_cl2chan->channel->name, key_cl2chan->channel, cand_cl2chan->channel->name,
+			 cand_cl2chan->channel, key_cl2chan->channel - cand_cl2chan->channel);
+	}
+#endif
+	return key_cl2chan->channel ? (key_cl2chan->channel - cand_cl2chan->channel) : 0;
+}
+
+
+/* callback that returns the hash value of a given cl2chan structure.
+ * We simply use the address of cl2chan->client --  it will remain constant. */
+static size_t cl2chanhash(const void *k)
+{
+	const CL2CHAN *cl2chan = k;
+
+	assert(cl2chan != NULL);
+	assert(cl2chan->client != NULL);
+
+	return (size_t) cl2chan->client;
+}
+
+
+bool Cl2Chan_InitClient(CLIENT *c)
+{
+	return hashtable_init(&c->Cl2Chan, free, cl2chandiff, cl2chanhash);
+}
+
+
+GLOBAL CL2CHAN *
+Cl2Chan_FirstMember( CHANNEL *Chan )
+{
+	assert( Chan != NULL );
+	hashtable_iterator_reset(&Chan->members);
+	return Cl2Chan_NextMember(Chan);
+}
+
+
+GLOBAL CL2CHAN *
+Cl2Chan_NextMember( CHANNEL *Chan )
+{
+	CL2CHAN *res;
+	assert( Chan != NULL );
+	res = hashtable_iterator_getnext(&Chan->members);
+#ifdef DEBUG
+	if (res) assert(res->channel = Chan);
+#endif
+	return res;
+}
+
+
+GLOBAL CL2CHAN *
+Cl2Chan_FirstChan( CLIENT *Client )
+{
+	assert( Client != NULL );
+
+	hashtable_iterator_reset(&Client->Cl2Chan);
+	return Cl2Chan_NextChan( Client );
+}
+
+
+GLOBAL CL2CHAN *
+Cl2Chan_NextChan( CLIENT *Client )
+{
+	CL2CHAN *res;
+	assert( Client != NULL );
+
+	res = hashtable_iterator_getnext(&Client->Cl2Chan);
+#ifdef DEBUG
+	if (res) assert(res->client == Client);
+#endif
+	return res;
+}
+
+
+GLOBAL CLIENT *
+Cl2Chan_GetClient( CL2CHAN *Cl2Chan )
+{
+	assert( Cl2Chan != NULL );
+	return Cl2Chan->client;
+}
+
+
+GLOBAL CHANNEL *
+Cl2Chan_GetChannel( CL2CHAN *Cl2Chan )
+{
+	assert( Cl2Chan != NULL );
+	return Cl2Chan->channel;
+}
+
+
+bool
+Cl2Chan_InitChannel( CHANNEL *c )
+{
+	return hashtable_init(&c->members, NULL, cl2chandiff_client, cl2chanhash);
+}
+
+
+CL2CHAN *
+Cl2Chan_Get( CHANNEL *Chan, CLIENT *Client )
+{
+	CL2CHAN cl2chan;
+#ifdef DEBUG
+	CL2CHAN *res;
+#endif
+	assert( Chan != NULL );
+	assert( Client != NULL );
+
+	cl2chan.channel = Chan;
+	cl2chan.client = Client;
+#ifdef DEBUG
+	res = hashtable_get(&Chan->members, &cl2chan);
+	if (res) {
+		assert(res->channel == Chan);
+		assert(res->client == Client);
+	}
+
+	return res;
+#else
+	return hashtable_get(&Chan->members, &cl2chan);
+#endif
+}
+
+
+bool Cl2Chan_Join(CHANNEL *Chan, CLIENT *Client )
+{
+	CL2CHAN *cl2chan;
+
+	assert( Chan != NULL );
+	assert( Client != NULL );
+
+	cl2chan = malloc( sizeof( CL2CHAN ));
+	if( ! cl2chan ) {
+		Log( LOG_EMERG, "Can't allocate memory! [Cl2Chan_Create]" );
+		return NULL;
+	}
+
+	memset(cl2chan, 0, sizeof(CL2CHAN));
+	cl2chan->channel = Chan;
+	cl2chan->client = Client;
+
+	if (!hashtable_add(&Client->Cl2Chan, cl2chan, cl2chan)) {
+		if (errno != EEXIST)
+			Log( LOG_EMERG, "Can't add client to lookup hash table: %s", strerror(errno));
+
+		free(cl2chan);
+		return NULL;
+	}
+
+	if (!hashtable_add(&Chan->members, cl2chan, cl2chan)) {
+		if (errno != EEXIST)
+			Log( LOG_EMERG, "Can't add client to lookup hash table: %s", strerror(errno));
+
+		hashtable_delete(&Client->Cl2Chan, cl2chan);
+		return NULL;
+	}
+
+	LogDebug("User \"%s\" joined channel \"%s\".", Client_Mask( Client ), Chan->name);
+	return cl2chan;
+} /* Add_Client */
+
+
+unsigned int Cl2Chan_CountForUser(CLIENT *c)
+{
+	return hashtable_getfillcount(&c->Cl2Chan);
+}
+
+
+bool
+Cl2Chan_Destroy(CHANNEL *Chan, CLIENT *Client)
+{
+	CL2CHAN cl2chan, *removed = NULL;
+
+	assert( Chan != NULL );
+	assert( Client != NULL );
+	cl2chan.client = Client;
+	cl2chan.channel = Chan;
+
+	removed = hashtable_remove(&Chan->members, &cl2chan);
+#ifdef DEBUG
+	if (!removed) {
+		LogDebug("Cl2Chan_Destroy(): tried to remove nonexistant client member %s from channel %s!",
+			Client_ID(Client), Chan->name);
+
+		assert(hashtable_get(&Chan->members, &cl2chan) == NULL);
+		if (hashtable_getfillcount(&Client->Cl2Chan))
+			assert(hashtable_get(&Client->Cl2Chan, &cl2chan) == NULL);
+		else
+			LogDebug("Cl2Chan_Destroy(): Fillcount empty: %s, chan %s!", Client_ID(Client), Chan->name);
+		return false;
+	}
+	assert(removed->channel == Chan);
+#endif
+	hashtable_delete(&Client->Cl2Chan, &cl2chan);
+	return removed != NULL;
+}
+/* -eof- */
diff --git a/src/ngircd/cl2chan.h b/src/ngircd/cl2chan.h
new file mode 100644
index 0000000..6444672
--- /dev/null
+++ b/src/ngircd/cl2chan.h
@@ -0,0 +1,52 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de)
+ *
+ * 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.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ *
+ * Client <-> Channel Management
+ */
+
+#ifndef cl2chan_hdr__
+#define cl2chan_hdr__
+
+#include "portab.h"
+#include "defines.h"
+
+#include "channel.h"
+#include "client.h"
+
+typedef struct _CLIENT2CHAN
+{
+	CLIENT *client;
+	CHANNEL *channel;
+	char modes[CHANNEL_MODE_LEN];   /* User-Modes in the Channel */
+} CL2CHAN;
+
+GLOBAL CL2CHAN * Cl2Chan_FirstMember PARAMS(( CHANNEL *Chan ));
+GLOBAL CL2CHAN * Cl2Chan_NextMember PARAMS(( CHANNEL *Chan ));
+
+GLOBAL CL2CHAN * Cl2Chan_FirstChan PARAMS(( CLIENT *Client ));
+
+GLOBAL CL2CHAN * Cl2Chan_NextChan PARAMS(( CLIENT *Client ));
+
+GLOBAL CLIENT * Cl2Chan_GetClient PARAMS(( CL2CHAN *Cl2Chan ));
+
+GLOBAL CHANNEL * Cl2Chan_GetChannel PARAMS(( CL2CHAN *Cl2Chan ));
+
+GLOBAL CL2CHAN * Cl2Chan_Get PARAMS(( CHANNEL *Chan, CLIENT *Client ));
+
+GLOBAL bool Cl2Chan_Join PARAMS((CHANNEL *Chan, CLIENT *Client ));
+GLOBAL bool Cl2Chan_Destroy PARAMS((CHANNEL *Chan, CLIENT *Client));
+
+GLOBAL bool Cl2Chan_InitClient PARAMS(( CLIENT *c ));
+GLOBAL bool Cl2Chan_InitChannel PARAMS(( CHANNEL *c ));
+
+unsigned int Cl2Chan_CountForUser PARAMS(( CLIENT *Client ));
+
+#endif
+
diff --git a/src/ngircd/client.c b/src/ngircd/client.c
index 1b72adc..d2aa42b 100644
--- a/src/ngircd/client.c
+++ b/src/ngircd/client.c
@@ -21,6 +21,7 @@ static char UNUSED id[] = "$Id: client.c,v 1.98 2008/04/04 19:30:01 fw Exp $";
 
 #include "imp.h"
 #include <assert.h>
+#include <errno.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,6 +34,7 @@ static char UNUSED id[] = "$Id: client.c,v 1.98 2008/04/04 19:30:01 fw Exp $";
 
 #include "exp.h"
 #include "client.h"
+#include "cl2chan.h"
 
 #include <imp.h>
 #include "ngircd.h"
@@ -50,7 +52,8 @@ static char UNUSED id[] = "$Id: client.c,v 1.98 2008/04/04 19:30:01 fw Exp $";
 #define GETID_LEN (CLIENT_NICK_LEN-1) + 1 + (CLIENT_USER_LEN-1) + 1 + (CLIENT_HOST_LEN-1) + 1
 
 
-static CLIENT *This_Server, *My_Clients;
+static CLIENT *This_Server;
+static hashtable My_Clients;
 static char GetID_Buffer[GETID_LEN];
 
 static WHOWAS My_Whowas[MAX_WHOWAS];
@@ -70,21 +73,28 @@ static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer,
  char *Info, int Hops, int Token, char *Modes, bool Idented));
 
 
+static int clientcmp(const void *key, const void *candidate)
+{
+	return strcasecmp(key, candidate);
+}
+
+
+static size_t clienthash(const void *key)
+{
+	return Hash(key);
+}
+
+
 GLOBAL void
 Client_Init( void )
 {
 	struct hostent *h;
-	
+
 	This_Server = New_Client_Struct( );
-	if( ! This_Server )
-	{
-		Log( LOG_EMERG, "Can't allocate client structure for server! Going down." );
-		Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
-		exit( 1 );
-	}
+	if (!This_Server || !hashtable_init(&My_Clients, free, clientcmp, clienthash ))
+		goto server_dies;
 
 	/* Client-Struktur dieses Servers */
-	This_Server->next = NULL;
 	This_Server->type = CLIENT_SERVER;
 	This_Server->conn_id = NONE;
 	This_Server->introducer = This_Server;
@@ -96,35 +106,30 @@ Client_Init( void )
 		h = gethostbyname( This_Server->host );
 		if (h) strlcpy(This_Server->host, h->h_name, sizeof(This_Server->host));
 	}
-	Client_SetID( This_Server, Conf_ServerName );
-	Client_SetInfo( This_Server, Conf_ServerInfo );
 
-	My_Clients = This_Server;
-	
+	if (!Client_SetID(This_Server, Conf_ServerName)) {
+		Log( LOG_ERR, "Can't add new Servername (%s) to hashtable", Conf_ServerName, strerror(errno));
+		goto server_dies;
+	}
+	Client_SetInfo( This_Server, Conf_ServerInfo );
 	memset( &My_Whowas, 0, sizeof( My_Whowas ));
+	return;
+
+server_dies:
+	Log( LOG_EMERG, "Can't allocate client structure for server! Going down." );
+	Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
+	exit( 1 );
 } /* Client_Init */
 
 
 GLOBAL void
 Client_Exit( void )
 {
-	CLIENT *c, *next;
-	int cnt;
+	Client_Destroy( This_Server, NGIRCd_SignalRestart ?
+		"Server going down (restarting)." : "Server going down.", NULL, false );
 
-	if( NGIRCd_SignalRestart ) Client_Destroy( This_Server, "Server going down (restarting).", NULL, false );
-	else Client_Destroy( This_Server, "Server going down.", NULL, false );
-	
-	cnt = 0;
-	c = My_Clients;
-	while( c )
-	{
-		cnt++;
-		next = (CLIENT *)c->next;
-		free( c );
-		c = next;
-	}
-	if( cnt ) Log( LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s" );
-} /* Client_Exit */
+	hashtable_free(&My_Clients);
+}
 
 
 GLOBAL CLIENT *
@@ -141,8 +146,17 @@ Client_ThisServer( void )
 GLOBAL CLIENT *
 Client_NewLocal(CONN_ID Idx, char *Hostname, int Type, bool Idented)
 {
-	return Init_New_Client(Idx, This_Server, NULL, Type, NULL, NULL,
-		Hostname, NULL, 0, 0, NULL, Idented);
+	/* # is illegal in a nick name, so #+CONN_ID is unique.
+	 * We need this as a placeholder until the clients sets
+	   the real nick. */
+	char fake_nick[CLIENT_NICK_LEN];
+
+	snprintf(fake_nick, sizeof fake_nick, "#%x#", Idx);
+#ifdef DEBUG
+	assert(Client_Search(fake_nick) == NULL);
+#endif
+	return Init_New_Client(Idx, This_Server, NULL, Type, fake_nick, NULL,
+					Hostname, NULL, 0, 0, NULL, Idented);
 } /* Client_NewLocal */
 
 
@@ -197,22 +211,34 @@ Init_New_Client(CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer,
 	client->introducer = Introducer;
 	client->topserver = TopServer;
 	client->type = Type;
-	if( ID ) Client_SetID( client, ID );
+
+	switch(Type) {
+		case CLIENT_SERVER:
+			Generate_MyToken( client );
+			break;
+		default:
+		if (!Cl2Chan_InitClient(client)) {
+			free(client);
+			return NULL;
+		}
+	}
+
+	if (!Client_SetID(client, ID)) {
+		hashtable_free(&client->Cl2Chan);
+		free(client);
+		return NULL;
+	}
+
 	if( User ) Client_SetUser( client, User, Idented );
 	if( Hostname ) Client_SetHostname( client, Hostname );
 	if( Info ) Client_SetInfo( client, Info );
 	client->hops = Hops;
 	client->token = Token;
 	if( Modes ) Client_SetModes( client, Modes );
-	if( Type == CLIENT_SERVER ) Generate_MyToken( client );
 
 	if( strchr( client->modes, 'a' ))
 		strlcpy( client->away, DEFAULT_AWAY_MSG, sizeof( client->away ));
 
-	/* Verketten */
-	client->next = (POINTER *)My_Clients;
-	My_Clients = client;
-
 	/* Adjust counters */
 	Adjust_Counters( client );
 
@@ -224,10 +250,12 @@ GLOBAL void
 Client_Destroy( CLIENT *Client, char *LogMsg, char *FwdMsg, bool SendQuit )
 {
 	/* Client entfernen. */
-	
-	CLIENT *last, *c;
-	char msg[LINE_LEN], *txt;
 
+	CLIENT *c;
+	char msg[LINE_LEN], *txt;
+#ifdef DEBUG
+	char nickbuf[CLIENT_ID_LEN];
+#endif
 	assert( Client != NULL );
 
 	if( LogMsg ) txt = LogMsg;
@@ -241,93 +269,110 @@ Client_Destroy( CLIENT *Client, char *LogMsg, char *FwdMsg, bool SendQuit )
 		strlcat(msg, Client->id, sizeof (msg));
 	}
 
-	last = NULL;
-	c = My_Clients;
-	while( c )
+#ifdef DEBUG
+	strlcpy(nickbuf, Client->id, sizeof nickbuf);
+	if (hashtable_get(&My_Clients, nickbuf) == NULL)
+		LogDebug("Client_Destroy() called for unknown nickname %s!", nickbuf);
+#endif
+	hashtable_iterator_reset(&My_Clients);
+	while(( c = hashtable_iterator_getnext(&My_Clients)))
 	{
-		if(( Client->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c != Client ))
-		{
-			/* der Client, der geloescht wird ist ein Server. Der Client, den wir gerade
-			 * pruefen, ist ein Child von diesem und muss daher auch entfernt werden */
-			Client_Destroy( c, NULL, msg, false );
-			last = NULL;
-			c = My_Clients;
+		LogDebug("Testing client nick: %s, want %s", c->id, Client->id);
+
+		if (c != Client) {
+			if ((Client->type == CLIENT_SERVER) && (c->introducer == Client)) {
+				/* The client that is about to be removed is a server,
+				 * the client we are checking right now is a child of that
+				 * server and thus has to be removed, too. */
+				Client_Destroy( c, NULL, msg, false );
+				hashtable_iterator_reset(&My_Clients);
+			}
 			continue;
 		}
-		if( c == Client )
-		{
-			/* Wir haben den Client gefunden: entfernen */
-			if( last ) last->next = c->next;
-			else My_Clients = (CLIENT *)c->next;
-
-			if( c->type == CLIENT_USER )
-			{
-				if( c->conn_id != NONE )
-				{
-					/* Ein lokaler User */
-					Log( LOG_NOTICE, "User \"%s\" unregistered (connection %d): %s", Client_Mask( c ), c->conn_id, txt );
-
-					if( SendQuit )
-					{
-						/* Alle andere Server informieren! */
-						if( FwdMsg ) IRC_WriteStrServersPrefix( NULL, c, "QUIT :%s", FwdMsg );
-						else IRC_WriteStrServersPrefix( NULL, c, "QUIT :" );
+		/* c == Client */
+		switch (c->type) {
+			case CLIENT_USER:
+				if (c->conn_id >= 0) { /* local user */
+					Log(LOG_NOTICE, "User \"%s\" unregistered (connection %d): %s",
+									Client_Mask( c ), c->conn_id, txt);
+					if (SendQuit) {
+						/* inform all other servers */
+						if (FwdMsg) {
+							IRC_WriteStrServersPrefix(NULL,
+									c, "QUIT :%s", FwdMsg );
+						} else {
+							IRC_WriteStrServersPrefix(NULL,
+										c, "QUIT :" );
+						}
 					}
+				} else { /* Remote User */
+					Log(LOG_DEBUG, "User \"%s\" unregistered: %s",
+								Client_Mask( c ), txt );
 				}
-				else
-				{
-					/* Remote User */
-					Log( LOG_DEBUG, "User \"%s\" unregistered: %s", Client_Mask( c ), txt );
-
-					if( SendQuit )
-					{
-						/* Andere Server informieren, ausser denen, die "in
-						 * Richtung dem liegen", auf dem der User registriert
-						 * ist. Von denen haben wir das QUIT ja wohl bekommen. */
-						if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :%s", FwdMsg );
-						else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :" );
+				if (SendQuit) {
+					/* inform all other servers, except those
+					 * that are along the clients route (i.e.
+					 * the server that passed this clients QUIT along */
+					if (FwdMsg) {
+						IRC_WriteStrServersPrefix(Client_NextHop(c),
+									c, "QUIT :%s", FwdMsg );
+					} else {
+						IRC_WriteStrServersPrefix(Client_NextHop(c),
+										c, "QUIT :" );
 					}
 				}
 
 				/* Unregister client from channels */
 				Channel_Quit( c, FwdMsg ? FwdMsg : c->id );
-				
+
 				/* Register client in My_Whowas structure */
 				Client_RegisterWhowas( c );
-			}
-			else if( c->type == CLIENT_SERVER )
-			{
-				if( c != This_Server )
-				{
-					if( c->conn_id != NONE ) Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
-					else Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered: %s", c->id, txt );
+
+				/* destroy client <-> channel table */
+				hashtable_free(&Client->Cl2Chan);
+			break;
+			case CLIENT_SERVER:
+				if( c != This_Server ) {
+					Log(LOG_NOTICE|LOG_snotice,
+						"Server \"%s\" unregistered (fd %d): %s",
+								c->id, c->conn_id, txt );
 				}
 
 				/* andere Server informieren */
-				if( ! NGIRCd_SignalQuit )
-				{
-					if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :%s", c->id, FwdMsg );
-					else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :", c->id );
+				if( ! NGIRCd_SignalQuit ) {
+					if (FwdMsg) {
+						IRC_WriteStrServersPrefix(Client_NextHop(c),
+								c, "SQUIT %s :%s", c->id, FwdMsg );
+					} else {
+						IRC_WriteStrServersPrefix(Client_NextHop(c),
+									c, "SQUIT %s :", c->id );
+					}
+				} else {
+					Log(LOG_NOTICE,"Client \"%s\" unregistered (fd %d): %s",
+									c->id, c->conn_id, txt );
 				}
-			}
-			else
-			{
-				if( c->conn_id != NONE )
-				{
-					if( c->id[0] ) Log( LOG_NOTICE, "Client \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
-					else Log( LOG_NOTICE, "Client unregistered (connection %d): %s", c->conn_id, txt );
+			break;
+			default:
+				LogDebug("Unknown Client type %d", c->type);
+				if( c->conn_id >= 0) {
+					Log( LOG_NOTICE, "Client \"%s\" unregistered (connection %d): %s",
+										c->id, c->conn_id, txt );
 				} else {
 					Log(LOG_WARNING, "Unregistered unknown client \"%s\": %s",
-								c->id[0] ? c->id : "(No Nick)", txt );
+										c->id[0], txt );
 				}
-			}
-
-			free( c );
 			break;
 		}
-		last = c;
-		c = (CLIENT *)c->next;
+
+		LogDebug("Remove Nick \"%s\" from hashtable (Destroy)", c->id);
+		hashtable_delete(&My_Clients, c->id);
+		break;
 	}
+	hashtable_iterator_reset(&My_Clients);
+
+#ifdef DEBUG
+	assert(hashtable_get(&My_Clients, nickbuf) == NULL);
+#endif
 } /* Client_Destroy */
 
 
@@ -343,19 +388,48 @@ Client_SetHostname( CLIENT *Client, char *Hostname )
 } /* Client_SetHostname */
 
 
-GLOBAL void
-Client_SetID( CLIENT *Client, char *ID )
+static bool
+Client_ChangeID( CLIENT *Client, const char *ID )
 {
-	/* Hostname eines Clients setzen, Hash-Wert berechnen */
+	assert( Client != NULL );
+	assert( ID != NULL );
 
+	if (!hashtable_changekey(&My_Clients, Client->id, ID )) {
+		assert(errno != EEXIST);
+		Log(LOG_ERR, "Nickchange: %s -> %s failed (%s)", Client->id, ID, strerror(errno));
+		return false;
+	}
+	strlcpy(Client->id, ID, sizeof Client->id);
+#ifdef DEBUG
+	assert(hashtable_changekey(&My_Clients, ID, Client->id));
+	return true;
+#else
+	return hashtable_changekey(&My_Clients, ID, Client->id);
+#endif
+}
+
+
+GLOBAL bool
+Client_SetID( CLIENT *Client, char *ID )
+{
 	assert( Client != NULL );
 	assert( ID != NULL );
-	
-	strlcpy( Client->id, ID, sizeof( Client->id ));
 
-	/* Hash */
-	Client->hash = Hash( Client->id );
-} /* Client_SetID */
+	if (Client->id[0])
+		return Client_ChangeID(Client, ID);
+
+	strlcpy(Client->id, ID, sizeof(Client->id));
+	if (!hashtable_add(&My_Clients, Client->id, Client )) {
+#ifdef DEBUG
+		if (errno == EEXIST) {
+			assert(Client_Search( Client->id ) != NULL);
+		}
+#endif
+		Log( LOG_ERR, "Can't add new Client-Nick (%s) to hashtable: %s", ID, strerror(errno));
+		return false;
+	}
+	return true;
+}
 
 
 GLOBAL void
@@ -537,8 +611,6 @@ Client_Search( const char *Nick )
 	 */
 
 	char search_id[CLIENT_ID_LEN], *ptr;
-	CLIENT *c = NULL;
-	UINT32 search_hash;
 
 	assert( Nick != NULL );
 
@@ -547,19 +619,7 @@ Client_Search( const char *Nick )
 	ptr = strchr( search_id, '!' );
 	if( ptr ) *ptr = '\0';
 
-	search_hash = Hash( search_id );
-
-	c = My_Clients;
-	while( c )
-	{
-		if( c->hash == search_hash )
-		{
-			/* lt. Hash-Wert: Treffer! */
-			if( strcasecmp( c->id, search_id ) == 0 ) return c;
-		}
-		c = (CLIENT *)c->next;
-	}
-	return NULL;
+	return hashtable_get(&My_Clients, search_id);
 } /* Client_Search */
 
 
@@ -575,13 +635,14 @@ Client_GetFromToken( CLIENT *Client, int Token )
 	assert( Client != NULL );
 	assert( Token > 0 );
 
-	c = My_Clients;
-	while( c )
-	{
-		if(( c->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c->token == Token )) return c;
-		c = (CLIENT *)c->next;
+	hashtable_iterator_reset(&My_Clients);
+	while((c = hashtable_iterator_getnext(&My_Clients))) {
+		if(( c->type == CLIENT_SERVER ) &&
+				( c->introducer == Client ) &&
+				( c->token == Token )) break;
 	}
-	return NULL;
+	hashtable_iterator_reset(&My_Clients);
+	return c;
 } /* Client_GetFromToken */
 
 
@@ -610,9 +671,11 @@ Client_ID( CLIENT *Client )
 	if(Client->type == CLIENT_USER)
 		assert(strlen(Client->id) < Conf_MaxNickLength);
 #endif
-						   
-	if( Client->id[0] ) return Client->id;
-	else return "*";
+	switch (Client->id[0]) {
+		case '#':
+		case '0': return "*";
+	}
+	return Client->id;
 } /* Client_ID */
 
 
@@ -804,19 +867,15 @@ Client_CheckID( CLIENT *Client, char *ID )
 	}
 
 	/* ID bereits vergeben? */
-	c = My_Clients;
-	while( c )
-	{
-		if( strcasecmp( c->id, ID ) == 0 )
-		{
-			/* die Server-ID gibt es bereits */
-			snprintf( str, sizeof( str ), "ID \"%s\" already registered", ID );
-			if( Client->conn_id != c->conn_id ) Log( LOG_ERR, "%s (on connection %d)!", str, c->conn_id );
-			else Log( LOG_ERR, "%s (via network)!", str );
-			Conn_Close( Client->conn_id, str, str, true);
-			return false;
-		}
-		c = (CLIENT *)c->next;
+	c = hashtable_get(&My_Clients, ID);
+	if (c) {
+		snprintf( str, sizeof( str ), "ID \"%s\" already registered", ID );
+		if( Client->conn_id != c->conn_id )
+			Log( LOG_ERR, "%s (on connection %d)!", str, c->conn_id );
+		else
+			Log( LOG_ERR, "%s (via network)!", str );
+		Conn_Close( Client->conn_id, str, str, true);
+		return false;
 	}
 
 	return true;
@@ -827,19 +886,15 @@ GLOBAL CLIENT *
 Client_First( void )
 {
 	/* Ersten Client liefern. */
-
-	return My_Clients;
+	hashtable_iterator_reset(&My_Clients);
+	return hashtable_iterator_getnext(&My_Clients);
 } /* Client_First */
 
 
 GLOBAL CLIENT *
-Client_Next( CLIENT *c )
+Client_Next( void )
 {
-	/* Naechsten Client liefern. Existiert keiner,
-	 * so wird NULL geliefert. */
-
-	assert( c != NULL );
-	return (CLIENT *)c->next;
+	return hashtable_iterator_getnext(&My_Clients);
 } /* Client_Next */
 
 
@@ -884,12 +939,11 @@ Client_MyServerCount( void )
 	CLIENT *c;
 	unsigned long cnt = 0;
 
-	c = My_Clients;
-	while( c )
-	{
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients)))
 		if(( c->type == CLIENT_SERVER ) && ( c->hops == 1 )) cnt++;
-		c = (CLIENT *)c->next;
-	}
+
+	hashtable_iterator_reset(&My_Clients);
 	return cnt;
 } /* Client_MyServerCount */
 
@@ -900,12 +954,11 @@ Client_OperCount( void )
 	CLIENT *c;
 	unsigned long cnt = 0;
 
-	c = My_Clients;
-	while( c )
-	{
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients)))
 		if( c && ( c->type == CLIENT_USER ) && ( strchr( c->modes, 'o' ))) cnt++;
-		c = (CLIENT *)c->next;
-	}
+
+	hashtable_iterator_reset(&My_Clients);
 	return cnt;
 } /* Client_OperCount */
 
@@ -916,12 +969,16 @@ Client_UnknownCount( void )
 	CLIENT *c;
 	unsigned long cnt = 0;
 
-	c = My_Clients;
-	while( c )
-	{
-		if( c && ( c->type != CLIENT_USER ) && ( c->type != CLIENT_SERVICE ) && ( c->type != CLIENT_SERVER )) cnt++;
-		c = (CLIENT *)c->next;
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients))) {
+		if( c && ( c->type != CLIENT_USER ) &&
+				( c->type != CLIENT_SERVICE ) &&
+				( c->type != CLIENT_SERVER ))
+		{
+			cnt++;
+		}
 	}
+	hashtable_iterator_reset(&My_Clients);
 
 	return cnt;
 } /* Client_UnknownCount */
@@ -1003,12 +1060,11 @@ Count( CLIENT_TYPE Type )
 	CLIENT *c;
 	unsigned long cnt = 0;
 
-	c = My_Clients;
-	while( c )
-	{
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients)))
 		if( c->type == Type ) cnt++;
-		c = (CLIENT *)c->next;
-	}
+
+	hashtable_iterator_reset(&My_Clients);
 	return cnt;
 } /* Count */
 
@@ -1019,12 +1075,12 @@ MyCount( CLIENT_TYPE Type )
 	CLIENT *c;
 	unsigned long cnt = 0;
 
-	c = My_Clients;
-	while( c )
-	{
-		if(( c->introducer == This_Server ) && ( c->type == Type )) cnt++;
-		c = (CLIENT *)c->next;
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients))) {
+		if(( c->introducer == This_Server ) &&
+				( c->type == Type )) cnt++;
 	}
+	hashtable_iterator_reset(&My_Clients);
 	return cnt;
 } /* MyCount */
 
@@ -1062,18 +1118,14 @@ Generate_MyToken( CLIENT *Client )
 	CLIENT *c;
 	int token;
 
-	c = My_Clients;
 	token = 2;
-	while( c )
-	{
-		if( c->mytoken == token )
-		{
-			/* Das Token wurde bereits vergeben */
+	hashtable_iterator_reset(&My_Clients);
+	while ((c = hashtable_iterator_getnext(&My_Clients))) {
+		if( c->mytoken == token ) {
+			/* Token already in use */
 			token++;
-			c = My_Clients;
-			continue;
+			hashtable_iterator_reset(&My_Clients);
 		}
-		else c = (CLIENT *)c->next;
 	}
 	Client->mytoken = token;
 	Log( LOG_DEBUG, "Assigned token %d to server \"%s\".", token, Client->id );
diff --git a/src/ngircd/client.h b/src/ngircd/client.h
index 63406f2..3706168 100644
--- a/src/ngircd/client.h
+++ b/src/ngircd/client.h
@@ -30,15 +30,13 @@
 #define CLIENT_TYPE int
 
 #include "defines.h"
-
-#if defined(__client_c__) | defined(S_SPLINT_S)
+#include "hash.h"
+#if defined(__client_c__) | defined(cl2chan_c__) | defined(S_SPLINT_S)
 
 typedef struct _CLIENT
 {
 	time_t starttime;		/* Start time of link */
 	char id[CLIENT_ID_LEN];		/* nick (user) / ID (server) */
-	UINT32 hash;			/* hash of lower-case ID */
-	POINTER *next;			/* pointer to next client structure */
 	CLIENT_TYPE type;		/* type of client, see CLIENT_xxx */
 	CONN_ID conn_id;		/* ID of the connection (if local) or NONE (remote) */
 	struct _CLIENT *introducer;	/* ID of the servers which the client is connected to */
@@ -52,6 +50,7 @@ typedef struct _CLIENT
 	bool oper_by_me;		/* client is local IRC operator on this server? */
 	char away[CLIENT_AWAY_LEN];	/* AWAY text (valid if mode 'a' is set) */
 	char flags[CLIENT_FLAGS_LEN];	/* flags of the client */
+	hashtable Cl2Chan;		/* Channels this client is in */
 } CLIENT;
 
 #else
@@ -75,7 +74,7 @@ typedef struct _WHOWAS
 GLOBAL void Client_Init PARAMS(( void ));
 GLOBAL void Client_Exit PARAMS(( void ));
 
-GLOBAL CLIENT *Client_NewLocal PARAMS(( CONN_ID Idx, char *Hostname, int Type, bool Idented ));
+GLOBAL CLIENT *Client_NewLocal PARAMS(( int Idx, char *Hostname, int Type, bool Idented ));
 GLOBAL CLIENT *Client_NewRemoteServer PARAMS(( CLIENT *Introducer, char *Hostname, CLIENT *TopServer, int Hops, int Token, char *Info, bool Idented ));
 GLOBAL CLIENT *Client_NewRemoteUser PARAMS(( CLIENT *Introducer, char *Nick, int Hops, char *User, char *Hostname, int Token, char *Modes, char *Info, bool Idented ));
 
@@ -87,10 +86,10 @@ GLOBAL CLIENT *Client_GetFromToken PARAMS(( CLIENT *Client, int Token ));
 
 GLOBAL CLIENT *Client_Search PARAMS(( const char *ID ));
 GLOBAL CLIENT *Client_First PARAMS(( void ));
-GLOBAL CLIENT *Client_Next PARAMS(( CLIENT *c ));
+GLOBAL CLIENT *Client_Next PARAMS(( void ));
 
 GLOBAL int Client_Type PARAMS(( CLIENT *Client ));
-GLOBAL CONN_ID Client_Conn PARAMS(( CLIENT *Client ));
+GLOBAL int Client_Conn PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_ID PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Mask PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Info PARAMS(( CLIENT *Client ));
@@ -112,7 +111,7 @@ GLOBAL time_t Client_StartTime PARAMS(( CLIENT *Client ));
 GLOBAL bool Client_HasMode PARAMS(( CLIENT *Client, char Mode ));
 
 GLOBAL void Client_SetHostname PARAMS(( CLIENT *Client, char *Hostname ));
-GLOBAL void Client_SetID PARAMS(( CLIENT *Client, char *Nick ));
+GLOBAL bool Client_SetID PARAMS(( CLIENT *Client, char *Nick ));
 GLOBAL void Client_SetUser PARAMS(( CLIENT *Client, char *User, bool Idented ));
 GLOBAL void Client_SetInfo PARAMS(( CLIENT *Client, char *Info ));
 GLOBAL void Client_SetPassword PARAMS(( CLIENT *Client, char *Pwd ));
diff --git a/src/ngircd/hash.c b/src/ngircd/hash.c
index d9aa79b..a637793 100644
--- a/src/ngircd/hash.c
+++ b/src/ngircd/hash.c
@@ -9,6 +9,7 @@
  * Please read the file COPYING, README and AUTHORS for more information.
  *
  * Hash calculation
+ * Hash table (c) 2006 Florian Westphal (fw@strlen.de)
  */
 
 
@@ -19,16 +20,33 @@ static char UNUSED id[] = "$Id: hash.c,v 1.13 2006/10/06 21:23:47 fw Exp $";
 #include "imp.h"
 #include <assert.h>
 #include <string.h>
+#include <errno.h>
+#include <stdlib.h>
 
 #include "defines.h"
+#include "log.h"
 #include "tool.h"
 
 #include "exp.h"
 #include "hash.h"
 
+#define INITIAL_HASHSIZE	23
+
+static struct bucket *hashtable_bucket_get(hashtable *h, const void *key);
+static struct bucket *hashtable_bucket_steal(hashtable *h, const void *key);
 
 static UINT32 jenkins_hash PARAMS(( register UINT8 *k, register UINT32 length, register UINT32 initval ));
 
+/* Enable Extra Debug Output in Hash Insertion/Removal/Iteration code.
+ * This creates a _LOT_ of debug messages. */
+/* #define DEBUG_HASH */
+
+#ifdef DEBUG
+static void hashtable_stats(const hashtable * const h);
+#else
+static inline void hashtable_stats(const UNUSED hashtable * const h) { }
+#endif
+
 
 GLOBAL UINT32
 Hash( const char *String )
@@ -122,4 +140,493 @@ jenkins_hash( register UINT8 *k, register UINT32 length, register UINT32 initval
 } /* jenkins_hash */
 
 
-/* -eof- */
+static size_t get_larger_prime(size_t old)
+{
+	size_t i;
+	static const size_t primes[] = { 223, 523, 823, 1123,
+				1523, 1823, 2423, 4523, 8123,
+				20323, 28723, 47123, 76423,
+				96223, 133723, 220123,
+				386723, 647723, 999623,
+				1923323, 4735823, 6664223 };
+
+	for (i=0; i < sizeof primes/sizeof primes[0];i++) {
+		if (old < primes[i])
+			return primes[i];
+	}
+#ifdef DEBUG
+	Log(LOG_DEBUG, "Could not find larger prime than  %u", old);
+#endif
+	return old*2 + 1;
+}
+
+
+struct bucket {
+	struct bucket *next;
+	const void *keyptr;
+	void *elemptr;
+};
+
+
+static struct {
+	struct bucket *first;
+	UINT8 elems;		/* number of buckets in list */
+	UINT8 thresh;		/* number of buckets to keep before newly returned buckets are free'd */
+	UINT8 drained;		/* number of times we had to malloc() before bucket was returned */
+} bucket_head;
+
+
+static struct bucket *bucket_alloc(void)
+{
+	struct bucket *res;
+
+	if (!bucket_head.first) {
+		assert(bucket_head.elems == 0);
+		bucket_head.drained++;
+		return malloc(sizeof *res);
+	}
+	assert(bucket_head.elems > 0);
+	bucket_head.elems--;
+	res = bucket_head.first;
+	bucket_head.first = bucket_head.first->next;
+	res->next = NULL;
+	return res;
+}
+
+
+static void bucket_free(struct bucket *b)
+{
+	assert(b != NULL);
+
+	if ((bucket_head.drained > 5) &&
+		(bucket_head.thresh <= 220))
+	{
+		bucket_head.thresh += 20;
+		Log(LOG_INFO, "hashtable: drain %u, increased backlog to %u buckets",
+						bucket_head.drained, bucket_head.thresh);
+	}
+
+	bucket_head.drained = 0;
+
+	if (bucket_head.elems > bucket_head.thresh) {
+		free(b);
+	} else {
+		b->next = bucket_head.first;
+		bucket_head.first = b;
+		bucket_head.elems++;
+	}
+}
+
+
+#ifdef DEBUG
+unsigned int hashtable_checkref(hashtable *h, const void *key)
+{
+	unsigned hits = 0;
+	unsigned depth;
+	size_t i = 0;
+	struct bucket *b;
+	size_t hashval;
+
+	while (i < h->hash_size) {
+		b = h->buckets[i];
+		depth = 0;
+		while (b) {
+			if (h->compar(key, b->keyptr) == 0) {
+				if (hits) LogDebug("hashtable_checkref(): at pos %u, depth %u",
+											i, depth);
+				hits++;
+			}
+			hashval = h->hashfunc(b->keyptr);
+			if ((hashval % h->hash_size) != i) {
+				LogDebug("h->hashfunc(b->keyptr)= %u "
+					"h->hash_size %u, should be at %u, is at %u!",
+					hashval, h->hash_size, hashval % h->hash_size, i);
+				abort();
+			}
+			b = b->next;
+			depth++;
+		}
+		i++;
+	}
+	if (hits>1) {
+		LogDebug("hashtable_checkref(): %u dupes", hits-1);
+		abort();
+	}
+	return hits;
+}
+#endif
+
+static struct bucket *hashtable_iterator_getnextlist(hashtable *h, size_t startpos)
+{
+	struct bucket *b = NULL;
+	size_t i = startpos;
+
+	assert(h->hash_size > 0);
+	while (i < h->hash_size) {
+		b = h->buckets[i];
+		if (b) break;
+		i++;
+	}
+
+	h->iterator.nextres = b;
+	h->iterator.curhash = i;
+#ifdef DEBUG_HASH
+	LogDebug("hashtable_iterator_getnextlist: found %sresult, start at pos %u, now at %u",
+			b?"":"no ", startpos,i);
+#endif
+	return b;
+}
+
+
+void *hashtable_iterator_getnext(hashtable *h)
+{
+	void *retval;
+	struct bucket *nextres;
+
+	assert(h != NULL);
+	nextres = h->iterator.nextres;
+#ifdef DEBUG_HASH
+	if (!nextres) LogDebug("hashtable_iterator_getnext(): initial h->iterator.nextres == NULL");
+#endif
+	if (h->iterator.curhash == 0 && !nextres)
+		nextres = hashtable_iterator_getnextlist(h, 0);
+
+	if (!nextres || h->iterator.curhash >= h->hash_size)
+		return NULL;
+
+	retval = nextres->elemptr;
+	h->iterator.nextres = nextres->next;
+
+	if (!h->iterator.nextres) /* no more buckets in this list, find next list */
+		(void)hashtable_iterator_getnextlist(h, h->iterator.curhash+1);
+#ifdef DEBUG_HASH
+	LogDebug("hashtable_iterator_getnext(): %s, next result: %s",
+			retval ? "ok": "(NULL)", h->iterator.nextres ? "yes" :"(NULL)");
+	if (h->iterator.nextres)
+		assert(hashtable_checkref(h,  h->iterator.nextres->keyptr) == 1);
+#endif
+	return retval;
+}
+
+
+void hashtable_iterator_reset(hashtable *h)
+{
+	assert(h != NULL);
+	h->iterator.curhash = 0;
+	h->iterator.nextres = NULL;
+}
+
+
+static void hashtable_initcb(hashtable *h, void (*freefunc)(void*),
+			int(*compar)(const void *key, const void *candidate),
+	                size_t (*hashfunc)(const void *key))
+{
+	assert (h != NULL);
+
+	h->hash_fillcount = 0;
+	h->hashfunc = hashfunc;
+	h->freefunc = freefunc;
+	h->compar = compar;
+
+	hashtable_iterator_reset(h);
+}
+
+
+static bool hashtable_initsize(hashtable *h, size_t size, void (*freefunc)(void*),
+				int(*compar)(const void *key, const void *candidate),
+		                size_t (*hashfunc)(const void *key))
+{
+	assert(size > 0);
+	assert(h != NULL);
+
+	h->buckets = calloc(size, sizeof(struct bucket *));
+	if (!h->buckets) return false;
+
+	h->hash_size = size;
+	hashtable_initcb(h, freefunc, compar, hashfunc);
+	hashtable_iterator_reset(h);
+	return true;
+}
+
+
+bool hashtable_init(hashtable *h, void (*freefunc)(void*),
+				int(*compar)(const void *key, const void *candidate),
+		                size_t (*hashfunc)(const void *key))
+{
+	return hashtable_initsize(h, INITIAL_HASHSIZE, freefunc, compar, hashfunc);
+}
+
+
+static bool rehash(hashtable *h, size_t newsize)
+{
+	hashtable newhash;
+	size_t i;
+#ifdef DEBUG
+	unsigned int collisions = 0;
+#endif
+
+	hashtable_stats(h);
+
+	if (newsize <= h->hash_size)
+		return true;
+
+	if (!hashtable_initsize(&newhash, newsize, h->freefunc, h->compar, h->hashfunc))
+		return false;
+
+	for (i=0; i < h->hash_size; i++) {
+		struct bucket *b = h->buckets[i];
+		while (b) {
+			size_t hashval;
+			struct bucket *relocate = b;
+
+			assert(relocate->keyptr != NULL);
+			assert(relocate->elemptr != NULL );
+
+			b = b->next;
+			hashval = h->hashfunc(relocate->keyptr) % newsize;
+			relocate->next = newhash.buckets[hashval];
+#ifdef DEBUG
+			if (newhash.buckets[hashval])
+				collisions++;
+#endif
+			newhash.buckets[hashval] = relocate;
+
+		}
+	}
+	free(h->buckets);
+	h->buckets = newhash.buckets;
+#ifdef DEBUG
+	Log(LOG_INFO, "Rehashed: %u -> %u elements, got %u collisions total", h->hash_size, newsize, collisions);
+#else
+	Log(LOG_INFO, "Rehashed: %u -> %u elements", h->hash_size, newsize);
+#endif
+	h->hash_size = newsize;
+	hashtable_stats(h);
+	return true;
+}
+
+
+void hashtable_free(hashtable *h)
+{
+	size_t i;
+
+	hashtable_iterator_reset(h);
+
+	for (i=0; i < h->hash_size; i++) {
+		struct bucket *b = h->buckets[i];
+
+		while (b) {
+			struct bucket *victim;
+			if (h->freefunc)
+				h->freefunc(b->elemptr);
+			victim = b;
+			b = b->next;
+			bucket_free(victim);
+		}
+	}
+
+	h->hash_size = 0;
+	free(h->buckets);
+	h->buckets = NULL;
+	hashtable_initcb(h, NULL, NULL, NULL);
+}
+
+
+bool hashtable_changekey(hashtable *h, const void *oldkey, const void *newkey)
+{
+	struct bucket *b, *reloc;
+	size_t newpos, oldpos;
+	bool ret = true;
+
+	assert(h->hash_size > 0);
+	assert(oldkey != NULL);
+	assert(newkey != NULL);
+	assert(oldkey != newkey);
+
+	errno = 0;
+	oldpos = h->hashfunc(oldkey) % h->hash_size;
+	reloc = hashtable_bucket_steal(h, oldkey);
+	if (!reloc) return false;
+
+	newpos = h->hashfunc(newkey) % h->hash_size;
+	if (newpos == oldpos) { /* uh, dupe? */
+		b = h->buckets[newpos];
+		while (b) {
+			if ((b->keyptr != oldkey) && (h->compar(newkey, b->keyptr) == 0)) {
+				errno = EEXIST; /* yep, dupe */
+				ret = false;
+				break;
+			}
+			b = b->next;
+		}
+	}
+
+	if (ret)
+		reloc->keyptr = newkey;
+	else
+		newpos = oldpos; /* put bucket back into old place */
+
+	reloc->next = h->buckets[newpos];
+	h->buckets[newpos] = reloc;
+	return ret;
+}
+
+
+bool hashtable_add(hashtable *h, const void *key, void *item)
+{
+	size_t pos;
+	struct bucket *b;
+
+	assert(h->hash_size > 0);
+	assert(key != NULL);
+	assert(item != NULL);
+
+	if ((h->hash_fillcount * 100 / h->hash_size) > 90)
+		rehash(h, get_larger_prime(h->hash_size));
+
+	errno = 0;
+	if (hashtable_bucket_get(h, key)) {
+		errno = EEXIST; /* dupe */
+		return false;
+	}
+
+	b = bucket_alloc();
+	if (!b) return false;
+
+	pos = h->hashfunc(key) % h->hash_size;
+	b->keyptr = key;
+	b->elemptr = item;
+	b->next = h->buckets[pos];
+	h->buckets[pos] = b;
+
+	h->hash_fillcount++;
+#ifdef DEBUG_HASH
+	LogDebug("hashtable_insert(): pos %u", pos);
+	assert(hashtable_checkref(h, key) == 1);
+#endif
+	return true;
+}
+
+
+void* hashtable_remove(hashtable *h, const void *key)
+{
+	void *res = NULL;
+	struct bucket *b = hashtable_bucket_steal(h, key);
+	if (b) {
+		if (b == h->iterator.nextres)
+			hashtable_iterator_getnext(h);
+
+		assert(hashtable_checkref(h, key) == 0);
+
+		assert(h->hash_fillcount > 0);
+		h->hash_fillcount--;
+		res = b->elemptr;
+		bucket_free(b);
+	}
+#ifdef DEBUG
+	assert(hashtable_get(h, key) == NULL);
+#endif
+	return res;
+}
+
+
+void* hashtable_get(hashtable *h, const void *key)
+{
+	struct bucket *b = hashtable_bucket_get(h, key);
+	return b ? b->elemptr : NULL;
+}
+
+
+void hashtable_delete(hashtable *h, const void *key)
+{
+	void *res = hashtable_remove(h, key);
+	if (res && h->freefunc)
+		h->freefunc(res);
+}
+
+
+static struct bucket *hashtable_bucket_get(hashtable *h, const void *key)
+{
+	size_t hash;
+	struct bucket *b;
+	assert(h->hash_size > 0);
+	assert(key != NULL);
+
+	hash = h->hashfunc(key) % h->hash_size;
+	b = h->buckets[hash];
+	while (b) {
+		assert(b->keyptr);
+		if (h->compar(key, b->keyptr) == 0)
+			break;
+		b = b->next;
+	}
+#ifdef DEBUG_HASH
+	LogDebug("hashtable_bucket_get(): At hashpos %u; found %s",
+					hash, b?"match":"nothing");
+#endif
+	return b;
+}
+
+
+static struct bucket *hashtable_bucket_steal(hashtable *h, const void *key)
+{
+	size_t hash;
+	struct bucket *b;
+	struct bucket *prev = NULL;
+	assert(h->hash_size > 0);
+	assert(key != NULL);
+
+	hash = h->hashfunc(key) % h->hash_size;
+	b = h->buckets[hash];
+	while (b) {
+		if (h->compar(key, b->keyptr) == 0)
+			break;
+
+		prev = b;
+		b = b->next;
+	}
+#ifdef DEBUG_HASH
+	LogDebug("hashtable_bucket_steal(): At hashpos %u; found %s",
+					hash, b?"match":"nothing");
+#endif
+	if (b) {
+		if (prev) prev->next = b->next;
+		else h->buckets[hash] = b->next;
+		b->next = NULL;
+
+		if (b == h->iterator.nextres)
+			hashtable_iterator_getnext(h);
+	}
+	return b;
+}
+
+
+#ifdef DEBUG
+static void hashtable_stats(const hashtable*const h)
+{
+	size_t max_length = 0, used_bucketlists = 0, bucketcnt, i;
+
+	assert(h->hash_size > 0); /* called from rehash(), so this should be != 0 */
+
+	for (i = 0; i < h->hash_size; i++) {
+		struct bucket *b = h->buckets[i];
+		bucketcnt = 0;
+		while (b) {
+			b = b->next;
+			bucketcnt++;
+		}
+		if (!bucketcnt)
+			continue;
+		used_bucketlists++;
+		if (bucketcnt > max_length)
+			max_length = bucketcnt;
+	 }
+
+	assert(used_bucketlists > 0);
+	assert(used_bucketlists <=  h->hash_fillcount);
+
+	Log(LOG_INFO, "HASHSTAT: Hashsize %u\tElements: %u\tUsed Bucketlists: %u\ttMax Length: %u",
+				 h->hash_size, h->hash_fillcount, used_bucketlists, max_length);
+}
+#endif
+
diff --git a/src/ngircd/hash.h b/src/ngircd/hash.h
index 577d1f5..3974cf3 100644
--- a/src/ngircd/hash.h
+++ b/src/ngircd/hash.h
@@ -13,14 +13,63 @@
  * Hash calculation (header)
  */
 
-
 #ifndef __hash_h__
 #define __hash_h__
+#include "portab.h"
 
 GLOBAL UINT32 Hash PARAMS((const char *String ));
 
+struct bucket;
 
-#endif
+/* do NOT access this structure directly */
+typedef struct {
+	struct bucket **buckets;
+	size_t hash_size;
+	size_t hash_fillcount;
+	size_t (*hashfunc)(const void *a);
+	void   (*freefunc)(void *a);		/* e.g. free() */
+	int(*compar)(const void *key, const void *candidate); /* e.g. strcmp */
+	struct {
+		size_t curhash;
+		struct bucket *nextres;
+	} iterator;
+} hashtable;
+
+/* return the 1st, 2nd, 3d, ... etc. record in h, in no particular order.
+ * do NOT ADD elements to the hashtable while iterating */
+void *hashtable_iterator_getnext(hashtable *h);
+
+/* rewind iterator */
+void hashtable_iterator_reset(hashtable *h);
+
+/* init hashtable.
+ freefunc: called for each record when hasthtable_free() or hashtable_delete() is called
+ compar(): must return 0 if *candidate is equal to *key
+ hashfunc(): return hash of *a */
+bool hashtable_init(hashtable *h, void (*freefunc)(void*),
+				int(*compar)(const void *key, const void *candidate),
+				size_t (*hashfunc)(const void *a));
+
+
+/* free table and call freefunc callback for all elems in h */
+void hashtable_free(hashtable *h);
 
+/* change key of a record */
+bool  hashtable_changekey(hashtable *h, const void *oldkey, const void *newkey);
 
-/* -eof- */
+/* add record *newelem with key *key to *h. Only pointers are stored, no copying is performed */
+bool  hashtable_add(hashtable *h, const void *key, void *newelem);
+
+/* remove record from hastable, but do not call freefunc(record) */
+void* hashtable_remove(hashtable *h, const void *key);
+
+/* remove and free record with key *k */
+void  hashtable_delete(hashtable *h, const void *key);
+
+/* return pointer to record with key *key, or NULL if key does not exist */
+void* hashtable_get(hashtable *h, const void *key);
+
+/* return number of records in h */
+static inline size_t hashtable_getfillcount(const hashtable *h) { return h->hash_fillcount; }
+
+#endif
diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c
index e959992..03c020a 100644
--- a/src/ngircd/irc-channel.c
+++ b/src/ngircd/irc-channel.c
@@ -24,6 +24,7 @@ static char UNUSED id[] = "$Id: irc-channel.c,v 1.45 2008/02/24 18:57:38 fw Exp
 
 #include "defines.h"
 #include "conn.h"
+#include "cl2chan.h"
 #include "client.h"
 #include "channel.h"
 #include "lists.h"
@@ -54,8 +55,8 @@ part_from_all_channels(CLIENT* client, CLIENT *target)
 	CL2CHAN *cl2chan;
 	CHANNEL *chan;
 
-	while ((cl2chan = Channel_FirstChannelOf(target))) {
-		chan = Channel_GetChannel(cl2chan);
+	while ((cl2chan = Cl2Chan_FirstChan(target))) {
+		chan = Cl2Chan_GetChannel(cl2chan);
 		assert( chan != NULL );
 		Channel_Part(target, client, Channel_Name(chan), Client_ID(target));
 	}
@@ -240,7 +241,8 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 		/* Local client? */
 		if (Client_Type(Client) == CLIENT_USER) {
 			/* Test if the user has reached his maximum channel count */
-			if ((Conf_MaxJoins > 0) && (Channel_CountForUser(Client) >= Conf_MaxJoins))
+
+			if ((Conf_MaxJoins > 0) && (Cl2Chan_CountForUser(Client) >= Conf_MaxJoins))
 				return IRC_WriteStrClient(Client, ERR_TOOMANYCHANNELS_MSG,
 							Client_ID(Client), channame);
 			if (!chan) {
@@ -476,7 +478,7 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 						return DISCONNECTED;
 				}
 			}
-			chan = Channel_Next( chan );
+			chan = Channel_Next();
 		}
 		
 		/* Get next name ... */
diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c
index 43f2c1b..e766bdc 100644
--- a/src/ngircd/irc-info.c
+++ b/src/ngircd/irc-info.c
@@ -25,6 +25,7 @@
 #include "ngircd.h"
 #include "conn-func.h"
 #include "conn-zip.h"
+#include "cl2chan.h"
 #include "client.h"
 #include "channel.h"
 #include "resolve.h"
@@ -225,7 +226,7 @@ IRC_LINKS( CLIENT *Client, REQUEST *Req )
 		{
 			if( ! IRC_WriteStrClient( target, RPL_LINKS_MSG, Client_ID( target ), Client_ID( c ), Client_ID( Client_TopServer( c ) ? Client_TopServer( c ) : Client_ThisServer( )), Client_Hops( c ), Client_Info( c ))) return DISCONNECTED;
 		}
-		c = Client_Next( c );
+		c = Client_Next();
 	}
 	
 	IRC_SetPenalty( target, 1 );
@@ -382,7 +383,7 @@ IRC_NAMES( CLIENT *Client, REQUEST *Req )
 		if( ! IRC_Send_NAMES( from, chan )) return DISCONNECTED;
 
 		/* naechster Channel */
-		chan = Channel_Next( chan );
+		chan = Channel_Next();
 	}
 
 	/* Nun noch alle Clients ausgeben, die in keinem Channel sind */
@@ -390,7 +391,7 @@ IRC_NAMES( CLIENT *Client, REQUEST *Req )
 	snprintf( rpl, sizeof( rpl ), RPL_NAMREPLY_MSG, Client_ID( from ), "*", "*" );
 	while( c )
 	{
-		if(( Client_Type( c ) == CLIENT_USER ) && ( Channel_FirstChannelOf( c ) == NULL ) && ( ! strchr( Client_Modes( c ), 'i' )))
+		if(( Client_Type( c ) == CLIENT_USER ) && ( Cl2Chan_FirstChan( c ) == NULL ) && ( ! strchr( Client_Modes( c ), 'i' )))
 		{
 			/* Okay, das ist ein User: anhaengen */
 			if( rpl[strlen( rpl ) - 1] != ':' ) strlcat( rpl, " ", sizeof( rpl ));
@@ -404,8 +405,7 @@ IRC_NAMES( CLIENT *Client, REQUEST *Req )
 			}
 		}
 
-		/* naechster Client */
-		c = Client_Next( c );
+		c = Client_Next();
 	}
 	if( rpl[strlen( rpl ) - 1] != ':')
 	{
@@ -740,9 +740,9 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 	if (!is_member && strchr(Channel_Modes(Chan), 's'))
 		return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan));
 
-	cl2chan = Channel_FirstMember(Chan);
-	for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
-		c = Channel_GetClient(cl2chan);
+	cl2chan = Cl2Chan_FirstMember(Chan);
+	for (; cl2chan ; cl2chan = Cl2Chan_NextMember(Chan)) {
+		c = Cl2Chan_GetClient(cl2chan);
 
 		client_modes = Client_Modes(c);
 		is_ircop = strchr(client_modes, 'o') != NULL;
@@ -821,7 +821,7 @@ IRC_WHO( CLIENT *Client, REQUEST *Req )
 		}
 	}
 
-	for (c = Client_First(); c != NULL; c = Client_Next(c)) {
+	for (c = Client_First(); c != NULL; c = Client_Next()) {
 		if (Client_Type(c) != CLIENT_USER)
 			continue;
 		 /*
@@ -862,19 +862,20 @@ IRC_WHO( CLIENT *Client, REQUEST *Req )
 			strlcat(flags, "*", sizeof(flags));
 
 		/* Search suitable channel */
-		cl2chan = Channel_FirstChannelOf(c);
+		cl2chan = Cl2Chan_FirstChan(c);
 		while (cl2chan) {
-			cn = Channel_GetChannel(cl2chan);
+			cn = Cl2Chan_GetChannel(cl2chan);
 			if (Channel_IsMemberOf(cn, Client) ||
 				    !strchr(Channel_Modes(cn), 's'))
 			{
 				channelname = Channel_Name(cn);
 				break;
 			}
-			cl2chan = Channel_NextChannelOf(c, cl2chan);
+			cl2chan = Cl2Chan_NextChan( c );
 		}
+
 		if (cl2chan) {
-			chan = Channel_GetChannel(cl2chan);
+			chan = Cl2Chan_GetChannel(cl2chan);
 			chan_user_modes = Channel_UserModes(chan, c);
 			strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags));
 		} else
@@ -937,14 +938,14 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 
 	/* Channels */
 	snprintf( str, sizeof( str ), RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c ));
-	cl2chan = Channel_FirstChannelOf( c );
+	cl2chan = Cl2Chan_FirstChan( c );
 	while( cl2chan )
 	{
-		chan = Channel_GetChannel( cl2chan );
+		chan = Cl2Chan_GetChannel( cl2chan );
 		assert( chan != NULL );
 
 		/* next */
-		cl2chan = Channel_NextChannelOf( c, cl2chan );
+		cl2chan = Cl2Chan_NextChan( c );
 
 		/* Secret channel? */
 		if( strchr( Channel_Modes( chan ), 's' ) && ! Channel_IsMemberOf( chan, Client )) continue;
@@ -1243,10 +1244,10 @@ IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
 
 	/* Alle Mitglieder suchen */
 	snprintf( str, sizeof( str ), RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan ));
-	cl2chan = Channel_FirstMember( Chan );
+	cl2chan = Cl2Chan_FirstMember( Chan );
 	while( cl2chan )
 	{
-		cl = Channel_GetClient( cl2chan );
+		cl = Cl2Chan_GetClient( cl2chan );
 
 		if( strchr( Client_Modes( cl ), 'i' )) is_visible = false;
 		else is_visible = true;
@@ -1268,7 +1269,7 @@ IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
 		}
 
 		/* naechstes Mitglied suchen */
-		cl2chan = Channel_NextMember( Chan, cl2chan );
+		cl2chan = Cl2Chan_NextMember( Chan );
 	}
 	if( str[strlen( str ) - 1] != ':')
 	{
@@ -1280,7 +1281,6 @@ IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
 } /* IRC_Send_NAMES */
 
 
-
 /**
  * Send the ISUPPORT numeric (005).
  * This numeric indicates the features that are supported by this server.
diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c
index e94b62f..f7f227d 100644
--- a/src/ngircd/irc-login.c
+++ b/src/ngircd/irc-login.c
@@ -244,7 +244,9 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 			     Client_Conn( Client ));
 
 			/* Register new nickname of this client */
-			Client_SetID( target, Req->argv[0] );
+			if (!Client_SetID( target, Req->argv[0] ))
+				return IRC_WriteStrClient( Client, ERR_NICKNAMEINUSE_MSG,
+						Client_ID( Client ),  Req->argv[0] );
 
 			/* If we received a valid USER command already then
 			 * register the new client! */
diff --git a/src/ngircd/irc-oper.c b/src/ngircd/irc-oper.c
index c9286d7..1a5c839 100644
--- a/src/ngircd/irc-oper.c
+++ b/src/ngircd/irc-oper.c
@@ -292,7 +292,7 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 	if (!from)
 		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix);
 
-	for (to=Client_First(); to != NULL; to=Client_Next(to)) {
+	for (to=Client_First(); to != NULL; to=Client_Next()) {
 		if (Client_Conn(to) < 0) /* no local connection or WALLOPS origin */
 			continue;
 
diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c
index d342ffa..cf43bfa 100644
--- a/src/ngircd/irc-server.c
+++ b/src/ngircd/irc-server.c
@@ -28,6 +28,7 @@ static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp
 #include "conn.h"
 #include "conn-zip.h"
 #include "conf.h"
+#include "cl2chan.h"
 #include "client.h"
 #include "channel.h"
 #include "irc-write.h"
@@ -94,7 +95,11 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;
 
 		/* Server-Strukturen fuellen ;-) */
-		Client_SetID( Client, Req->argv[0] );
+		if (!Client_SetID( Client, Req->argv[0] )) {
+			Conn_Close( Client_Conn( Client ), NULL, "Internal Server Error", true);
+			return DISCONNECTED;
+		}
+
 		Client_SetHops( Client, 1 );
 		Client_SetInfo( Client, Req->argv[Req->argc - 1] );
 
diff --git a/src/ngircd/irc-write.c b/src/ngircd/irc-write.c
index 61131b6..a67a81f 100644
--- a/src/ngircd/irc-write.c
+++ b/src/ngircd/irc-write.c
@@ -28,6 +28,7 @@ static char UNUSED id[] = "$Id: irc-write.c,v 1.21 2006/08/12 11:56:24 fw Exp $"
 
 #include "defines.h"
 #include "conn-func.h"
+#include "cl2chan.h"
 #include "client.h"
 #include "channel.h"
 
@@ -177,10 +178,10 @@ va_dcl
 
 	/* An alle Clients, die in den selben Channels sind.
 	 * Dabei aber nur einmal je Remote-Server */
-	cl2chan = Channel_FirstMember( Chan );
+	cl2chan = Cl2Chan_FirstMember( Chan );
 	while( cl2chan )
 	{
-		c = Channel_GetClient( cl2chan );
+		c = Cl2Chan_GetClient( cl2chan );
 		if( ! Remote )
 		{
 			if( Client_Conn( c ) <= NONE ) c = NULL;
@@ -195,7 +196,7 @@ va_dcl
 			if( Client_Type( c ) == CLIENT_SERVER )	Conn_SetFlag( conn, SEND_TO_SERVER );
 			else Conn_SetFlag( conn, SEND_TO_USER );
 		}
-		cl2chan = Channel_NextMember( Chan, cl2chan );
+		cl2chan = Cl2Chan_NextMember( Chan );
 	}
 
 	/* Senden: alle Verbindungen durchgehen ... */
@@ -310,7 +311,7 @@ va_dcl
 			/* Ziel-Server gefunden. Nun noch pruefen, ob Flags stimmen */
 			if(( Flag == '\0' ) || ( strchr( Client_Flags( c ), Flag ) != NULL )) IRC_WriteStrClientPrefix( c, Prefix, "%s", buffer );
 		}
-		c = Client_Next( c );
+		c = Client_Next();
 	}
 } /* IRC_WriteStrServersPrefixFlag */
 
@@ -353,15 +354,15 @@ va_dcl
 
 	/* An alle Clients, die in einem Channel mit dem "Ausloeser" sind,
 	 * den Text schicken. An Remote-Server aber jeweils nur einmal. */
-	chan_cl2chan = Channel_FirstChannelOf( Client );
+	chan_cl2chan = Cl2Chan_FirstChan( Client );
 	while( chan_cl2chan )
 	{
 		/* Channel des Users durchsuchen */
-		chan = Channel_GetChannel( chan_cl2chan );
-		cl2chan = Channel_FirstMember( chan );
+		chan = Cl2Chan_GetChannel( chan_cl2chan );
+		cl2chan = Cl2Chan_FirstMember( chan );
 		while( cl2chan )
 		{
-			c = Channel_GetClient( cl2chan );
+			c = Cl2Chan_GetClient( cl2chan );
 			if( ! Remote )
 			{
 				if( Client_Conn( c ) <= NONE ) c = NULL;
@@ -376,11 +377,11 @@ va_dcl
 				if( Client_Type( c ) == CLIENT_SERVER ) Conn_SetFlag( conn, SEND_TO_SERVER );
 				else Conn_SetFlag( conn, SEND_TO_USER );
 			}
-			cl2chan = Channel_NextMember( chan, cl2chan );
+			cl2chan = Cl2Chan_NextMember( chan);
 		}
 
 		/* naechsten Channel */
-		chan_cl2chan = Channel_NextChannelOf( Client, chan_cl2chan );
+		chan_cl2chan = Cl2Chan_NextChan( Client);
 	}
 
 	/* Senden: alle Verbindungen durchgehen ... */
diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c
index 0418e7b..2483f46 100644
--- a/src/ngircd/irc.c
+++ b/src/ngircd/irc.c
@@ -279,7 +279,7 @@ IRC_TRACE( CLIENT *Client, REQUEST *Req )
 				if( ! IRC_WriteStrClient( from, RPL_TRACEOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
 			}
 		}
-		c = Client_Next( c );
+		c = Client_Next();
 	}
 
 	IRC_SetPenalty( Client, 3 );
diff --git a/src/ngircd/log.c b/src/ngircd/log.c
index 9c4daf5..ecff0d3 100644
--- a/src/ngircd/log.c
+++ b/src/ngircd/log.c
@@ -363,7 +363,7 @@ Wall_ServerNotice( char *Msg )
 			IRC_WriteStrClient(c, "NOTICE %s :%s%s", Client_ID(c),
 							NOTICE_TXTPREFIX, Msg);
 
-		c = Client_Next( c );
+		c = Client_Next();
 	}
 } /* Wall_ServerNotice */
 
diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c
index dd24016..2afe74f 100644
--- a/src/ngircd/numeric.c
+++ b/src/ngircd/numeric.c
@@ -26,6 +26,7 @@ static char UNUSED id[] = "$Id: numeric.c,v 1.1 2007/11/21 12:20:32 alex Exp $";
 #include "conn.h"
 #include "conf.h"
 #include "conn.h"
+#include "cl2chan.h"
 #include "client.h"
 #include "channel.h"
 #include "irc-write.h"
@@ -126,7 +127,7 @@ Synchronize_Lists(CLIENT * Client)
 			elem = Lists_GetNext(elem);
 		}
 
-		c = Channel_Next(c);
+		c = Channel_Next();
 	}
 	return CONNECTED;
 }
@@ -203,7 +204,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	while (c) {
 		if (Client_Hops(c) > max_hops)
 			max_hops = Client_Hops(c);
-		c = Client_Next(c);
+		c = Client_Next();
 	}
 
 	/* Inform the new server about all other servers, and announce the
@@ -211,7 +212,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	 * to do this "in order" and can't introduce servers of which the
 	 * "toplevel server" isn't known already. */
 	for (i = 0; i < (max_hops + 1); i++) {
-		for (c = Client_First(); c != NULL; c = Client_Next(c)) {
+		for (c = Client_First(); c != NULL; c = Client_Next()) {
 			if (Client_Type(c) != CLIENT_SERVER)
 				continue;	/* not a server */
 			if (Client_Hops(c) != i)
@@ -231,7 +232,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 			if (!Announce_User(Client, c))
 				return DISCONNECTED;
 		}
-		c = Client_Next(c);
+		c = Client_Next();
 	}
 
 	/* Announce all channels to the new server */
@@ -246,10 +247,10 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 #endif
 
 		/* Get all the members of this channel */
-		cl2chan = Channel_FirstMember(chan);
+		cl2chan = Cl2Chan_FirstMember(chan);
 		snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(chan));
 		while (cl2chan) {
-			cl = Channel_GetClient(cl2chan);
+			cl = Cl2Chan_GetClient(cl2chan);
 			assert(cl != NULL);
 
 			/* Nick name, with modes (if applicable) */
@@ -269,7 +270,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 					 Channel_Name(chan));
 			}
 
-			cl2chan = Channel_NextMember(chan, cl2chan);
+			cl2chan = Cl2Chan_NextMember(chan);
 		}
 
 		/* Data left in the buffer? */
@@ -280,7 +281,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 		}
 
 		/* Get next channel ... */
-		chan = Channel_Next(chan);
+		chan = Channel_Next();
 	}
 
 #ifdef IRCPLUS
