diff -ur modules-old/core/m_mode.c modules/core/m_mode.c
--- modules-old/core/m_mode.c	2003-08-25 21:23:43.000000000 +0100
+++ modules/core/m_mode.c	2003-08-25 21:23:59.000000000 +0100
@@ -42,6 +42,8 @@
 #include "parse.h"
 #include "modules.h"
 #include "packet.h"
+#include "sprintf_irc.h"
+#include "s_log.h"
 
 static void m_mode(struct Client *, struct Client *, int, char **);
 
@@ -66,6 +68,43 @@
 
 const char *_version = "$Revision: 1.73.2.1 $";
 #endif
+
+static void
+loc_channel_modes(struct Channel *chptr, struct Client *client_p, char *mbuf, char *pbuf)
+{
+	int len;
+	*mbuf++ = '+';
+	*pbuf = '\0';
+
+	if(chptr->mode.mode & MODE_SECRET)
+		*mbuf++ = 's';
+	if(chptr->mode.mode & MODE_PRIVATE)
+		*mbuf++ = 'p';
+	if(chptr->mode.mode & MODE_MODERATED)
+		*mbuf++ = 'm';
+	if(chptr->mode.mode & MODE_TOPICLIMIT)
+		*mbuf++ = 't';
+	if(chptr->mode.mode & MODE_INVITEONLY)
+		*mbuf++ = 'i';
+	if(chptr->mode.mode & MODE_NOPRIVMSGS)
+		*mbuf++ = 'n';
+
+	if(chptr->mode.limit)
+	{
+		*mbuf++ = 'l';
+		len = ircsprintf(pbuf, "%d ", chptr->mode.limit);
+		pbuf += len;
+	}
+
+	if(*chptr->mode.key)
+	{
+		*mbuf++ = 'k';
+		ircsprintf(pbuf, "%s ", chptr->mode.key);
+	}
+
+	*mbuf++ = '\0';
+}
+
 /*
  * m_mode - MODE command handler
  * parv[0] - sender
@@ -79,8 +118,18 @@
 	static char modebuf[MODEBUFLEN];
 	static char parabuf[MODEBUFLEN];
 	int n = 2;
+	char *my_parv;
+	int whois_cheat = 0;
+
+	my_parv = parv[1];
+
+	if(IsOperSpy(source_p) && *my_parv == '!')
+	{
+		my_parv++;
+		whois_cheat = 1;
+	}
 
-	if(EmptyString(parv[1]))
+	if(EmptyString(my_parv))
 	{
 		sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
 			   me.name, parv[0], "MODE");
@@ -88,21 +137,21 @@
 	}
 
 	/* Now, try to find the channel in question */
-	if(!IsChanPrefix(parv[1][0]))
+	if(!IsChanPrefix(*my_parv))
 	{
 		/* if here, it has to be a non-channel name */
 		user_mode(client_p, source_p, parc, parv);
 		return;
 	}
 
-	if(!check_channel_name(parv[1]))
+	if(!check_channel_name(my_parv))
 	{
 		sendto_one(source_p, form_str(ERR_BADCHANNAME),
 			   me.name, parv[0], (unsigned char *) parv[1]);
 		return;
 	}
 
-	chptr = hash_find_channel(parv[1]);
+	chptr = hash_find_channel(my_parv);
 
 	if(chptr == NULL)
 	{
@@ -113,7 +162,14 @@
 	/* Now know the channel exists */
 	if(parc < n + 1)
 	{
-		channel_modes(chptr, source_p, modebuf, parabuf);
+		if(whois_cheat)
+		{
+			log_operspy(source_p, "MODE", chptr->chname);
+			loc_channel_modes(chptr, source_p, modebuf, parabuf);
+		}
+		else
+			channel_modes(chptr, source_p, modebuf, parabuf);
+
 		sendto_one(source_p, form_str(RPL_CHANNELMODEIS),
 			   me.name, parv[0], parv[1], modebuf, parabuf);
 
diff -ur modules-old/m_who.c modules/m_who.c
--- modules-old/m_who.c	2003-08-25 21:23:43.000000000 +0100
+++ modules/m_who.c	2003-08-25 21:28:28.000000000 +0100
@@ -42,6 +42,7 @@
 #include "parse.h"
 #include "modules.h"
 #include "packet.h"
+#include "s_log.h"
 
 static void m_who(struct Client *, struct Client *, int, char **);
 
@@ -67,7 +68,7 @@
 static void do_who_on_channel(struct Client *source_p, struct Channel *chptr,
 			      int server_oper, int member);
 
-static void who_global(struct Client *source_p, char *mask, int server_oper);
+static void who_global(struct Client *source_p, char *mask, int server_oper, int);
 
 static void do_who(struct Client *source_p, struct Client *target_p, const char *chname, const char *op_flags);
 
@@ -89,6 +90,7 @@
 	struct Channel *chptr = NULL;
 	int server_oper = parc > 2 ? (*parv[2] == 'o') : 0;	/* Show OPERS only */
 	int member;
+	int whois_cheat = 0;
 
 	/* See if mask is there, collapse it or return if not there */
 	if(mask != NULL)
@@ -118,7 +120,7 @@
 				last_used = CurrentTime;
 		}
 
-		who_global(source_p, mask, server_oper);
+		who_global(source_p, mask, server_oper, 0);
 		sendto_one(source_p, form_str(RPL_ENDOFWHO), me.name, parv[0], "*");
 		return;
 	}
@@ -141,6 +143,20 @@
 		return;
 	}
 
+	if(IsOperSpy(source_p) && *mask == '!')
+	{
+		mask++;
+		whois_cheat = 1;
+
+		if(EmptyString(mask))
+		{
+			sendto_one(source_p, form_str(RPL_ENDOFWHO), 
+				   me.name, parv[0], mask);
+			return;
+		}
+			
+	}
+
 	/* '/who #some_channel' */
 	if(IsChannelName(mask))
 	{
@@ -150,7 +166,9 @@
 		chptr = hash_find_channel(mask);
 		if(chptr != NULL)
 		{
-			if(IsMember(source_p, chptr))
+			if(whois_cheat)
+				log_operspy(source_p, "WHO", chptr->chname);
+			if(IsMember(source_p, chptr) || whois_cheat)
 				do_who_on_channel(source_p, chptr, server_oper, YES);
 			else if(!SecretChannel(chptr))
 				do_who_on_channel(source_p, chptr, server_oper, NO);
@@ -210,9 +228,9 @@
 
 	/* '/who 0' */
 	if((*(mask + 1) == '\0') && (*mask == '0'))
-		who_global(source_p, NULL, server_oper);
+		who_global(source_p, NULL, server_oper, 0);
 	else
-		who_global(source_p, mask, server_oper);
+		who_global(source_p, mask, server_oper, whois_cheat);
 
 	/* Wasn't a nick, wasn't a channel, wasn't a '*' so ... */
 	sendto_one(source_p, form_str(RPL_ENDOFWHO), me.name, parv[0], mask);
@@ -282,19 +300,24 @@
  *		  this is slightly expensive on EFnet ...
  */
 static void
-who_global(struct Client *source_p, char *mask, int server_oper)
+who_global(struct Client *source_p, char *mask, int server_oper, int whois_cheat)
 {
 	struct Client *target_p;
 	struct membership *msptr;
 	dlink_node *lp, *ptr;
 	int maxmatches = 500;
 
-	/* first, list all matching INvisible clients on common channels */
-	DLINK_FOREACH(lp, source_p->user->channel.head)
+	if(!whois_cheat)
 	{
-		msptr = lp->data;
-		who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches);
+		/* first, list all matching INvisible clients on common channels */
+		DLINK_FOREACH(lp, source_p->user->channel.head)
+		{
+			msptr = lp->data;
+			who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches);
+		}
 	}
+	else
+		log_operspy(source_p, "WHO", mask);
 
 	/* second, list all matching visible clients */
 	DLINK_FOREACH(ptr, global_client_list.head)
@@ -303,7 +326,7 @@
 		if(!IsPerson(target_p))
 			continue;
 
-		if(IsInvisible(target_p))
+		if(IsInvisible(target_p) && !whois_cheat)
 		{
 			ClearMark(target_p);
 			continue;
diff -ur modules-old/m_whois.c modules/m_whois.c
--- modules-old/m_whois.c	2003-08-25 21:23:43.000000000 +0100
+++ modules/m_whois.c	2003-08-25 21:23:59.000000000 +0100
@@ -45,9 +45,10 @@
 #include "parse.h"
 #include "modules.h"
 #include "hook.h"
+#include "s_log.h"
 
 static void do_whois(struct Client *client_p, struct Client *source_p, int parc, char *parv[]);
-static void single_whois(struct Client *source_p, struct Client *target_p, int);
+static void single_whois(struct Client *source_p, struct Client *target_p, int, int);
 
 static void m_whois(struct Client *, struct Client *, int, char **);
 static void ms_whois(struct Client *, struct Client *, int, char **);
@@ -204,6 +205,7 @@
 	char *nick;
 	char *p = NULL;
 	int glob = 0;
+	int whois_cheat = 0;
 
 	/* This lets us make all "whois nick" queries look the same, and all
 	 * "whois nick nick" queries look the same.  We have to pass it all
@@ -217,13 +219,28 @@
 		*p = '\0';
 
 	(void) collapse(nick);
+	if(IsOperSpy(source_p) && *nick == '!')
+	{
+		whois_cheat = 1;
+		nick++;
+	}
+
 	if(((target_p = find_client(nick)) != NULL) && IsPerson(target_p))
 	{
 		/* cant give detailed whois on a remote client */
 		if(!MyClient(target_p))
 			glob = 0;
 
-		single_whois(source_p, target_p, glob);
+		if(whois_cheat)
+		{
+			char buffer[BUFSIZE];
+
+			snprintf(buffer, sizeof(buffer), "%s!%s@%s %s",
+				 target_p->name, target_p->username,
+				 target_p->host, target_p->user->server);
+			log_operspy(source_p, "WHOIS", buffer);
+		}
+		single_whois(source_p, target_p, glob, whois_cheat);
 	}
 	else
 		sendto_one(source_p, form_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
@@ -244,7 +261,7 @@
  * 		  writing results to source_p
  */
 static void
-single_whois(struct Client *source_p, struct Client *target_p, int glob)
+single_whois(struct Client *source_p, struct Client *target_p, int glob, int whois_cheat)
 {
 	char buf[BUFSIZE];
 	const char *name;
@@ -258,6 +275,10 @@
 	char *t;
 	int tlen;
 	struct hook_mfunc_data hd;
+	int whois_len = 4;
+
+	if(whois_cheat)
+		whois_len = 5;
 
 	if(target_p->name[0] == '\0')
 		name = "?";
@@ -289,16 +310,17 @@
 		msptr = lp->data;
 		chptr = msptr->chptr;
 
-		if(ShowChannel(source_p, chptr))
+		if(ShowChannel(source_p, chptr) || whois_cheat)
 		{
-			if((cur_len + strlen(chptr->chname) + 3) > (BUFSIZE - 4))
+			if((cur_len + strlen(chptr->chname) + 3) > (BUFSIZE - whois_len))
 			{
 				sendto_one(source_p, "%s", buf);
 				cur_len = mlen;
 				t = buf + mlen;
 			}
 
-			ircsprintf(t, "%s%s ", 
+			ircsprintf(t, "%s%s%s ", 
+				   ShowChannel(source_p, chptr) ? "" : "!",
 				   find_channel_status(msptr, 1), 
 				   chptr->chname);
 
