summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ngircd/irc-info.c245
-rw-r--r--src/ngircd/irc-login.c23
-rw-r--r--src/ngircd/irc-login.h1
-rw-r--r--src/ngircd/parse.c4
-rw-r--r--src/testsuite/.gitignore1
-rw-r--r--src/testsuite/Makefile.am9
-rw-r--r--src/testsuite/whois-test.e53
7 files changed, 252 insertions, 84 deletions
diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c
index 46e34271..22c65aa2 100644
--- a/src/ngircd/irc-info.c
+++ b/src/ngircd/irc-info.c
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2011 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
@@ -917,56 +917,20 @@ IRC_WHO( CLIENT *Client, REQUEST *Req )
 
 
 /**
- * Handler for the IRC "WHOIS" command.
- *
- * See RFC 2812, 3.6.2 "Whois query".
+ * Generate WHOIS reply of one actual client.
  *
  * @param Client	The client from which this command has been received.
- * @param Req		Request structure with prefix and all parameters.
- * @return		CONNECTED or DISCONNECTED.
+ * @param from		The client requesting the information ("originator").
+ * @param c		The client of which information should be returned.
+ * @returns		CONNECTED or DISCONNECTED.
  */
-GLOBAL bool
-IRC_WHOIS( CLIENT *Client, REQUEST *Req )
+static bool
+IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 {
-	CLIENT *from, *target, *c;
 	char str[LINE_LEN + 1];
 	CL2CHAN *cl2chan;
 	CHANNEL *chan;
 
-	assert( Client != NULL );
-	assert( Req != NULL );
-
-	/* Bad number of parameters? */
-	if (Req->argc < 1 || Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
-	/* Search client */
-	c = Client_Search(Req->argv[Req->argc - 1]);
-	if (!c || (Client_Type(c) != CLIENT_USER
-		   && Client_Type(c) != CLIENT_SERVICE))
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
-					  Client_ID(Client),
-					  Req->argv[Req->argc - 1]);
-
-	/* Search sender of the WHOIS */
-	if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
-	else from = Client;
-	if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
-
-	/* Forward to other server? */
-	if( Req->argc > 1 )
-	{
-		/* Search target server (can be specified as nick of that server!) */
-		target = Client_Search( Req->argv[0] );
-		if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
-	}
-	else target = Client_ThisServer( );
-
-	assert( target != NULL );
-
-	if(( Client_NextHop( target ) != Client_ThisServer( )) && ( Client_Type( Client_NextHop( target )) == CLIENT_SERVER )) return IRC_WriteStrClientPrefix( target, from, "WHOIS %s :%s", Req->argv[0], Req->argv[1] );
-
 	/* Nick, user, hostname and client info */
 	if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
 				Client_ID(c), Client_User(c),
@@ -974,18 +938,21 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 		return DISCONNECTED;
 
 	/* Server */
-	if( ! IRC_WriteStrClient( from, RPL_WHOISSERVER_MSG, Client_ID( from ), Client_ID( c ), Client_ID( Client_Introducer( c )), Client_Info( Client_Introducer( c )))) return DISCONNECTED;
+	if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from),
+				Client_ID(c), Client_ID(Client_Introducer(c)),
+				Client_Info(Client_Introducer(c))))
+		return DISCONNECTED;
 
 	/* Channels */
-	snprintf( str, sizeof( str ), RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c ));
-	cl2chan = Channel_FirstChannelOf( c );
-	while( cl2chan )
-	{
-		chan = Channel_GetChannel( cl2chan );
-		assert( chan != NULL );
+	snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
+		 Client_ID(from), Client_ID(c));
+	cl2chan = Channel_FirstChannelOf(c);
+	while (cl2chan) {
+		chan = Channel_GetChannel(cl2chan);
+		assert(chan != NULL);
 
 		/* next */
-		cl2chan = Channel_NextChannelOf( c, cl2chan );
+		cl2chan = Channel_NextChannelOf(c, cl2chan);
 
 		/* Secret channel? */
 		if (strchr(Channel_Modes(chan), 's')
@@ -998,54 +965,168 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 			continue;
 
 		/* Concatenate channel names */
-		if( str[strlen( str ) - 1] != ':' ) strlcat( str, " ", sizeof( str ));
-		if( strchr( Channel_UserModes( chan, c ), 'o' )) strlcat( str, "@", sizeof( str ));
-		else if( strchr( Channel_UserModes( chan, c ), 'v' )) strlcat( str, "+", sizeof( str ));
-		strlcat( str, Channel_Name( chan ), sizeof( str ));
+		if (str[strlen(str) - 1] != ':')
+			strlcat(str, " ", sizeof(str));
 
-		if( strlen( str ) > ( LINE_LEN - CHANNEL_NAME_LEN - 4 ))
-		{
+		strlcat(str, who_flags_qualifier(Channel_UserModes(chan, c)),
+						 sizeof(str));
+		strlcat(str, Channel_Name(chan), sizeof(str));
+
+		if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) {
 			/* Line becomes too long: send it! */
-			if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
-			snprintf( str, sizeof( str ), RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c ));
+			if (!IRC_WriteStrClient(Client, "%s", str))
+				return DISCONNECTED;
+			snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
+				 Client_ID(from), Client_ID(c));
 		}
 	}
-	if( str[strlen( str ) - 1] != ':')
-	{
+	if(str[strlen(str) - 1] != ':') {
 		/* There is data left to send: */
-		if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
+		if (!IRC_WriteStrClient(Client, "%s", str))
+			return DISCONNECTED;
 	}
 
 	/* IRC-Operator? */
-	if( Client_HasMode( c, 'o' ))
-	{
-		if( ! IRC_WriteStrClient( from, RPL_WHOISOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
-	}
+	if (Client_HasMode(c, 'o') &&
+		!IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
+				    Client_ID(from), Client_ID(c)))
+			return DISCONNECTED;
 
 	/* Connected using SSL? */
-	if (Conn_UsesSSL(Client_Conn(c))) {
-		if (!IRC_WriteStrClient
-		    (from, RPL_WHOISSSL_MSG, Client_ID(from), Client_ID(c)))
+	if (Conn_UsesSSL(Client_Conn(c)) &&
+		!IRC_WriteStrClient(from, RPL_WHOISSSL_MSG,
+				    Client_ID(from), Client_ID(c)))
 			return DISCONNECTED;
-	}
 
 	/* Idle and signon time (local clients only!) */
-	if (Client_Conn(c) > NONE ) {
-		if (! IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
-			Client_ID(from), Client_ID(c),
-			(unsigned long)Conn_GetIdle(Client_Conn(c)),
-			(unsigned long)Conn_GetSignon(Client_Conn(c))))
-				return DISCONNECTED;
-	}
+	if (Client_Conn(c) > NONE &&
+		!IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
+				    Client_ID(from), Client_ID(c),
+				    (unsigned long)Conn_GetIdle(Client_Conn(c)),
+				    (unsigned long)Conn_GetSignon(Client_Conn(c))))
+			return DISCONNECTED;
 
 	/* Away? */
-	if( Client_HasMode( c, 'a' ))
-	{
-		if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( c ), Client_Away( c ))) return DISCONNECTED;
+	if (Client_HasMode(c, 'a') &&
+		!IRC_WriteStrClient(from, RPL_AWAY_MSG,
+				    Client_ID(from), Client_ID(c),
+				    Client_Away(c)))
+			return DISCONNECTED;
+
+	return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
+				  Client_ID(from), Client_ID(c));
+} /* IRC_WHOIS_SendReply */
+
+
+/**
+ * Handler for the IRC "WHOIS" command.
+ *
+ * See RFC 2812, 3.6.2 "Whois query".
+ *
+ * @param Client	The client from which this command has been received.
+ * @param Req		Request structure with prefix and all parameters.
+ * @return		CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_WHOIS( CLIENT *Client, REQUEST *Req )
+{
+	CLIENT *from, *target, *c;
+	unsigned int match_count = 0, found = 0;
+	bool has_wildcards, is_remote;
+	bool got_wildcard = false;
+	const char *query;
+
+	assert( Client != NULL );
+	assert( Req != NULL );
+
+	/* Bad number of parameters? */
+	if (Req->argc < 1 || Req->argc > 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+
+	/* Search sender of the WHOIS */
+	if (Client_Type(Client) == CLIENT_SERVER) {
+		from = Client_Search(Req->prefix);
+	} else {
+		IRC_SetPenalty(Client, 1);
+		from = Client;
 	}
+	if (!from)
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->prefix);
+
+	/* Get target server for this command */
+	if (Req->argc > 1) {
+		/* Search the target server, which can be specified as a
+		 * nick name on that server as well: */
+		target = Client_Search(Req->argv[0]);
+		if (!target)
+			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+						Client_ID(from), Req->argv[0]);
+	} else
+		target = Client_ThisServer();
+	assert(target != NULL);
+
+	/* Forward to other server? */
+	if (Client_NextHop(target) != Client_ThisServer() &&
+	    Client_Type(Client_NextHop(target)) == CLIENT_SERVER)
+		return IRC_WriteStrClientPrefix(target, from,
+						"WHOIS %s :%s",
+						Req->argv[0], Req->argv[1]);
+
+	is_remote = Client_Conn(from) < 0;
+	for (query = strtok(Req->argv[Req->argc - 1], ",");
+			query && found < 3;
+			query = strtok(NULL, ","), found++)
+	{
+		has_wildcards = query[strcspn(query, "*?")] != 0;
+		/*
+		 * follows ircd 2.10 implementation:
+		 *  - handle up to 3 targets
+		 *  - no wildcards for remote clients
+		 *  - only one wildcard target per local client
+		 *
+		 *  also, at most ten matches are returned.
+		 */
+		if (!has_wildcards || is_remote) {
+			c = Client_Search(query);
+			if (c) {
+				if (!IRC_WHOIS_SendReply(Client, from, c))
+					return DISCONNECTED;
+			} else {
+				if (!IRC_WriteStrClient(Client,
+							ERR_NOSUCHNICK_MSG,
+							Client_ID(Client),
+							query))
+					return DISCONNECTED;
+			}
+			continue;
+		}
+		if (got_wildcard) {
+			/* we already handled one wildcard query */
+			if (!IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+			     Client_ID(Client), query))
+				return DISCONNECTED;
+			continue;
+		}
+		got_wildcard = true;
+		IRC_SetPenalty(Client, 3);
 
-	/* End of Whois */
-	return IRC_WriteStrClient( from, RPL_ENDOFWHOIS_MSG, Client_ID( from ), Client_ID( c ));
+		for (c = Client_First(); c && match_count < 10; c = Client_Next(c)) {
+			if (Client_Type(c) != CLIENT_USER)
+				continue;
+			if (!MatchCaseInsensitive(query, Client_ID(c)))
+				continue;
+			if (!IRC_WHOIS_SendReply(Client, from, c))
+				return DISCONNECTED;
+			match_count++;
+		}
+
+		if (match_count == 0)
+			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+				Client_ID(Client), Req->argv[Req->argc - 1]);
+	}
+	return CONNECTED;
 } /* IRC_WHOIS */
 
 
diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c
index f76a6270..92d54ab1 100644
--- a/src/ngircd/irc-login.c
+++ b/src/ngircd/irc-login.c
@@ -683,6 +683,29 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
 } /* IRC_QUIT */
 
 
+#ifndef STRICT_RFC
+
+/**
+ * Handler for HTTP command, e.g. GET and POST
+ *
+ * We handle these commands here to avoid the quite long timeout when
+ * some user tries to access this IRC daemon using an web browser ...
+ *
+ * @param Client	The client from which this command has been received.
+ * @param Req		Request structure with prefix and all parameters.
+ * @returns		CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
+{
+	Req->argc = 1;
+	Req->argv[0] = "Oops, HTTP request received? This is IRC!";
+	return IRC_QUIT(Client, Req);
+} /* IRC_QUIT_HTTP */
+
+#endif
+
+
 /**
  * Handler for the IRC "PING" command.
  *
diff --git a/src/ngircd/irc-login.h b/src/ngircd/irc-login.h
index 7ba53571..f3138f6e 100644
--- a/src/ngircd/irc-login.h
+++ b/src/ngircd/irc-login.h
@@ -25,6 +25,7 @@ GLOBAL bool IRC_WEBIRC PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_PING PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_PONG PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_QUIT PARAMS((CLIENT *Client, REQUEST *Req));
+GLOBAL bool IRC_QUIT_HTTP PARAMS((CLIENT *Client, REQUEST *Req));
 
 #endif
 
diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c
index 7c56a03d..8203dd0e 100644
--- a/src/ngircd/parse.c
+++ b/src/ngircd/parse.c
@@ -109,6 +109,10 @@ static COMMAND My_Commands[] =
 #ifdef IRCPLUS
 	{ "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, 0, 0 },
 #endif
+#ifndef STRICT_RFC
+	{ "GET",  IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
+	{ "POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
+#endif
 	{ NULL, NULL, 0x0, 0, 0, 0 } /* Ende-Marke */
 };
 
diff --git a/src/testsuite/.gitignore b/src/testsuite/.gitignore
index fd628e3d..5884a486 100644
--- a/src/testsuite/.gitignore
+++ b/src/testsuite/.gitignore
@@ -11,6 +11,7 @@ mode-test
 opless-channel-test
 server-link-test
 who-test
+whois-test
 ngircd-test1.log
 ngircd-test2.log
 ngircd-test1.motd
diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am
index f72453f1..9dc76a7d 100644
--- a/src/testsuite/Makefile.am
+++ b/src/testsuite/Makefile.am
@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2011 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
@@ -20,7 +20,7 @@ EXTRA_DIST = \
 	test-loop.sh wait-tests.sh \
 	channel-test.e connect-test.e check-idle.e invite-test.e \
 	join-test.e kick-test.e message-test.e misc-test.e mode-test.e \
-	opless-channel-test.e server-link-test.e who-test.e \
+	opless-channel-test.e server-link-test.e who-test.e whois-test.e \
 	stress-A.e stress-B.e \
 	start-server1 stop-server1 ngircd-test1.conf \
 	start-server2 stop-server2 ngircd-test2.conf
@@ -85,6 +85,10 @@ who-test: tests.sh
 	rm -f who-test
 	ln -s $(srcdir)/tests.sh who-test
 
+whois-test: tests.sh
+	rm -f whois-test
+	ln -s $(srcdir)/tests.sh whois-test
+
 TESTS = start-server1 \
 	connect-test \
 	start-server2 \
@@ -97,6 +101,7 @@ TESTS = start-server1 \
 	mode-test \
 	opless-channel-test \
 	who-test \
+	whois-test \
 	server-link-test \
 	stop-server2 \
 	stress-server.sh \
diff --git a/src/testsuite/whois-test.e b/src/testsuite/whois-test.e
new file mode 100644
index 00000000..7024d5ff
--- /dev/null
+++ b/src/testsuite/whois-test.e
@@ -0,0 +1,53 @@
+# ngIRCd test suite
+# WHOIS test
+
+spawn telnet localhost 6789
+expect {
+	timeout { exit 1 }
+	"Connected"
+}
+
+send "nick nick\r"
+send "user user . . :Real Name\r"
+expect {
+	timeout { exit 1 }
+	"376"
+}
+
+send "whois nick\r"
+expect {
+	timeout { exit 1 }
+	"311 nick nick ~user localhost \* :Real Name\r"
+}
+
+send "whois *\r"
+expect {
+	timeout { exit 1 }
+	"311 nick nick ~user localhost \* :Real Name\r"
+}
+
+send "whois n*\r"
+expect {
+	timeout { exit 1 }
+	"311 nick nick ~user localhost \* :Real Name\r"
+}
+
+send "whois ?ick\r"
+expect {
+	timeout { exit 1 }
+	"311 nick nick ~user localhost \* :Real Name\r"
+}
+
+send "whois ????,n?*k\r"
+expect {
+	timeout { exit 1 }
+	"311 nick nick ~user localhost \* :Real Name\r"
+}
+
+send "quit\r"
+expect {
+	timeout { exit 1 }
+	"ERROR"
+}
+
+# -eof-