about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ipaddr/.gitignore1
-rw-r--r--src/ipaddr/Makefile.ng (renamed from src/ipaddr/Makefile.am)8
-rw-r--r--src/ngircd/.gitignore1
-rw-r--r--src/ngircd/Makefile.ng (renamed from src/ngircd/Makefile.am)12
-rw-r--r--src/ngircd/channel.c119
-rw-r--r--src/ngircd/conf-ssl.h10
-rw-r--r--src/ngircd/conf.c10
-rw-r--r--src/ngircd/conf.h3
-rw-r--r--src/ngircd/conn-ssl.c26
-rw-r--r--src/ngircd/defines.h2
-rw-r--r--src/ngircd/irc-channel.c28
-rw-r--r--src/ngircd/irc-info.c73
-rw-r--r--src/ngircd/irc-login.c9
-rw-r--r--src/ngircd/irc-mode.c144
-rw-r--r--src/ngircd/irc-op.c7
-rw-r--r--src/ngircd/irc-oper.c2
-rw-r--r--src/ngircd/irc-server.c28
-rw-r--r--src/ngircd/messages.h6
-rw-r--r--src/ngircd/ngircd.c2
-rw-r--r--src/ngircd/numeric.c11
-rw-r--r--src/portab/.gitignore1
-rw-r--r--src/portab/Makefile.am34
-rw-r--r--src/portab/Makefile.ng35
-rw-r--r--src/testsuite/.gitignore1
-rw-r--r--src/testsuite/Makefile.ng (renamed from src/testsuite/Makefile.am)21
-rwxr-xr-xsrc/testsuite/getpid.sh2
-rw-r--r--src/testsuite/mode-test.e68
-rwxr-xr-xsrc/testsuite/stress-server.sh7
-rwxr-xr-xsrc/testsuite/tests.sh12
-rw-r--r--src/tool/.gitignore1
-rw-r--r--src/tool/Makefile.ng (renamed from src/tool/Makefile.am)8
31 files changed, 452 insertions, 240 deletions
diff --git a/src/ipaddr/.gitignore b/src/ipaddr/.gitignore
new file mode 100644
index 00000000..08a6d725
--- /dev/null
+++ b/src/ipaddr/.gitignore
@@ -0,0 +1 @@
+Makefile.am
diff --git a/src/ipaddr/Makefile.am b/src/ipaddr/Makefile.ng
index 6ce299f2..ecfce295 100644
--- a/src/ipaddr/Makefile.am
+++ b/src/ipaddr/Makefile.ng
@@ -3,9 +3,11 @@
 # (c) 2008 Florian Westphal <fw@strlen.de>, public domain.
 #
 
-AUTOMAKE_OPTIONS = ../portab/ansi2knr
+__ng_Makefile_am_template__
 
-INCLUDES = -I$(srcdir)/../portab
+EXTRA_DIST = Makefile.ng
+
+AM_CPPFLAGS = -I$(srcdir)/../portab
 
 noinst_LIBRARIES = libngipaddr.a
 
@@ -14,6 +16,6 @@ libngipaddr_a_SOURCES = ng_ipaddr.c
 noinst_HEADERS = ng_ipaddr.h
 
 maintainer-clean-local:
-	rm -f Makefile Makefile.in
+	rm -f Makefile Makefile.in Makefile.am
 
 # -eof-
diff --git a/src/ngircd/.gitignore b/src/ngircd/.gitignore
index c25ba5e3..d1148bfb 100644
--- a/src/ngircd/.gitignore
+++ b/src/ngircd/.gitignore
@@ -1,3 +1,4 @@
+Makefile.am
 check-help
 check-version
 ngircd
diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.ng
index c1fd4240..27b5c408 100644
--- a/src/ngircd/Makefile.am
+++ b/src/ngircd/Makefile.ng
@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
 #
 # 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
@@ -9,9 +9,11 @@
 # Please read the file COPYING, README and AUTHORS for more information.
 #
 
-AUTOMAKE_OPTIONS = ../portab/ansi2knr
+__ng_Makefile_am_template__
 
-INCLUDES = -I$(srcdir)/../portab -I$(srcdir)/../tool -I$(srcdir)/../ipaddr
+EXTRA_DIST = Makefile.ng
+
+AM_CPPFLAGS = -I$(srcdir)/../portab -I$(srcdir)/../tool -I$(srcdir)/../ipaddr
 
 LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
  -varuse -retvalother -emptyret -unrecog
@@ -105,7 +107,7 @@ clean-local:
 	rm -f check-version check-help lint.out
 
 maintainer-clean-local:
-	rm -f Makefile Makefile.in
+	rm -f Makefile Makefile.in Makefile.am
 
 check-version: Makefile
 	echo "#!/bin/sh" > check-version
@@ -124,7 +126,7 @@ lint:
 	for f in *.c; do \
 	 echo "checking $$f ..."; \
 	 splint $$f $(LINTARGS) -I$(srcdir) -I$(srcdir)/.. \
-	  $(INCLUDES) $(AM_CFLAGS) >lint.out 2>&1; \
+	  $(AM_CPPFLAGS) $(AM_CFLAGS) >lint.out 2>&1; \
 	 grep "no warnings" lint.out > /dev/null 2>&1; \
 	 if [ $$? -ne 0 ]; then \
 	  waswarning=1; \
diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c
index f0a9525d..10b05405 100644
--- a/src/ngircd/channel.c
+++ b/src/ngircd/channel.c
@@ -299,6 +299,8 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 	     const char *Reason )
 {
 	CHANNEL *chan;
+	char *ptr, *target_modes;
+	bool can_kick = false;
 
 	assert(Peer != NULL);
 	assert(Target != NULL);
@@ -319,14 +321,51 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 		/* Check that user is on the specified channel */
 		if (!Channel_IsMemberOf(chan, Origin)) {
 			IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG,
-					   Client_ID(Origin), Name);
+					    Client_ID(Origin), Name);
 			return;
 		}
+	}
+
+	if(Client_Type(Peer) == CLIENT_USER) {
+		/* Check if client has the rights to kick target */
+		ptr = Channel_UserModes(chan, Peer);
+		target_modes = Channel_UserModes(chan, Target);
+		while(*ptr) {
+			/* Owner can kick everyone */
+			if ( *ptr == 'q') {
+				can_kick = true;
+				break;
+			}
+			/* Admin can't kick owner */
+			if ( *ptr == 'a' ) {
+				if (!strchr(target_modes, 'q')) {
+					can_kick = true;
+					break;
+				}
+			}
+			/* Op can't kick owner | admin */
+			if ( *ptr == 'o' ) {
+				if (!strchr(target_modes, 'q') &&
+				    !strchr(target_modes, 'a')) {
+					can_kick = true;
+					break;
+				}
+			}
+			/* Half Op can't kick owner | admin | op */ 
+			if ( *ptr == 'h' ) {
+				if (!strchr(target_modes, 'q') &&
+				    !strchr(target_modes, 'a') &&
+				    !strchr(target_modes, 'o')) {
+					can_kick = true;
+					break;
+				}
+			}
+			ptr++;
+		}
 
-		/* Check if user has operator status */
-		if (!strchr(Channel_UserModes(chan, Origin), 'o')) {
-			IRC_WriteStrClient(Origin, ERR_CHANOPRIVSNEEDED_MSG,
-					   Client_ID(Origin), Name);
+		if(!can_kick) {
+			IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG,
+				Client_ID(Origin), Name);
 			return;
 		}
 	}
@@ -812,9 +851,9 @@ Channel_SetMaxUsers(CHANNEL *Chan, unsigned long Count)
 static bool
 Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 {
-	bool is_member, has_voice, is_op;
+	bool is_member, has_voice, is_halfop, is_op, is_chanadmin, is_owner;
 
-	is_member = has_voice = is_op = false;
+	is_member = has_voice = is_halfop = is_op = is_chanadmin = is_owner = false;
 
 	/* The server itself always can send messages :-) */
 	if (Client_ThisServer() == From)
@@ -824,8 +863,14 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 		is_member = true;
 		if (strchr(Channel_UserModes(Chan, From), 'v'))
 			has_voice = true;
+		if (strchr(Channel_UserModes(Chan, From), 'h'))
+			is_halfop = true;
 		if (strchr(Channel_UserModes(Chan, From), 'o'))
 			is_op = true;
+		if (strchr(Channel_UserModes(Chan, From), 'a'))
+			is_chanadmin = true;
+		if (strchr(Channel_UserModes(Chan, From), 'q'))
+			is_owner = true;
 	}
 
 	/*
@@ -841,7 +886,7 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 	    && !Client_HasMode(From, 'o'))
 		return false;
 
-	if (is_op || has_voice)
+	if (has_voice || is_halfop || is_op || is_chanadmin || is_owner)
 		return true;
 
 	if (strchr(Channel_Modes(Chan), 'm'))
@@ -1196,64 +1241,6 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key)
 } /* Channel_CheckKey */
 
 
-/**
- * Check wether a client is allowed to administer a channel or not.
- *
- * @param Chan		The channel to test.
- * @param Client	The client from which the command has been received.
- * @param Origin	The originator of the command (or NULL).
- * @param OnChannel	Set to true if the originator is member of the channel.
- * @param AdminOk	Set to true if the client is allowed to do
- *			administrative tasks on this channel.
- * @param UseServerMode	Set to true if ngIRCd should emulate "server mode",
- *			that is send commands as if originating from a server
- *			and not the originator of the command.
- */
-GLOBAL void
-Channel_CheckAdminRights(CHANNEL *Chan, CLIENT *Client, CLIENT *Origin,
-			 bool *OnChannel, bool *AdminOk, bool *UseServerMode)
-{
-	assert (Chan != NULL);
-	assert (Client != NULL);
-	assert (OnChannel != NULL);
-	assert (AdminOk != NULL);
-	assert (UseServerMode != NULL);
-
-	/* Use the client as origin, if no origin has been given (no prefix?) */
-	if (!Origin)
-		Origin = Client;
-
-	*OnChannel = false;
-	*AdminOk = false;
-	*UseServerMode = false;
-
-	if (Client_Type(Client) != CLIENT_USER
-	    && Client_Type(Client) != CLIENT_SERVER
-	    && Client_Type(Client) != CLIENT_SERVICE)
-		return;
-
-	/* Allow channel administration if the client is a server or service */
-	if (Client_Type(Client) != CLIENT_USER) {
-		*AdminOk = true;
-		return;
-	}
-
-	*OnChannel = Channel_IsMemberOf(Chan, Origin);
-
-	if (*OnChannel && strchr(Channel_UserModes(Chan, Origin), 'o')) {
-		/* User is a channel operator */
-		*AdminOk = true;
-	} else if (Conf_OperCanMode) {
-		/* IRC operators are allowed to administer channels as well */
-		if (Client_OperByMe(Origin)) {
-			*AdminOk = true;
-			if (Conf_OperServerMode)
-				*UseServerMode = true;
-		}
-	}
-} /* Channel_CheckAdminRights */
-
-
 static CL2CHAN *
 Get_First_Cl2Chan( CLIENT *Client, CHANNEL *Chan )
 {
diff --git a/src/ngircd/conf-ssl.h b/src/ngircd/conf-ssl.h
index 3fab579c..428bcf45 100644
--- a/src/ngircd/conf-ssl.h
+++ b/src/ngircd/conf-ssl.h
@@ -39,13 +39,9 @@ struct ConnSSL_State {
 #endif
 };
 
-bool
-ConnSSL_InitLibrary(void);
-#else
-static inline bool
-ConnSSL_InitLibrary(void)
-{ return true; }
-#endif /* SSL_SUPPORT */
+#endif
+
+bool	ConnSSL_InitLibrary(void);
 
 #endif /* conf_ssl_h */
 
diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c
index 5853926e..4ac37ad4 100644
--- a/src/ngircd/conf.c
+++ b/src/ngircd/conf.c
@@ -104,6 +104,8 @@ ConfSSL_Init(void)
 	free(Conf_SSLOptions.DHFile);
 	Conf_SSLOptions.DHFile = NULL;
 	array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
+
+	array_free(&Conf_SSLOptions.ListenPorts);
 }
 
 /**
@@ -370,6 +372,7 @@ Conf_Test( void )
 	printf("  MorePrivacy = %s\n", yesno_to_str(Conf_MorePrivacy));
 	printf("  NoticeAuth = %s\n", yesno_to_str(Conf_NoticeAuth));
 	printf("  OperCanUseMode = %s\n", yesno_to_str(Conf_OperCanMode));
+	printf("  OperChanPAutoOp = %s\n", yesno_to_str(Conf_OperChanPAutoOp));
 	printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
 #ifdef PAM
 	printf("  PAM = %s\n", yesno_to_str(Conf_PAM));
@@ -689,6 +692,7 @@ Set_Defaults(bool InitServers)
 		 PACKAGE_NAME, PACKAGE_VERSION);
 	free(Conf_ListenAddress);
 	Conf_ListenAddress = NULL;
+	array_free(&Conf_ListenPorts);
 	array_free(&Conf_Motd);
 	strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
 	strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
@@ -730,6 +734,7 @@ Set_Defaults(bool InitServers)
 	Conf_MorePrivacy = false;
 	Conf_NoticeAuth = false;
 	Conf_OperCanMode = false;
+	Conf_OperChanPAutoOp = true;
 	Conf_OperServerMode = false;
 #ifdef PAM
 	Conf_PAM = true;
@@ -1178,6 +1183,7 @@ CheckLegacyGlobalOption(int Line, char *Var, char *Arg)
 	    || strcasecmp(Var, "ConnectIPv4") == 0
 	    || strcasecmp(Var, "ConnectIPv6") == 0
 	    || strcasecmp(Var, "OperCanUseMode") == 0
+	    || strcasecmp(Var, "OperChanPAutoOp") == 0
 	    || strcasecmp(Var, "OperServerMode") == 0
 	    || strcasecmp(Var, "PredefChannelsOnly") == 0
 	    || strcasecmp(Var, "SyslogFacility") == 0
@@ -1553,6 +1559,10 @@ Handle_OPTIONS(int Line, char *Var, char *Arg)
 		Conf_OperCanMode = Check_ArgIsTrue(Arg);
 		return;
 	}
+	if (strcasecmp(Var, "OperChanPAutoOp") == 0) {
+		Conf_OperChanPAutoOp = Check_ArgIsTrue(Arg);
+		return;
+	}
 	if (strcasecmp(Var, "OperServerMode") == 0) {
 		Conf_OperServerMode = Check_ArgIsTrue(Arg);
 		return;
diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h
index 7a4e38aa..90d74d20 100644
--- a/src/ngircd/conf.h
+++ b/src/ngircd/conf.h
@@ -151,6 +151,9 @@ GLOBAL bool Conf_PredefChannelsOnly;
 /** Flag indicating if IRC operators are allowed to always use MODE (true) */
 GLOBAL bool Conf_OperCanMode;
 
+/** Flag indicating if IRC operators get AutoOp in persistent (+P) channels */
+GLOBAL bool Conf_OperChanPAutoOp;
+
 /**
  * If true, mask channel MODE commands of IRC operators to the server.
  * Background: ircd2 will ignore channel MODE commands if an IRC operator
diff --git a/src/ngircd/conn-ssl.c b/src/ngircd/conn-ssl.c
index 8f7b70af..59729e04 100644
--- a/src/ngircd/conn-ssl.c
+++ b/src/ngircd/conn-ssl.c
@@ -156,7 +156,7 @@ Load_DH_params(void)
 	bool ret = true;
 
 	if (!Conf_SSLOptions.DHFile) {
-		Log(LOG_NOTICE, "Configuration option \"SSLDHFile\" not set!");
+		Log(LOG_NOTICE, "Configuration option \"DHFile\" not set!");
 		return false;
 	}
 	fp = fopen(Conf_SSLOptions.DHFile, "r");
@@ -201,7 +201,7 @@ Load_DH_params(void)
 	}
 	if (need_dhgenerate) {
 		Log(LOG_WARNING,
-		    "SSLDHFile not set, generating %u bit DH parameters. This may take a while ...",
+		    "DHFile not set, generating %u bit DH parameters. This may take a while ...",
 		    DH_BITS);
 		err = gnutls_dh_params_generate2(tmp_dh_params, DH_BITS);
 		if (err < 0) {
@@ -241,6 +241,9 @@ void ConnSSL_Free(CONNECTION *c)
 bool
 ConnSSL_InitLibrary( void )
 {
+	if (!array_bytes(&Conf_SSLOptions.ListenPorts))
+		return true;
+
 #ifdef HAVE_LIBSSL
 	SSL_CTX *newctx;
 
@@ -256,12 +259,14 @@ ConnSSL_InitLibrary( void )
 		 * According to OpenSSL RAND_egd(3): "The automatic query of /var/run/egd-pool et al was added in OpenSSL 0.9.7";
 		 * so it makes little sense to deal with PRNGD seeding ourselves.
 		 */
+		array_free(&Conf_SSLOptions.ListenPorts);
 		return false;
 	}
 
 	newctx = SSL_CTX_new(SSLv23_method());
 	if (!newctx) {
 		LogOpenSSLError("SSL_CTX_new()", NULL);
+		array_free(&Conf_SSLOptions.ListenPorts);
 		return false;
 	}
 
@@ -276,6 +281,7 @@ ConnSSL_InitLibrary( void )
 	return true;
 out:
 	SSL_CTX_free(newctx);
+	array_free(&Conf_SSLOptions.ListenPorts);
 	return false;
 #endif
 #ifdef HAVE_LIBGNUTLS
@@ -287,10 +293,13 @@ out:
 	err = gnutls_global_init();
 	if (err) {
 		Log(LOG_ERR, "gnutls_global_init(): %s", gnutls_strerror(err));
+		array_free(&Conf_SSLOptions.ListenPorts);
 		return false;
 	}
-	if (!ConnSSL_LoadServerKey_gnutls())
+	if (!ConnSSL_LoadServerKey_gnutls()) {
+		array_free(&Conf_SSLOptions.ListenPorts);
 		return false;
+	}
 	Log(LOG_INFO, "gnutls %s initialized.", gnutls_check_version(NULL));
 	initialized = true;
 	return true;
@@ -313,7 +322,7 @@ ConnSSL_LoadServerKey_gnutls(void)
 
 	cert_file = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile;
 	if (!cert_file) {
-		Log(LOG_NOTICE, "No SSL server key configured, SSL disabled.");
+		Log(LOG_ERR, "No SSL server key configured!");
 		return false;
 	}
 
@@ -344,7 +353,7 @@ ConnSSL_LoadServerKey_openssl(SSL_CTX *ctx)
 
 	assert(ctx);
 	if (!Conf_SSLOptions.KeyFile) {
-		Log(LOG_NOTICE, "No SSL server key configured, SSL disabled.");
+		Log(LOG_ERR, "No SSL server key configured!");
 		return false;
 	}
 
@@ -714,6 +723,13 @@ ConnSSL_GetCipherInfo(CONNECTION *c, char *buf, size_t len)
 #endif
 }
 
+#else
+
+bool
+ConnSSL_InitLibrary(void)
+{
+	return true;
+}
 
 #endif /* SSL_SUPPORT */
 /* -eof- */
diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h
index 82837599..ba7adf17 100644
--- a/src/ngircd/defines.h
+++ b/src/ngircd/defines.h
@@ -164,7 +164,7 @@
 #define USERMODES "aBcCiorRswx"
 
 /** Supported channel modes. */
-#define CHANMODES "beiIklmMnoOPrRstvz"
+#define CHANMODES "abehiIklmMnoOPqrRstvz"
 
 /** Away message for users connected to linked servers. */
 #define DEFAULT_AWAY_MSG "Away"
diff --git a/src/ngircd/irc-channel.c b/src/ngircd/irc-channel.c
index d714b48f..4a157d67 100644
--- a/src/ngircd/irc-channel.c
+++ b/src/ngircd/irc-channel.c
@@ -167,8 +167,10 @@ join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
 		}
 	}
 
-	/* If channel persistent and client is ircop: make client chanop */
-	if (strchr(Channel_Modes(chan), 'P') && strchr(Client_Modes(target), 'o'))
+	/* If the channel is persistent (+P) and client is an IRC op:
+	 * make client chanop, if not disabled in configuration. */
+	if (strchr(Channel_Modes(chan), 'P') && Conf_OperChanPAutoOp
+	    && strchr(Client_Modes(target), 'o'))
 		Channel_UserModeAdd(chan, target, 'o');
 } /* join_set_channelmodes */
 
@@ -510,7 +512,7 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 	CHANNEL *chan;
 	CLIENT *from;
 	char *topic;
-	bool onchannel, topicok, use_servermode, r;
+	bool r, is_oper;
 
 	assert( Client != NULL );
 	assert( Req != NULL );
@@ -533,10 +535,9 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 		return IRC_WriteStrClient(from, ERR_NOSUCHCHANNEL_MSG,
 					  Client_ID(from), Req->argv[0]);
 
-	Channel_CheckAdminRights(chan, Client, from,
-				 &onchannel, &topicok, &use_servermode);
-
-	if (!onchannel && !topicok)
+	/* Only IRC opers and channel members allowed */
+	is_oper = Client_OperByMe(from);
+	if (!Channel_IsMemberOf(chan, from) && !is_oper)
 		return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG,
 					  Client_ID(from), Req->argv[0]);
 
@@ -565,8 +566,12 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 	}
 
 	if (strchr(Channel_Modes(chan), 't')) {
-		/* Topic Lock. Is the user a channel or IRC operator? */
-		if (!topicok)
+		/* Topic Lock. Is the user a channel op or IRC operator? */
+		if(!strchr(Channel_UserModes(chan, from), 'h') &&
+		   !strchr(Channel_UserModes(chan, from), 'o') &&
+		   !strchr(Channel_UserModes(chan, from), 'a') &&
+		   !strchr(Channel_UserModes(chan, from), 'q') &&
+		   !is_oper)
 			return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
 						  Client_ID(from),
 						  Channel_Name(chan));
@@ -578,7 +583,7 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 		 Client_TypeText(from), Client_Mask(from), Channel_Name(chan),
 		 Req->argv[1][0] ? Req->argv[1] : "<none>");
 
-	if (use_servermode)
+	if (Conf_OperServerMode)
 		from = Client_ThisServer();
 
 	/* Update channel and forward new topic to other servers */
@@ -666,7 +671,8 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 			if (MatchCaseInsensitive(pattern, Channel_Name(chan))) {
 				/* Gotcha! */
 				if (!strchr(Channel_Modes(chan), 's')
-				    || Channel_IsMemberOf(chan, from)) {
+				    || Channel_IsMemberOf(chan, from)
+				    || (!Conf_MorePrivacy && Client_OperByMe(Client))) {
 					if (IRC_CheckListTooBig(from, count,
 								 MAX_RPL_LIST,
 								 "LIST"))
diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c
index fc04773a..4909a96a 100644
--- a/src/ngircd/irc-info.c
+++ b/src/ngircd/irc-info.c
@@ -117,7 +117,7 @@ IRC_INFO(CLIENT * Client, REQUEST * Req)
 		target = Client_Search(Req->argv[0]);
 	else
 		target = Client_ThisServer();
-	
+
 	/* Make sure that the target is a server */
 	if (target && Client_Type(target) != CLIENT_SERVER)
 		target = Client_Introducer(target);
@@ -807,22 +807,48 @@ who_flags_status(const char *client_modes)
 }
 
 
-static const char *
-who_flags_qualifier(CLIENT *Client, const char *chan_user_modes)
+/**
+ * Return channel user mode prefix(es).
+ *
+ * @param Client The client requesting the mode prefixes.
+ * @param chan_user_modes String with channel user modes.
+ * @param str String buffer to which the prefix(es) will be appended.
+ * @param len Size of "str" buffer.
+ * @return Pointer to "str".
+ */
+static char *
+who_flags_qualifier(CLIENT *Client, const char *chan_user_modes,
+		    char *str, size_t len)
 {
 	assert(Client != NULL);
 
 	if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) {
-		if (strchr(chan_user_modes, 'o') &&
-		    strchr(chan_user_modes, 'v'))
-			return "@+";
+		if (strchr(chan_user_modes, 'q'))
+			strlcat(str, "~", len);
+		if (strchr(chan_user_modes, 'a'))
+			strlcat(str, "&", len);
+		if (strchr(chan_user_modes, 'o'))
+			strlcat(str, "@", len);
+		if (strchr(chan_user_modes, 'h'))
+			strlcat(str, "%", len);
+		if (strchr(chan_user_modes, 'v'))
+			strlcat(str, "+", len);
+
+		return str;
 	}
 
-	if (strchr(chan_user_modes, 'o'))
-		return "@";
+	if (strchr(chan_user_modes, 'q'))
+		strlcat(str, "~", len);
+	else if (strchr(chan_user_modes, 'a'))
+		strlcat(str, "&", len);
+	else if (strchr(chan_user_modes, 'o'))
+		strlcat(str, "@", len);
+	else if (strchr(chan_user_modes, 'h'))
+		strlcat(str, "%", len);
 	else if (strchr(chan_user_modes, 'v'))
-		return "+";
-	return "";
+		strlcat(str, "+", len);
+
+	return str;
 }
 
 
@@ -840,8 +866,7 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 	bool is_visible, is_member, is_ircop;
 	CL2CHAN *cl2chan;
 	const char *client_modes;
-	const char *chan_user_modes;
-	char flags[8];
+	char flags[10];
 	CLIENT *c;
 	int count = 0;
 
@@ -872,9 +897,8 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 			if (is_ircop)
 				strlcat(flags, "*", sizeof(flags));
 
-			chan_user_modes = Channel_UserModes(Chan, c);
-			strlcat(flags, who_flags_qualifier(c, chan_user_modes),
-				sizeof(flags));
+			who_flags_qualifier(Client, Channel_UserModes(Chan, c),
+					    flags, sizeof(flags));
 
 			if (!write_whoreply(Client, c, Channel_Name(Chan),
 					    flags))
@@ -1090,8 +1114,8 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 		if (str[strlen(str) - 1] != ':')
 			strlcat(str, " ", sizeof(str));
 
-		strlcat(str, who_flags_qualifier(c, Channel_UserModes(chan, c)),
-						 sizeof(str));
+		who_flags_qualifier(Client, Channel_UserModes(chan, c),
+				    str, sizeof(str));
 		strlcat(str, Channel_Name(chan), sizeof(str));
 
 		if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) {
@@ -1465,7 +1489,7 @@ IRC_Send_LUSERS(CLIENT *Client)
 			Conn_CountMax(), Conn_CountAccepted()))
 		return DISCONNECTED;
 #endif
-	
+
 	return CONNECTED;
 } /* IRC_Send_LUSERS */
 
@@ -1595,16 +1619,9 @@ IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
 		if (is_member || is_visible) {
 			if (str[strlen(str) - 1] != ':')
 				strlcat(str, " ", sizeof(str));
-			if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX && 
-					strchr(Channel_UserModes(Chan, cl), 'o') &&
-					strchr(Channel_UserModes(Chan, cl), 'v')) {
-				strlcat(str, "@+", sizeof(str));
-			} else {
-				if (strchr(Channel_UserModes(Chan, cl), 'o'))
-					strlcat(str, "@", sizeof(str));
-				else if (strchr(Channel_UserModes(Chan, cl), 'v'))
-					strlcat(str, "+", sizeof(str));
-			}
+
+			who_flags_qualifier(Client, Channel_UserModes(Chan, cl),
+					    str, sizeof(str));
 			strlcat(str, Client_ID(cl), sizeof(str));
 
 			if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 4)) {
diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c
index 74d8b9d1..99cd26f4 100644
--- a/src/ngircd/irc-login.c
+++ b/src/ngircd/irc-login.c
@@ -18,6 +18,7 @@
 
 #include "imp.h"
 #include <assert.h>
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
@@ -422,11 +423,9 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 		   punctuation is allowed.*/
 		ptr = Req->argv[0];
 		while (*ptr) {
-			if ((*ptr < '0' || *ptr > '9') &&
-			    (*ptr < 'A' || *ptr > 'Z') &&
-			    (*ptr < 'a' || *ptr > 'z') &&
-			    (*ptr != '+') && (*ptr != '-') &&
-			    (*ptr != '.') && (*ptr != '_')) {
+			if (!isalnum(*ptr) &&
+			    *ptr != '+' && *ptr != '-' &&
+			    *ptr != '.' && *ptr != '_') {
 				Conn_Close(Client_Conn(Client), NULL,
 					   "Invalid user name", true);
 				return DISCONNECTED;
diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c
index 71557201..90f638d8 100644
--- a/src/ngircd/irc-mode.c
+++ b/src/ngircd/irc-mode.c
@@ -278,9 +278,15 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 				ok = IRC_WriteStrClient(Origin,
 							ERR_RESTRICTED_MSG,
 							Client_ID(Origin));
-			else
+			else if (!set || Conf_CloakHostModeX[0]
+				 || Client_Type(Client) == CLIENT_SERVER
+				 || Client_OperByMe(Client)) {
 				x[0] = 'x';
 				send_RPL_HOSTHIDDEN_MSG = true;
+			} else
+				ok = IRC_WriteStrClient(Origin,
+							ERR_NOPRIVILEGES_MSG,
+							Client_ID(Origin));
 			break;
 		default:
 			if (Client_Type(Client) != CLIENT_SERVER) {
@@ -418,13 +424,16 @@ static bool
 Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 {
 	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
-	    argadd[CLIENT_PASS_LEN], *mode_ptr;
-	bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
+	    argadd[CLIENT_PASS_LEN], *mode_ptr, *o_mode_ptr;
+	bool connected, set, skiponce, retval, use_servermode,
+	     is_halfop, is_op, is_admin, is_owner, is_machine, is_oper;
 	int mode_arg, arg_arg, mode_arg_count = 0;
 	CLIENT *client;
 	long l;
 	size_t len;
 
+	is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false;
+
 	if (Channel_IsModeless(Channel))
 		return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
 				Client_ID(Client), Channel_Name(Channel));
@@ -433,10 +442,20 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 	if (Req->argc <= 1)
 		return Channel_Mode_Answer_Request(Origin, Channel);
 
-	Channel_CheckAdminRights(Channel, Client, Origin,
-				 &onchannel, &modeok, &use_servermode);
+	/* Check if origin is oper and opers can use mode */
+	use_servermode = Conf_OperServerMode;
+	if(Client_OperByMe(Client) && Conf_OperCanMode) {
+		is_oper = true;
+	}
 
-	if (!onchannel && !modeok)
+	/* Check if client is a server/service */
+	if(Client_Type(Client) == CLIENT_SERVER ||
+	   Client_Type(Client) == CLIENT_SERVICE) {
+		is_machine = true;
+	}
+
+	/* Check if client is member of channel or an oper or an server/service */
+	if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine)
 		return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
 					  Client_ID(Origin),
 					  Channel_Name(Channel));
@@ -515,21 +534,44 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		if (arg_arg >= Req->argc)
 			arg_arg = -1;
 
+		if(!is_machine) {
+			o_mode_ptr = Channel_UserModes(Channel, Client);
+			while( *o_mode_ptr ) {
+				if ( *o_mode_ptr == 'q')
+					is_owner = true;
+				if ( *o_mode_ptr == 'a')
+					is_admin = true;
+				if ( *o_mode_ptr == 'o')
+					is_op = true;
+				if ( *o_mode_ptr == 'h')
+					is_halfop = true;
+				o_mode_ptr++;
+			}
+		}
+
 		/* Validate modes */
 		x[0] = '\0';
 		argadd[0] = '\0';
 		client = NULL;
 		switch (*mode_ptr) {
 		/* --- Channel modes --- */
+		case 'R': /* Registered users only */
+		case 's': /* Secret channel */
+		case 'z': /* Secure connections only */
+			if(!is_oper && !is_machine && !is_owner &&
+			   !is_admin && !is_op) {
+				connected = IRC_WriteStrClient(Origin,
+					ERR_CHANOPRIVSNEEDED_MSG,
+					Client_ID(Origin), Channel_Name(Channel));
+				goto chan_exit;
+			}
 		case 'i': /* Invite only */
 		case 'M': /* Only identified nicks can write */
 		case 'm': /* Moderated */
 		case 'n': /* Only members can write */
-		case 'R': /* Registered users only */
-		case 's': /* Secret channel */
 		case 't': /* Topic locked */
-		case 'z': /* Secure connections only */
-			if (modeok)
+			if(is_oper || is_machine || is_owner ||
+			   is_admin || is_op || is_halfop)
 				x[0] = *mode_ptr;
 			else
 				connected = IRC_WriteStrClient(Origin,
@@ -540,7 +582,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			if (Mode_Limit_Reached(Client, mode_arg_count++))
 				goto chan_exit;
 			if (!set) {
-				if (modeok)
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop)
 					x[0] = *mode_ptr;
 				else
 					connected = IRC_WriteStrClient(Origin,
@@ -550,7 +593,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				break;
 			}
 			if (arg_arg > mode_arg) {
-				if (modeok) {
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop) {
 					Channel_ModeDel(Channel, 'k');
 					Channel_SetKey(Channel,
 						       Req->argv[arg_arg]);
@@ -576,7 +620,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			if (Mode_Limit_Reached(Client, mode_arg_count++))
 				goto chan_exit;
 			if (!set) {
-				if (modeok)
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop)
 					x[0] = *mode_ptr;
 				else
 					connected = IRC_WriteStrClient(Origin,
@@ -586,7 +631,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				break;
 			}
 			if (arg_arg > mode_arg) {
-				if (modeok) {
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop) {
 					l = atol(Req->argv[arg_arg]);
 					if (l > 0 && l < 0xFFFF) {
 						Channel_ModeDel(Channel, 'l');
@@ -611,44 +657,47 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			}
 			break;
 		case 'O': /* IRC operators only */
-			if (modeok) {
+			if (set) {
 				/* Only IRC operators are allowed to
 				 * set the 'O' channel mode! */
-				if (set && !(Client_OperByMe(Client)
-				    || Client_Type(Client) == CLIENT_SERVER))
+				if(is_oper || is_machine)
+					x[0] = 'O';
+				else
 					connected = IRC_WriteStrClient(Origin,
 						ERR_NOPRIVILEGES_MSG,
 						Client_ID(Origin));
-				else
-					x[0] = 'O';
-			} else
+			} else if(is_oper || is_machine || is_owner ||
+				  is_admin || is_op)
+				x[0] = 'O';
+			else
 				connected = IRC_WriteStrClient(Origin,
-						ERR_CHANOPRIVSNEEDED_MSG,
-						Client_ID(Origin),
-						Channel_Name(Channel));
-				break;
+					ERR_CHANOPRIVSNEEDED_MSG,
+					Client_ID(Origin),
+					Channel_Name(Channel));
+			break;
 		case 'P': /* Persistent channel */
-			if (modeok) {
+			if (set) {
 				/* Only IRC operators are allowed to
 				 * set the 'P' channel mode! */
-				if (set && !(Client_OperByMe(Client)
-				    || Client_Type(Client) == CLIENT_SERVER))
+				if(is_oper || is_machine)
+					x[0] = 'P';
+				else
 					connected = IRC_WriteStrClient(Origin,
 						ERR_NOPRIVILEGES_MSG,
 						Client_ID(Origin));
-				else
-					x[0] = 'P';
-			} else
+			} else if(is_oper || is_machine || is_owner ||
+				  is_admin || is_op)
+				x[0] = 'P';
+			else
 				connected = IRC_WriteStrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
 			break;
 		/* --- Channel user modes --- */
-		case 'a':
-		case 'h':
-		case 'q':
-			if (Client_Type(Client) != CLIENT_SERVER) {
+		case 'q': /* Owner */
+		case 'a': /* Channel admin */
+			if(!is_oper && !is_machine && !is_owner) {
 				connected = IRC_WriteStrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
@@ -656,16 +705,34 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				goto chan_exit;
 			}
 		case 'o': /* Channel operator */
+			if(!is_oper && !is_machine && !is_owner &&
+			   !is_admin && !is_op) {
+				connected = IRC_WriteStrClient(Origin,
+					ERR_CHANOPRIVSNEEDED_MSG,
+					Client_ID(Origin),
+					Channel_Name(Channel));
+				goto chan_exit;
+			}
+		case 'h': /* Half Op */
+			if(!is_oper && !is_machine && !is_owner &&
+			   !is_admin && !is_op) {
+				connected = IRC_WriteStrClient(Origin,
+					ERR_CHANOPRIVSNEEDED_MSG,
+					Client_ID(Origin),
+					Channel_Name(Channel));
+				goto chan_exit;
+			}
 		case 'v': /* Voice */
 			if (arg_arg > mode_arg) {
-				if (modeok) {
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop) {
 					client = Client_Search(Req->argv[arg_arg]);
 					if (client)
 						x[0] = *mode_ptr;
 					else
-						connected = IRC_WriteStrClient(Client,
+						connected = IRC_WriteStrClient(Origin,
 							ERR_NOSUCHNICK_MSG,
-							Client_ID(Client),
+							Client_ID(Origin),
 							Req->argv[arg_arg]);
 				} else {
 					connected = IRC_WriteStrClient(Origin,
@@ -690,7 +757,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				goto chan_exit;
 			if (arg_arg > mode_arg) {
 				/* modify list */
-				if (modeok) {
+				if (is_oper || is_machine || is_owner ||
+				    is_admin || is_op || is_halfop) {
 					connected = set
 					   ? Add_To_List(*mode_ptr, Origin,
 						Client, Channel,
diff --git a/src/ngircd/irc-op.c b/src/ngircd/irc-op.c
index 5e36b02b..08495475 100644
--- a/src/ngircd/irc-op.c
+++ b/src/ngircd/irc-op.c
@@ -166,8 +166,11 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req)
 
 		/* Is the channel "invite-only"? */
 		if (strchr(Channel_Modes(chan), 'i')) {
-			/* Yes. The user must be channel operator! */
-			if (!strchr(Channel_UserModes(chan, from), 'o'))
+			/* Yes. The user must be channel owner/admin/operator/halfop! */
+			if (!strchr(Channel_UserModes(chan, from), 'q') &&
+			    !strchr(Channel_UserModes(chan, from), 'a') &&
+			    !strchr(Channel_UserModes(chan, from), 'o') &&
+			    !strchr(Channel_UserModes(chan, from), 'h'))
 				return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(from), Channel_Name(chan));
 			remember = true;
diff --git a/src/ngircd/irc-oper.c b/src/ngircd/irc-oper.c
index 21577f00..237107f6 100644
--- a/src/ngircd/irc-oper.c
+++ b/src/ngircd/irc-oper.c
@@ -183,6 +183,8 @@ IRC_REHASH( CLIENT *Client, REQUEST *Req )
 
 	Log(LOG_NOTICE|LOG_snotice, "Got REHASH command from \"%s\" ...",
 	    Client_Mask(Client));
+	IRC_WriteStrClient(Client, RPL_REHASHING_MSG, Client_ID(Client));
+
 	raise(SIGHUP);
 
 	return CONNECTED;
diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c
index 8526a573..d605745c 100644
--- a/src/ngircd/irc-server.c
+++ b/src/ngircd/irc-server.c
@@ -54,7 +54,7 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 	CLIENT *from, *c;
 	int i;
 	CONN_ID con;
-	
+
 	assert( Client != NULL );
 	assert( Req != NULL );
 
@@ -88,7 +88,7 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 			Conn_Close( Client_Conn( Client ), NULL, "Bad password", true);
 			return DISCONNECTED;
 		}
-		
+
 		/* Is there a registered server with this ID? */
 		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;
 
@@ -203,10 +203,10 @@ GLOBAL bool
 IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 {
 	char nick_in[COMMAND_LEN], nick_out[COMMAND_LEN], *channame, *ptr, modes[8];
-	bool is_op, is_voiced;
+	bool is_owner, is_chanadmin, is_op, is_halfop, is_voiced;
 	CHANNEL *chan;
 	CLIENT *c;
-	
+
 	assert( Client != NULL );
 	assert( Req != NULL );
 
@@ -219,12 +219,16 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 	ptr = strtok( nick_in, "," );
 	while( ptr )
 	{
-		is_op = is_voiced = false;
-		
+		is_owner = is_chanadmin = is_op = is_halfop = is_voiced = false;
+
 		/* cut off prefixes */
-		while(( *ptr == '@' ) || ( *ptr == '+' ))
+		while(( *ptr == '~') || ( *ptr == '&' ) || ( *ptr == '@' ) ||
+			( *ptr == '%') || ( *ptr == '+' ))
 		{
+			if( *ptr == '~' ) is_owner = true;
+			if( *ptr == '&' ) is_chanadmin = true;
 			if( *ptr == '@' ) is_op = true;
+			if( *ptr == 'h' ) is_halfop = true;
 			if( *ptr == '+' ) is_voiced = true;
 			ptr++;
 		}
@@ -235,8 +239,11 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 			Channel_Join( c, channame );
 			chan = Channel_Search( channame );
 			assert( chan != NULL );
-			
+
+			if( is_owner ) Channel_UserModeAdd( chan, c, 'q' );
+			if( is_chanadmin ) Channel_UserModeAdd( chan, c, 'a' );
 			if( is_op ) Channel_UserModeAdd( chan, c, 'o' );
+			if( is_halfop ) Channel_UserModeAdd( chan, c, 'h' );
 			if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' );
 
 			/* announce to channel... */
@@ -251,12 +258,15 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 			}
 
 			if( nick_out[0] != '\0' ) strlcat( nick_out, ",", sizeof( nick_out ));
+			if( is_owner ) strlcat( nick_out, "~", sizeof( nick_out ));
+			if( is_chanadmin ) strlcat( nick_out, "&", sizeof( nick_out ));
 			if( is_op ) strlcat( nick_out, "@", sizeof( nick_out ));
+			if( is_halfop ) strlcat( nick_out, "%", sizeof( nick_out ));
 			if( is_voiced ) strlcat( nick_out, "+", sizeof( nick_out ));
 			strlcat( nick_out, ptr, sizeof( nick_out ));
 		}
 		else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame );
-		
+
 		/* search for next Nick */
 		ptr = strtok( NULL, "," );
 	}
diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h
index 99d25828..35d621a6 100644
--- a/src/ngircd/messages.h
+++ b/src/ngircd/messages.h
@@ -21,7 +21,7 @@
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(qaohv)~&@%%+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
 #define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d MODES=%d MAXLIST=beI:%d EXCEPTS=e INVEX=I PENALTY :are supported on this server"
 
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
@@ -93,6 +93,7 @@
 #define RPL_ENDOFMOTD_MSG		"376 %s :End of MOTD command"
 #define RPL_WHOISHOST_MSG		"378 %s %s :is connecting from *@%s %s"
 #define RPL_YOUREOPER_MSG		"381 %s :You are now an IRC Operator"
+#define RPL_REHASHING_MSG		"382 %s :Rehashing"
 #define RPL_YOURESERVICE_MSG		"383 %s :You are service %s"
 #define RPL_TIME_MSG			"391 %s %s :%s"
 #define RPL_HOSTHIDDEN_MSG		"396 %s %s :is your displayed hostname now"
@@ -125,7 +126,7 @@
 #define ERR_NEEDMOREPARAMS_MSG		"461 %s %s :Syntax error"
 #define ERR_ALREADYREGISTRED_MSG	"462 %s :Connection already registered"
 #define ERR_PASSWDMISMATCH_MSG		"464 %s :Invalid password"
-#define ERR_CHANNELISFULL_MSG		"471 %s %s :Cannot join channel (+l) -- Channel is too full, try later"
+#define ERR_CHANNELISFULL_MSG		"471 %s %s :Cannot join channel (+l) -- Channel is full, try later"
 #define ERR_SECURECHANNEL_MSG		"471 %s %s :Cannot join channel (+z) -- SSL connections only"
 #define ERR_OPONLYCHANNEL_MSG		"471 %s %s :Cannot join channel (+O) -- IRC opers only"
 #define ERR_REGONLYCHANNEL_MSG		"471 %s %s :Cannot join channel (+R) -- Registered users only"
@@ -137,6 +138,7 @@
 #define ERR_LISTFULL_MSG		"478 %s %s %s: Channel list is full (%d)"
 #define ERR_NOPRIVILEGES_MSG		"481 %s :Permission denied"
 #define ERR_CHANOPRIVSNEEDED_MSG	"482 %s %s :You are not channel operator"
+#define ERR_CHANOPPRIVTOOLOW_MSG	"482 %s %s :Your privileges are too low"
 #define ERR_CANTKILLSERVER_MSG		"483 %s :You can't kill a server!"
 #define ERR_RESTRICTED_MSG		"484 %s :Your connection is restricted"
 #define ERR_NICKREGISTER_MSG		"484 %s :Cannot modify user mode (+R) -- Use IRC services"
diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c
index bbfc6ce1..50d91ce8 100644
--- a/src/ngircd/ngircd.c
+++ b/src/ngircd/ngircd.c
@@ -669,7 +669,7 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 	/* SSL initialization */
 	if (!ConnSSL_InitLibrary())
 		Log(LOG_WARNING,
-		    "Warning: Error during SSL initialization, continuing ...");
+		    "Error during SSL initialization, continuing without SSL ...");
 
 	/* Change root */
 	if (Conf_Chroot[0]) {
diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c
index d59a1dc3..4bce60fb 100644
--- a/src/ngircd/numeric.c
+++ b/src/ngircd/numeric.c
@@ -67,10 +67,17 @@ Announce_Channel(CLIENT *Client, CHANNEL *Chan)
 			 * (if user is channel operator or has voice) */
 			if (str[strlen(str) - 1] != ':')
 				strlcat(str, ",", sizeof(str));
-			if (strchr(Channel_UserModes(Chan, cl), 'v'))
-				strlcat(str, "+", sizeof(str));
+			if (strchr(Channel_UserModes(Chan, cl), 'q'))
+				strlcat(str, "~", sizeof(str));
+			if (strchr(Channel_UserModes(Chan, cl), 'a'))
+				strlcat(str, "&", sizeof(str));
 			if (strchr(Channel_UserModes(Chan, cl), 'o'))
 				strlcat(str, "@", sizeof(str));
+			if (strchr(Channel_UserModes(Chan, cl), 'h'))
+				strlcat(str, "%", sizeof(str));
+			if (strchr(Channel_UserModes(Chan, cl), 'v'))
+				strlcat(str, "+", sizeof(str));
+
 			strlcat(str, Client_ID(cl), sizeof(str));
 
 			/* Send the data if the buffer is "full" */
diff --git a/src/portab/.gitignore b/src/portab/.gitignore
index 839a69fd..9bac6ac1 100644
--- a/src/portab/.gitignore
+++ b/src/portab/.gitignore
@@ -1 +1,2 @@
+Makefile.am
 portabtest
diff --git a/src/portab/Makefile.am b/src/portab/Makefile.am
deleted file mode 100644
index a57ea495..00000000
--- a/src/portab/Makefile.am
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
-#
-# Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
-# der GNU General Public License (GPL), wie von der Free Software Foundation
-# herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
-# der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
-# Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
-# der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
-#
-
-AUTOMAKE_OPTIONS = ansi2knr
-
-noinst_LIBRARIES = libngportab.a
-
-libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
-
-check_PROGRAMS = portabtest
-
-portabtest_SOURCES = portabtest.c
-
-portabtest_LDFLAGS = -L.
-
-portabtest_LDADD = -lngportab
-
-noinst_HEADERS = imp.h exp.h portab.h splint.h
-
-maintainer-clean-local:
-	rm -f Makefile Makefile.in
-
-TESTS = portabtest
-
-# -eof-
diff --git a/src/portab/Makefile.ng b/src/portab/Makefile.ng
new file mode 100644
index 00000000..dac329fa
--- /dev/null
+++ b/src/portab/Makefile.ng
@@ -0,0 +1,35 @@
+#
+# ngIRCd -- The Next Generation IRC Daemon
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+#
+# 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.
+#
+
+__ng_Makefile_am_template__
+
+EXTRA_DIST = Makefile.ng
+
+noinst_LIBRARIES = libngportab.a
+
+libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
+
+check_PROGRAMS = portabtest
+
+portabtest_SOURCES = portabtest.c
+
+portabtest_LDFLAGS = -L.
+
+portabtest_LDADD = -lngportab
+
+noinst_HEADERS = imp.h exp.h portab.h splint.h
+
+maintainer-clean-local:
+	rm -f Makefile Makefile.in Makefile.am
+
+TESTS = portabtest
+
+# -eof-
diff --git a/src/testsuite/.gitignore b/src/testsuite/.gitignore
index 5884a486..b33a08f4 100644
--- a/src/testsuite/.gitignore
+++ b/src/testsuite/.gitignore
@@ -1,3 +1,4 @@
+Makefile.am
 T-ngircd1
 T-ngircd2
 channel-test
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.ng
index 9dc76a7d..b906091e 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.ng
@@ -1,21 +1,20 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
 #
-# Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
-# der GNU General Public License (GPL), wie von der Free Software Foundation
-# herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
-# der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
-# Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
-# der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
+# 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.
 #
 
-AUTOMAKE_OPTIONS = ../portab/ansi2knr
+__ng_Makefile_am_template__
 
-INCLUDES = -I$(srcdir)/../portab
+AM_CPPFLAGS = -I$(srcdir)/../portab
 
 EXTRA_DIST = \
-	README functions.inc getpid.sh \
+	Makefile.ng README functions.inc getpid.sh \
 	start-server.sh stop-server.sh tests.sh stress-server.sh \
 	test-loop.sh wait-tests.sh \
 	channel-test.e connect-test.e check-idle.e invite-test.e \
@@ -32,7 +31,7 @@ clean-local:
 	 T-ngircd1 ngircd-test1.motd T-ngircd2 ngircd-test2.motd
 
 maintainer-clean-local:
-	rm -f Makefile Makefile.in
+	rm -f Makefile Makefile.in Makefile.am
 
 check_SCRIPTS = ngircd-TEST-Binary tests.sh
 
diff --git a/src/testsuite/getpid.sh b/src/testsuite/getpid.sh
index 19ced762..96ab4746 100755
--- a/src/testsuite/getpid.sh
+++ b/src/testsuite/getpid.sh
@@ -10,7 +10,7 @@ UNAME=`uname`
 if [ $UNAME = "FreeBSD" ]; then
   PS_FLAGS="-a"; PS_PIDCOL="1"; HEAD_FLAGS="-n 1"
 elif [ $UNAME = "A/UX" ]; then
-  PS_FLAGS="-ae"; PS_PIDCOL="1"; HEAD_FLAGS="-1"
+  PS_FLAGS="-af"; PS_PIDCOL="2"; HEAD_FLAGS="-1"
 elif [ $UNAME = "GNU" ]; then
   PS_FLAGS="-ax"; PS_PIDCOL="2"; HEAD_FLAGS="-n 1"
 elif [ $UNAME = "Linux" ]; then
diff --git a/src/testsuite/mode-test.e b/src/testsuite/mode-test.e
index 260cd03c..44b6e5df 100644
--- a/src/testsuite/mode-test.e
+++ b/src/testsuite/mode-test.e
@@ -31,6 +31,46 @@ expect {
 	"@* MODE nick :-i"
 }
 
+send "join #usermode\r"
+expect {
+	timeout { exit 1 }
+	"@* JOIN :#usermode"
+}
+expect {
+	timeout { exit 1 }
+	"366"
+}
+
+send "mode #usermode +v nick\r"
+expect {
+	timeout { exit 1 }
+	"@* MODE #usermode +v nick\r"
+}
+
+send "mode #usermode +h nick\r"
+expect {
+	timeout { exit 1 }
+	"@* MODE #usermode +h nick\r"
+}
+
+send "mode #usermode +a nick\r"
+expect {
+	timeout { exit 1 }
+	"482 nick"
+}
+
+send "mode #usermode +q nick\r"
+expect {
+	timeout { exit 1 }
+	"482 nick"
+}
+
+send "mode #usermode -vho nick nick nick\r"
+expect {
+	timeout { exit 1 }
+	"@* MODE #usermode -vho nick nick nick"
+}
+
 send "oper TestOp 123\r"
 expect {
 	timeout { exit 1 }
@@ -47,6 +87,34 @@ expect {
 	"221 nick +o"
 }
 
+send "mode #usermode +a nick\r"
+expect {
+	timeout { exit 1 }
+	"@* MODE #usermode +a nick"
+}
+
+send "mode #usermode +q nick\r"
+expect {
+	timeout { exit 1 }
+	"@* MODE #usermode +q nick"
+}
+
+send "names #usermode\r"
+expect {
+	timeout { exit 1 }
+	"353 nick = #usermode :~nick"
+}
+expect {
+	timeout { exit 1 }
+	"366 nick #usermode"
+}
+
+send "part #usermode\r"
+expect {
+	timeout { exit 1 }
+	"@* PART #usermode"
+}
+
 send "join #channel\r"
 expect {
 	timeout { exit 1 }
diff --git a/src/testsuite/stress-server.sh b/src/testsuite/stress-server.sh
index 0f85958e..15d6df1f 100755
--- a/src/testsuite/stress-server.sh
+++ b/src/testsuite/stress-server.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # ngIRCd Test Suite
-# Copyright (c)2002-2004 by Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
 #
 # 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
@@ -9,8 +9,6 @@
 # (at your option) any later version.
 # Please read the file COPYING, README and AUTHORS for more information.
 #
-# $Id: stress-server.sh,v 1.16 2005/12/30 22:13:21 alex Exp $
-#
 
 # detect source directory
 [ -z "$srcdir" ] && srcdir=`dirname $0`
@@ -23,7 +21,8 @@
 name=`basename $0`
 
 # create directories
-mkdir -p logs tests
+[ -d logs ] || mkdir logs
+[ -d tests ] || mkdir tests
 
 # test for required external tools
 type expect > /dev/null 2>&1
diff --git a/src/testsuite/tests.sh b/src/testsuite/tests.sh
index 2bd9ae40..dc184ffc 100755
--- a/src/testsuite/tests.sh
+++ b/src/testsuite/tests.sh
@@ -1,13 +1,21 @@
 #!/bin/sh
+#
 # ngIRCd Test Suite
-# $Id: tests.sh,v 1.7 2004/09/06 22:04:06 alex Exp $
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+#
+# 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.
+#
 
 # detect source directory
 [ -z "$srcdir" ] && srcdir=`dirname $0`
 
 name=`basename $0`
 test=`echo ${name} | cut -d '.' -f 1`
-mkdir -p logs
+[ -d logs ] || mkdir logs
 
 if [ ! -r "$test" ]; then
   echo "      ${name}: test \"$test\" not found!";  exit 77
diff --git a/src/tool/.gitignore b/src/tool/.gitignore
new file mode 100644
index 00000000..08a6d725
--- /dev/null
+++ b/src/tool/.gitignore
@@ -0,0 +1 @@
+Makefile.am
diff --git a/src/tool/Makefile.am b/src/tool/Makefile.ng
index 8d6cda46..d88bdc94 100644
--- a/src/tool/Makefile.am
+++ b/src/tool/Makefile.ng
@@ -9,9 +9,11 @@
 # Please read the file COPYING, README and AUTHORS for more information.
 #
 
-AUTOMAKE_OPTIONS = ../portab/ansi2knr
+__ng_Makefile_am_template__
 
-INCLUDES = -I$(srcdir)/../portab
+EXTRA_DIST = Makefile.ng
+
+AM_CPPFLAGS = -I$(srcdir)/../portab
 
 noinst_LIBRARIES = libngtool.a
 
@@ -20,6 +22,6 @@ libngtool_a_SOURCES = tool.c
 noinst_HEADERS = tool.h
 
 maintainer-clean-local:
-	rm -f Makefile Makefile.in
+	rm -f Makefile Makefile.in Makefile.am
 
 # -eof-