Index: Makefile
===================================================================
--- Makefile	(revision ebaebfe35c82460581fa6db518d8848996c9a0f4)
+++ Makefile	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
@@ -11,5 +11,5 @@
 # Program variables
 #objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS)
-objects = bitlbee.o help.o ipc.o irc.o irc_commands.o irc_send.o irc_user.o nick.o set.o
+objects = bitlbee.o help.o ipc.o irc.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o set.o
 headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h
 subdirs = lib protocols
Index: irc.c
===================================================================
--- irc.c	(revision ebaebfe35c82460581fa6db518d8848996c9a0f4)
+++ irc.c	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
@@ -586,5 +586,37 @@
 		else
 		{
+			irc_channel_t *ic;
+			irc_user_t *iu = irc->user;
+			
+			irc->user = irc_user_new( irc, iu->nick );
+			irc->user->user = iu->user;
+			irc->user->fullname = iu->fullname;
+			g_free( iu->nick );
+			g_free( iu );
+			
+			irc->umode[0] = '\0';
+			/*irc_umode_set( irc, "+" UMODE, 1 );*/
+			
+			if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
+				ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
+			
+			irc->status |= USTATUS_LOGGED_IN;
+			
+			/* This is for bug #209 (use PASS to identify to NickServ). */
+			if( irc->password != NULL )
+			{
+				char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
+				
+				/*irc_setpass( irc, NULL );*/
+				/*root_command( irc, send_cmd );*/
+				g_free( send_cmd[1] );
+			}
+			
 			irc_send_login( irc );
+			
+			ic = irc_channel_new( irc, ROOT_CHAN );
+			irc_channel_set_topic( ic, CONTROL_TOPIC );
+			irc_channel_add_user( ic, irc->user );
+			
 			return 1;
 		}
Index: irc.h
===================================================================
--- irc.h	(revision ebaebfe35c82460581fa6db518d8848996c9a0f4)
+++ irc.h	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
@@ -73,7 +73,6 @@
 	struct account *accounts;
 	GSList *file_transfers;
-	struct chat *chatrooms;
 	
-	GSList *users;
+	GSList *users, *channels;
 	GHashTable *nick_user_hash;
 	GHashTable *watches;
@@ -106,4 +105,20 @@
 } irc_user_t;
 
+typedef enum
+{
+	IRC_CHANNEL_JOINED = 1,
+} irc_channel_flags_t;
+
+typedef struct irc_channel
+{
+	irc_t *irc;
+	int flags;
+	char *name;
+	char *topic;
+	char mode[8];
+	GSList *users;
+	struct set *set;
+} irc_channel_t;
+
 #include "user.h"
 
@@ -125,4 +140,10 @@
 int irc_check_login( irc_t *irc );
 
+/* irc_channel.c */
+irc_channel_t *irc_channel_new( irc_t *irc, const char *name );
+int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu );
+int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu );
+int irc_channel_set_topic( irc_channel_t *ic, const char *topic );
+
 /* irc_commands.c */
 void irc_exec( irc_t *irc, char **cmd );
@@ -132,5 +153,9 @@
 void irc_send_login( irc_t *irc );
 void irc_send_motd( irc_t *irc );
-int irc_usermsg( irc_t *irc, char *format, ... );
+void irc_usermsg( irc_t *irc, char *format, ... );
+void irc_send_join( irc_channel_t *ic, irc_user_t *iu );
+void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason );
+void irc_send_names( irc_channel_t *ic );
+void irc_send_topic( irc_channel_t *ic );
 
 /* irc_user.c */
Index: irc_channel.c
===================================================================
--- irc_channel.c	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
+++ irc_channel.c	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
@@ -0,0 +1,88 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* The IRC-based UI - Representing (virtual) channels.                  */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+
+irc_channel_t *irc_channel_new( irc_t *irc, const char *name )
+{
+	irc_channel_t *ic;
+	
+	if( strchr( CTYPES, name[0] ) == NULL || !nick_ok( name + 1 ) )
+		return NULL;
+	
+	ic = g_new0( irc_channel_t, 1 );
+	ic->irc = irc;
+	ic->name = g_strdup( name );
+	strcpy( ic->mode, CMODE );
+	
+	irc_channel_add_user( ic, irc->root );
+	
+	irc->channels = g_slist_prepend( irc->channels, ic );
+	
+	return ic;
+}
+
+int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu )
+{
+	if( g_slist_find( ic->users, iu ) != NULL )
+		return 0;
+	
+	ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp );
+	
+	if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED )
+	{
+		ic->flags |= IRC_CHANNEL_JOINED;
+		irc_send_join( ic, iu );
+	}
+	
+	return 1;
+}
+
+int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu )
+{
+	if( g_slist_find( ic->users, iu ) == NULL )
+		return 0;
+	
+	ic->users = g_slist_remove( ic->users, iu );
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		irc_send_part( ic, iu, "" );
+	
+	if( iu == ic->irc->user )
+		ic->flags &= ~IRC_CHANNEL_JOINED;
+	
+	return 1;
+}
+
+int irc_channel_set_topic( irc_channel_t *ic, const char *topic )
+{
+	g_free( ic->topic );
+	ic->topic = g_strdup( topic );
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		irc_send_topic( ic );
+	
+	return 1;
+}
Index: irc_send.c
===================================================================
--- irc_send.c	(revision ebaebfe35c82460581fa6db518d8848996c9a0f4)
+++ irc_send.c	(revision 4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0)
@@ -34,19 +34,10 @@
 	g_vsnprintf( text, IRC_MAX_LINE, format, params );
 	va_end( params );
+	
 	irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text );
-	
-	return;
 }
 
 void irc_send_login( irc_t *irc )
 {
-	irc_user_t *iu = irc->user;
-	
-	irc->user = irc_user_new( irc, iu->nick );
-	irc->user->user = iu->user;
-	irc->user->fullname = iu->fullname;
-	g_free( iu->nick );
-	g_free( iu );
-	
 	irc_send_num( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->user->nick );
 	irc_send_num( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->root->host );
@@ -57,6 +48,4 @@
 	                        CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
 	irc_send_motd( irc );
-	irc->umode[0] = '\0';
-	/*irc_umode_set( irc, "+" UMODE, 1 );*/
 	
 	irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
@@ -66,19 +55,4 @@
 	                  "If you already have an account on this server, just use the "
 	                  "\x02identify\x02 command to identify yourself." );
-	
-	if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
-		ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
-	
-	irc->status |= USTATUS_LOGGED_IN;
-	
-	/* This is for bug #209 (use PASS to identify to NickServ). */
-	if( irc->password != NULL )
-	{
-		char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
-		
-		/*irc_setpass( irc, NULL );*/
-		/*root_command( irc, send_cmd );*/
-		g_free( send_cmd[1] );
-	}
 }
 
@@ -136,5 +110,5 @@
 
 /* FIXME/REPLACEME */
-int irc_usermsg( irc_t *irc, char *format, ... )
+void irc_usermsg( irc_t *irc, char *format, ... )
 {
 	char text[1024];
@@ -148,6 +122,69 @@
 	fprintf( stderr, "%s\n", text );
 	
-	return 1;
-	
 	/*return( irc_msgfrom( irc, u->nick, text ) );*/
 }
+
+void irc_send_join( irc_channel_t *ic, irc_user_t *iu )
+{
+	irc_t *irc = ic->irc;
+	
+	irc_write( irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name );
+	
+	if( iu == irc->user )
+	{
+		irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode );
+		irc_send_names( ic );
+		irc_send_topic( ic );
+	}
+}
+
+void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason )
+{
+	irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason );
+}
+
+void irc_send_names( irc_channel_t *ic )
+{
+	GSList *l;
+	irc_user_t *iu;
+	char namelist[385] = "";
+	struct groupchat *c = NULL;
+	char *ops = set_getstr( &ic->irc->b->set, "ops" );
+	
+	/* RFCs say there is no error reply allowed on NAMES, so when the
+	   channel is invalid, just give an empty reply. */
+	for( l = ic->users; l; l = l->next )
+	{
+		irc_user_t *iu = l->data;
+		
+		if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 )
+		{
+			irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
+			*namelist = 0;
+		}
+		
+		/*
+		if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
+			strcat( namelist, "+" );
+		else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
+		         ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
+			strcat( namelist, "@" );
+		*/
+		
+		strcat( namelist, iu->nick );
+		strcat( namelist, " " );
+	}
+	
+	if( *namelist )
+		irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
+	
+	irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name );
+}
+
+void irc_send_topic( irc_channel_t *ic )
+{
+	if( ic->topic )
+		irc_send_num( ic->irc, 332, "%s :%s", ic->name, ic->topic );
+	else
+		irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name );
+}
