Author: Isomer <isomer@coders.net>
authorPerry Lorier <isomer@undernet.org>
Sun, 9 Jul 2000 04:52:59 +0000 (04:52 +0000)
committerPerry Lorier <isomer@undernet.org>
Sun, 9 Jul 2000 04:52:59 +0000 (04:52 +0000)
Log message:

* Updated doc/ircd.8, it was 12 years out of date.
* rewrote m_whois.c, much clearer how it works, fixed a couple of it's quirks
and documented how it works.  Should work the same as the old version.
If the phase of the moon is in the right quarter it may even use less cpu.
* updated rfc1459.unet
* fixed some IPcheck funnies.
* fixed the numeric nick collision funnies? (unsure)
* We're now keeping track of server connections in IPcheck to prevent us having
to special case them all over the place.

Testing required:
 * does it run.
 * does whois work as expected?
 * does ipcheck still core?
 * are the docs all up to date?

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@265 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

13 files changed:
ChangeLog
RELEASE.NOTES
config/config-sh.in
doc/Configure.help
doc/example.conf
doc/ircd.8
doc/p10.html
include/IPcheck.h
ircd/IPcheck.c
ircd/channel.c
ircd/m_nick.c
ircd/m_whois.c
ircd/s_bsd.c

index bef1a32f3fbb1398556c20240fe42b394388abd2..f1feee49a963035c1d005a52bd51deddf2cac84a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2000-07-08  Perry Lorier       <Isomer@coders.net>
+       * doc/ircd.8: Updated the documentation, it was slightly out of date
+                     being updated around the 05 March 1988.
+       * ircd/m_whois.c: Rewrote for clarity, and probably a bit more speed.
+                         fixed a few minor glitches.
+       * doc/rfc1459.unet: Updated.
+       * ircd/IPcheck.c: Fixed more bugs.
+       * ircd/s_bsd.c: We now keep track of servers we've conected.
+
 2000-07-02  Perry Lorier       <Isomer@coders.net>
        * ircd/s_misc.c: Fixed remote IPcheck bug.  Ok, I'm a moron, so sue
                        me.  Thanks to Hektik, thanks thanks thanks thanks
 #
 # ChangeLog for ircu2.10.11
 #
-# $Id: ChangeLog,v 1.153 2000-07-02 01:06:17 isomer Exp $
+# $Id: ChangeLog,v 1.154 2000-07-09 04:52:58 isomer Exp $
 #
 # Insert new changes at beginning of the change list.
 #
index 47c4f2b3e35e7e0d3c5e892828f693f8bad9690d..42dbf24e3c58e4e615a85a99f247d93b20d2cf9d 100644 (file)
@@ -68,11 +68,6 @@ run make config.
 -DEXTENDED_NUMERICS This option configures the server to send
 extended numerics as well as parse them. This option should only
 be used on networks that run ircu2.10.07 and above only.
--DFERGUSON_FLUSHER This causes your server to attempt to flush
-it's internal buffers before dropping clients during a netbreak.
-This option *may* panic your server - especially if your running
-on a unconfigured FreeBSD box.  If you can define this we recommend
-you do - but beware of spontanious reboots on netbreak.
 -DWALLOPS_OPER_ONLY Setting this option removes the ability for
 clients that are not opered to see wallops messages.
 -DNOOPER Disallow the /oper command.
index 9fc336e713cc3a3e60c14bbe4f6c01464a3a609d..1c26420b5d3b5dc31e9b787d1e755c7b686f0dab 100644 (file)
@@ -317,6 +317,7 @@ comment 'Configuration'
   else
     int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 9000000
   fi
+  bool 'Aggressively empty the sendqpool (Read Help!)' HAS_FERGUSON_FLUSHER n
   int 'Max receive queue for clients (bytes)' CLIENT_FLOOD 1024
   int 'Maximum number of network connections (23 - (FD_SETSIZE-4))' MAXCONNECTIONS 252
   int 'Default port for connections to other servers' SERVER_PORT 4400
@@ -384,7 +385,7 @@ comment 'Mandatory defines (you should leave these untouched)'
     define_bool LOCAL_KILL_ONLY $LOCAL_KILL_ONLY
   fi
   int 'KILL nick chase time limit (30)' KILLCHASETIMELIMIT 30
-  int 'Max number of channels per user (recommended: 10)' MAXCHANNELSPERUSER 10
+  int 'Max number of channels per user' MAXCHANNELSPERUSER 10
   int 'Max number of silence masks (15!)' MAXSILES 15
   int 'Expected average banmask length (40!)' AVBANLEN 40
   eval define_macro MAXSILELENGTH \'\($AVBANLEN * MAXSILES\)\'
index 965c7f92c69b7fe6fabd152444e5463508619387..d8cf81472ec7cb8b201617a0bb4e4fdc934d05e0 100644 (file)
@@ -652,6 +652,15 @@ BUFFERPOOL
   have less than 4000 local clients.
   Don't forget to specify this value in bytes.
 
+Aggressively empty the sendqpool (Read Help!)
+HAS_FERGUSON_FLUSHER
+  Instead of dropping clients with 'Buffer Allocation Error', try to send
+  data to clients to try and drop the sendq buffer size as a last resort
+  before dropping clients.  This doesn't get rid of the problem, it just
+  tries hard to limit it's impact.
+  WARNING: *THIS PANICS UNCONFIGURED FREEBSD MACHINES*, make sure you've
+  read doc/freebsd.txt before enabling this feature on a FreeBSD machine.
+
 Max receive queue for clients (bytes)
 CLIENT_FLOOD
   Currently, everything that a client sends to a server is read by the server
index 5bcc3939a2f96d5cc789e095f370bade529c2c17..f867212a4121e3256723363859ab958e8e0223d1 100644 (file)
@@ -333,11 +333,15 @@ O:*@*.cs.vu.nl:VRKLKuGKn0jLs:Niels::10
 # ports;
 # Undernet uses 4400 for server listener ports.
 # These are just hints, they are in no way official IANA or IETF policies.
+# IANA says we should use port 194, but that requires us to run as root, so
+# we don't do that.
 #
 # The interface setting allows multiply homed hosts to specify which
 # interface to use on a port by port basis, if an interface is not specified
 # the default interface will be used. The interface MUST be the complete
 # IP address for a real hardware interface on the machine running ircd.
+# If you want to use virtual hosting *YOU* *MUST* *USE* *THIS* otherwise it
+# WILL bind to all interfaces - not what most people seem to expect.
 #
 # The [CS][H] field is an optional field to specify that a port is a
 # server port or a client port and whether it's hidden or not.
index be36b88fc323e56497f01e0fb1d9fd0ce5968dc0..92a1369ab50d88e8d2f86d4407a1438764c45ad1 100644 (file)
@@ -1,12 +1,11 @@
 .\" @(#)ircd.8 2.0 (beta version) 29 Mar 1989 
-.TH IRCD 8 "29 March 1989"
+.TH IRCD 8 "8 July 2000"
 .SH NAME
-ircd \- The Internet Relay Chat Program Server 
+ircd \- The Undernet Internet Relay Chat Daemon
 .SH SYNOPSIS
 .hy 0
 .IP \fBircd\fP
-[-a] [-c] [-i] [-o] [-q] [-t] [-d directory]
-[-f configfile] [-w interface] [-x debuglevel] [-h hostname] [-p portnum]
+[-t] [-d directory] [-f configfile] [-x debuglevel] [-h hostname]
 .SH DESCRIPTION
 .LP
 \fIircd\fP is the server (daemon) program for the Internet Relay Chat
@@ -16,7 +15,10 @@ and user messages are passed directly to the \fIircd\fP for processing
 and relaying to other ircd sites.  The \fIirc(1)\fP program depends upon
 there being an \fIircd\fP server running somewhere (either on your local
 UNIX site or a remote ircd site) so that it will have somewhere to connect
-to and thus allow the user to begin talking to other users. 
+to and thus allow the user to begin talking to other users.
+.LP
+There are many common clients including ircii, epic, BitchX and other ones
+for Non unix platforms as well, such as mIRC, Pirch, Ircle etc.
 .SH OPTIONS
 .TP
 .B \-d directory
@@ -25,26 +27,14 @@ that as a reference point when opening \fIircd.conf\fP and other startup
 files.
 .TP
 .B \-o
-Starts up a local ircdaemon. Standard input can be used to send IRC
-commands to the daemon. The user logging in from standard input will
-be given operator privileges on this local ircd. If ircd is a setuid program,
-it will call setuid(getuid()) before going to local mode. This option
-can be used in inetd.conf to allow users to open their own irc clients
-by simply connecting their clients to the correct ports. For example:
-.TP
-.B
-irc stream tcp nowait irc /etc/ircd ircd \\-f/etc/ircd.conf \\-o
-
-allows users connecting to irc port (specified in /etc/services) to start
-up their own ircdaemon. The configuration file should be used to check from
-which hosts these connections are allowed from. This option also turns
-on the autodie option -a.
+This option is obsolete.
 .TP
 .B \-a
-Instructs the server to automatically die off if it loses all it's clients.
+This option is obsolete.
 .TP
 .B \-t
-Instructs the server run in the foreground and to direct debugging output to standard output.
+Instructs the server run in the foreground and to direct debugging output to
+standard output.
 .TP
 .B \-x#
 Defines the debuglevel for ircd. The higher the debuglevel, the more stuff
@@ -52,21 +42,12 @@ gets directed to debugging file (or standard output if -t option was used
 as well).
 .TP
 .B \-i
-The server was started by inetd and it should start accepting connections
-from standard input. The following inetd.conf-line could be used to start
-up ircd automatically when needed:
-.TP
-.B
-ircd stream tcp wait irc /etc/ircd ircd \-i
-
-allows inetd to start up ircd on request.
+This option is obsolete.
 .TP
 .B \-w interface
-If the server was compiled with VIRTUAL_HOST (run 'make config' to toggle
-this compile option), then \fIinterface\fP is passed to gethostbyname(3) in
-order to retrieve the IP-number of the interface to bind to. An example
-would be to use '-w localhost', after which the server only listens on the
-loopback interface.  Run `ifconfig -a' to see which interfaces you have.
+This command is depreciated.  Outgoing connections are bound to the
+interface specified in the M:, and Incoming connections are accepted only on
+interfaces specified in the P:'s.
 .TP
 .B \-f filename
 Specifies the ircd.conf file to be used for this ircdaemon. The option
@@ -79,25 +60,21 @@ off and run in the background. This needs to be given if you are starting
 \fIircd\fP from an \fIrc\fP (such as \fI/etc/rc.local\fP) file.
 .TP
 .B \-q
-Using the -q option stops the server from doing DNS lookups on all the
-servers in your \fIircd.conf\fP file when it boots. This can take a lengthy
-amount of time if you have a large number of servers and they are not all
-close by.
+This option is obsolete.
 .TP
 .B \-h hostname
 Allows the user to manually set the server name at startup. The default
 name is hostname.domainname.
 .B \-p portname
-Specifies the server port where the daemon should start waiting for connections
-from other servers. Clients should connect to ports as specified in the ircd.conf file by means of a P: line.
-.TP
-.SH
+This is depreciated in favour of specifying server ports in P:'s
+
+.SH CONFIGURATION
 If you plan to connect your \fIircd\fP server to an existing Irc-Network,
 you will need to alter your local IRC CONFIGURATION FILE (typically named
 "ircd.conf") so that it will accept and make connections to other \fIircd\fP
 servers.  This file contains the hostnames, Network Addresses, and sometimes
 passwords for connections to other ircds around the world.  Because 
-description of the actual file format of the "ircs.conf" file is beyond the
+description of the actual file format of the "ircd.conf" file is beyond the
 scope of this document, please refer to the file INSTALL in the IRC source
 files documentation directory.
 .LP
@@ -140,8 +117,9 @@ For full COPYRIGHT see LICENSE file with IRC package.
 .SH "SEE ALSO"
 irc(1)
 .SH BUGS
-None... ;-) if somebody finds one, please inform author
+See the file 'BUGS' included in the distribution.
 .SH AUTHOR
-Jarkko Oikarinen, currently jto@tolsun.oulu.fi,
-manual page written by Jeff Trim, jtrim@orion.cair.du.edu,
-later modified by jto@tolsun.oulu.fi.
+The current authors of the undernet IRC daemon are coder-com@undernet.org,
+the original author was Jarkko Oikarinen, currently jto@tolsun.oulu.fi,
+manual page written by Jeff Trim, jtrim@orion.cair.du.edu, later modified by
+jto@tolsun.oulu.fi and then isomer@coders.net.
index eafab9f8e6be05fb828263659a169adfa3b96e91..c90071510e6619cb838eb51b3724917dd69ab27a 100644 (file)
@@ -12,7 +12,7 @@
 <I>(As of ircu 2.10.11)</I>
 <H2>
 Undernet Coder-com, <TT>coder-com@undernet.org</TT></H2>
-$Id: p10.html,v 1.4 2000-04-11 05:51:25 gte Exp $
+$Id: p10.html,v 1.5 2000-07-09 04:52:58 isomer Exp $
 <P>
 <HR ALIGN=CENTER WIDTH=100% SIZE=2><I>This document aims to be a practical
 guide for implementing and maintaining the protocol, not just a reference
@@ -623,7 +623,13 @@ This token is formatted exactly the same as a client numeric is formatted.
 The first 2 characters identify the server's numeric, whilst in this situation,
 the final 3 characters define the maximum number of clients that this server
 can hold (and more importantly, the maximum number of numerics it will
-generate).</LI>
+generate). This is always one less than a power of two, because the server
+uses this as a bitmask.  A server can give out a higher numeric than this,
+however it will be "anded" with this number to find it's entry slot.  The
+reason for this is so a server which is near the maximum number of clients
+can give out more numerics than it's using to prevent a new client getting a
+numeric that was used only seconds ago and maybe get messages destined to
+the old user.</LI>
 
 <LI>
 &nbsp;The example "AA]]]" shows that this is a server with numeric 0, which
@@ -633,7 +639,8 @@ will generate client numerics up to 262,143.</LI>
 <LI>
 This final parameter simply consists of a textual description of the server
 prefixed by a colon. This is displayed in a clients WHOIS line, aswell
-as in the LINKS reply.</LI>
+as in the LINKS reply.  By convention, if this is a leaf server it contains
+the servers IP in square brackets at the beginning of the string,</LI>
 </OL>
 <A NAME="chap3.2"></A><B><FONT SIZE=+1>3.2 Network Database resyncronisation</FONT></B>
 <P>After the connection has been established and verified, the next step
@@ -652,7 +659,8 @@ the originally recieved server message, with the only exception being that
 the message is numeric prefixed, to indicate which server sent this message
 (and also therefore, which hub this new server is linked too). There is
 also a fixed "0" present before the Description field, this is a placeholder
-for future use and currently unused.</FONT>
+for future use and currently unused. [Isomer: Question, what IS this
+reserved for?] </FONT>
 <P><B>3.2.2 - NICK Messages</B></BLOCKQUOTE>
 
 <BLOCKQUOTE>Client information is transmitted via "NICK" messages, of the
@@ -1137,6 +1145,7 @@ from msg.h. -Gte.
 <BR>[2000-04-10]: Added information about OPMODE and CLEARMODE tokens.
 -Kev
 <BR>[2000-04-11]: Started work on chapter 4. -Gte
+<BR>[2000-06-01]: Changed some info about the max number of clients -Isomer
 <P><A NAME="chap8.1"></A><B><FONT SIZE=+1>8.1 TODO</FONT></B>
 <UL>
 <LI>
index e47eb31d3af72ed9388b9986573a35e0e91e2d5e..0c7f23fe50d9258246383cbfdc31db37c1ecfc50 100644 (file)
@@ -18,6 +18,7 @@ struct Client;
  *--------------------------------------------------------------------------*/
 extern void ip_registry_expire(void);
 extern int  ip_registry_check_local(unsigned int addr, time_t* next_target_out);
+extern void ip_registry_add_local(unsigned int addr);
 extern int  ip_registry_remote_connect(struct Client *cptr);
 extern void ip_registry_connect_succeeded(struct Client *cptr);
 extern void ip_registry_local_disconnect(struct Client *cptr);
index 2e9dce223c4b187d4d30482b1efbb09c99fa5200..47bb61b2bf029df97b509ae9f7bfe462e2d20917 100644 (file)
@@ -113,7 +113,7 @@ static unsigned int ip_registry_hash(unsigned int ip)
  *--------------------------------------------------------------------------*/
 static struct IPRegistryEntry *ip_registry_find(unsigned int ip) 
 {
-  struct IPRegistryEntry *entry = NULL;
+  struct IPRegistryEntry *entry = 0;
 
   for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) {
     if (entry->addr == ip)
@@ -361,6 +361,28 @@ int ip_registry_check_local(unsigned int addr, time_t *next_target_out)
   return 0;
 }
 
+/*
+ * Add someone to the ip registry without throttling them.
+ * This is used for server connections.
+ */
+void ip_registry_add_local(unsigned int addr)
+{
+  struct IPRegistryEntry *entry        = ip_registry_find(addr);
+  /* If they've never connected before, let them on */
+  if (0 == entry) {
+    Debug((DEBUG_DEBUG,"IPcheck: Local user allowed - unseen"));
+    entry = ip_registry_new_entry(addr, 1);
+    return;
+  }
+  
+  /* Keep track of how many people have connected */
+  entry->connected++;
+
+  assert(250 <= entry->connected);
+
+  return;
+}
 
 /*----------------------------------------------------------------------------
  * ip_registry_remote_connect
@@ -460,15 +482,6 @@ void ip_registry_local_disconnect(struct Client *cptr)
 
   entry = ip_registry_find(cptr->ip.s_addr);
 
-  /* Servers might not be in IPcheck because we connected to them, not visa
-   * versa.
-   * We can't use IsServer() here, because it might be in the 'unregistered'
-   * state.
-   */
-  if (0 != cptr->serv && !entry) {
-        Debug((DEBUG_DEBUG,"IPcheck: Server ignored"));
-       return;
-  }
   Debug((DEBUG_DEBUG,"IPcheck: Local Disconnect"));
        
   assert(IsIPChecked(cptr));
index 560bcadb8ca93432ba34d3fa649dc3b26410b2b5..76a20e8e1d0cdffec7a931a2182714cd969b47d6 100644 (file)
@@ -227,7 +227,7 @@ int sub1_from_channel(struct Channel* chptr)
     int i;
     for (i = 0; i <= HighestFd; i++)
     {
-      struct Client *acptr;
+      struct Client *acptr = 0;
       if ((acptr = LocalClientArray[i]) && acptr->listing &&
           acptr->listing->chptr == chptr)
       {
index b8cf167b593f2bee876ef5eb352c5d49893f520d..aa388825fe470858dbd7de1aded3e6ed87dcc4d6 100644 (file)
@@ -269,17 +269,15 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   ircd_strncpy(nick, parv[1], NICKLEN);
   nick[NICKLEN] = '\0';
 
-  if (!IsBurstOrBurstAck(sptr)) {
-     if (IsServer(sptr)) {
-       lastnick = atoi(parv[3]);
-       if (lastnick > OLDEST_TS) 
-         sptr->serv->lag = TStime() - lastnick;
-     }
-     else {
-       lastnick = atoi(parv[2]); 
-       if (lastnick > OLDEST_TS)
-         sptr->user->server->serv->lag = TStime() - lastnick;
-     }
+  if (IsServer(sptr)) {
+    lastnick = atoi(parv[3]);
+    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) 
+      sptr->serv->lag = TStime() - lastnick;
+  }
+  else {
+    lastnick = atoi(parv[2]); 
+    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
+      sptr->user->server->serv->lag = TStime() - lastnick;
   }
   /*
    * If do_nick_name() returns a null name OR if the server sent a nick
index 0f2bf933b53fd10f9efb9d70f7ea0a23bb71dfad..c2b3fa191fe4fb4355e2d3bcf2cf197ba1699b3a 100644 (file)
 #include <string.h>
 
 /*
- * m_whois - generic message handler
- *
- * parv[0] = sender prefix
- * parv[1] = nickname masklist
- *
- * or
- *
- * parv[1] = target server
- * parv[2] = nickname masklist
+ * 2000-07-01: Isomer
+ *  * Rewritten to make this understandable
+ *  * You can nolonger /whois unregistered clients.
+ *  
+ *
+ * General rules:
+ *  /whois nick always shows the nick.
+ *  /whois wild* shows the nick if:
+ *   * they aren't +i and aren't on any channels.
+ *   * they are on a common channel.
+ *   * they aren't +i and are on a public channel. (not +p and not +s)
+ *   * they aren't +i and are on a private channel. (+p but not +s)
+ *  Or to look at it another way (I think):
+ *   * +i users are only shown if your on a common channel.
+ *   * users on +s channels only aren't shown.
+ *
+ *  whois doesn't show what channels a +k client is on, for the reason that
+ *  /whois X or /whois W floods a user off the server. :)
+ *
+ * nb: if the code and this comment disagree, the codes right and I screwed
+ *     up.
  */
-int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
-{
-  struct User*    user;
-  struct Client*  acptr;
-  struct Client*  a2cptr;
-  struct Channel* chptr;
-  char*           nick;
-  char*           tmp;
-  const char*     name;
-  char*           p = 0;
-  int             found;
-  int             len;
-  int             mlen;
-  int             total;
-  static char     buf[512];
 
-  if (parc < 2)
-  {
-    send_reply(sptr, ERR_NONICKNAMEGIVEN);
-    return 0;
-  }
-
-  if (parc > 2)
-  {
-    struct Client *acptr;
-    /* For convenience: Accept a nickname as first parameter, by replacing
-       it with the correct servername - as is needed by hunt_server() */
-    if (MyUser(sptr) && (acptr = FindUser(parv[1])))
-      parv[1] = acptr->user->server->name;
-    if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
-        HUNTED_ISME)
-      return 0;
-    parv[1] = parv[2];
-  }
+/*
+ * Send whois information for acptr to sptr
+ */
+static void do_whois(struct Client* sptr, struct Client *acptr)
+{
+  struct Client *a2cptr=0;
+  struct Channel *chptr=0;
+  int mlen;
+  int len;
+  static char buf[512];
+  
+  const struct User* user = acptr->user;
+  const char* name = (!*acptr->name) ? "?" : acptr->name;  
+  a2cptr = user->server;
+  assert(user);
+  send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
+                  acptr->info);
 
-  total = 0;
-  for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
+  /* Display the channels this user is on. */
+  if (!IsChannelService(acptr))
   {
-    int invis, showperson, member, wilds;
-
-    found = 0;
-    collapse(nick);
-    wilds = (strchr(nick, '?') || strchr(nick, '*'));
-    /* Do a hash lookup if the nick does not contain wilds */
-    if (wilds)
+    struct Membership* chan;
+    mlen = strlen(me.name) + strlen(sptr->name) + 12 + strlen(name);
+    len = 0;
+    *buf = '\0';
+    for (chan = user->channel; chan; chan = chan->next_channel)
     {
-      /*
-       * We're no longer allowing remote users to generate requests with wildcards.
-       */
-      if (!MyConnect(sptr))
-        continue;
-      for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
-          acptr = acptr->next)
-      {
-        if (!IsRegistered(acptr) || IsServer(acptr))
+       chptr = chan->channel;
+       
+       if (!ShowChannel(sptr, chptr))
           continue;
-        /*
-         * I'm always last :-) and acptr->next == 0!!
-         */
-        if (IsMe(acptr))
-          break;
-        /*
-         * 'Rules' established for sending a WHOIS reply:
-         *
-         * - if wildcards are being used dont send a reply if
-         *   the querier isnt any common channels and the
-         *   client in question is invisible and wildcards are
-         *   in use (allow exact matches only);
-         *
-         * - only send replies about common or public channels
-         *   the target user(s) are on;
-         */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-
-        invis = acptr != sptr && IsInvisible(acptr);
-        member = (user && user->channel) ? 1 : 0;
-        showperson = (wilds && !invis && !member) || !wilds;
-        if (user) {
-          struct Membership* chan;
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            member = find_channel_member(sptr, chptr) ? 1 : 0;
-            if (invis && !member)
-              continue;
-            if (IsZombie(chan))
-              continue;
-            if (member || (!invis && PubChannel(chptr)))
-            {
-              showperson = 1;
-              break;
-            }
-            if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
-              showperson = 1;
-          }
-        }
-        if (!showperson)
+          
+       if (acptr != sptr && IsZombie(chan))
           continue;
-
-        if (user)
-        {
-          a2cptr = user->server;
-         send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                    acptr->info);
-        }
-        else
-        {
-          a2cptr = &me;
-         send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
-                    "<unknown>");
-        }
-
-        found = 1;
-
-exact_match:
-        if (user && !IsChannelService(acptr))
-        {
-          struct Membership* chan;
-          mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
-          len = 0;
+          
+       if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) 
+       {
+          send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf);
           *buf = '\0';
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            if (ShowChannel(sptr, chptr) &&
-                (acptr == sptr || !IsZombie(chan)))
-            {
-              if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
-              {
-               send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS,
-                          "%s :%s", name, buf);
-                *buf = '\0';
-                len = 0;
-              }
-              if (IsDeaf(acptr))
-                *(buf + len++) = '-';
-              if (is_chan_op(acptr, chptr))
-                *(buf + len++) = '@';
-              else if (has_voice(acptr, chptr))
-                *(buf + len++) = '+';
-              else if (IsZombie(chan))
-                *(buf + len++) = '!';
-              if (len)
-                *(buf + len) = '\0';
-              strcpy(buf + len, chptr->chname);
-              len += strlen(chptr->chname);
-              strcat(buf + len, " ");
-              len++;
-            }
-          }
-          if (buf[0] != '\0')
-           send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
-        }
-
-       send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
-
-        if (user)
-        {
-          if (user->away)
-           send_reply(sptr, RPL_AWAY, name, user->away);
-
-          if (IsAnOper(acptr))
-           send_reply(sptr, RPL_WHOISOPERATOR, name);
-
-          if (MyConnect(acptr))
-           send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
-                      acptr->firsttime);
-        }
-        if (found == 2 || total++ >= MAX_WHOIS_LINES)
-          break;
-      }
-    }
-    else
-    {
-      /* No wildcards */
-      if ((acptr = FindUser(nick)))
-      {
-        found = 2;              /* Make sure we exit the loop after passing it once */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-        a2cptr = user->server;
-       send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                  acptr->info);
-        goto exact_match;
-      }
-    }
-    if (!found)
-      send_reply(sptr, ERR_NOSUCHNICK, nick);
-    if (p)
-      p[-1] = ',';
-    if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
-      break;
+          len = 0;
+       }
+       if (IsDeaf(acptr))
+         *(buf + len++) = '-';
+       if (is_chan_op(acptr, chptr))
+         *(buf + len++) = '@';
+       else if (has_voice(acptr, chptr))
+         *(buf + len++) = '+';
+       else if (IsZombie(chan))
+         *(buf + len++) = '!';
+       if (len)
+          *(buf + len) = '\0';
+       strcpy(buf + len, chptr->chname);
+       len += strlen(chptr->chname);
+       strcat(buf + len, " ");
+       len++;
+     }
+     if (buf[0] != '\0')
+        send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
   }
-  send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
+  send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
 
-  return 0;
+  if (user)
+  {
+    if (user->away)
+       send_reply(sptr, RPL_AWAY, name, user->away);
+
+    if (IsAnOper(acptr))
+       send_reply(sptr, RPL_WHOISOPERATOR, name);
+   
+    /* Hint: if your looking to add more flags to a user, eg +h, here's
+     *       probably a good place to add them :)
+     */
+     
+    if (MyConnect(acptr))
+       send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, 
+                  acptr->firsttime);
+  }
 }
 
 /*
- * ms_whois - server message handler
- *
- * parv[0] = sender prefix
- * parv[1] = nickname masklist
- *
- * or
- *
- * parv[1] = target server
- * parv[2] = nickname masklist
+ * Search and return as many people as matched by the wild 'nick'.
+ * returns the number of people found (or, obviously, 0, if none where
+ * found).
  */
-int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+static int do_wilds(struct Client* sptr,char *nick,int count)
 {
-  struct User*    user;
-  struct Client*  acptr;
-  struct Client*  a2cptr;
-  struct Channel* chptr;
-  char*           nick;
-  char*           tmp;
-  const char*     name;
-  char*           p = 0;
-  int             found;
-  int             len;
-  int             mlen;
-  int             total;
-  static char     buf[512];
-
-  if (parc < 2)
-  {
-    send_reply(sptr, ERR_NONICKNAMEGIVEN);
-    return 0;
-  }
-
-  if (parc > 2)
-  {
-    struct Client *acptr;
-    /* For convenience: Accept a nickname as first parameter, by replacing
-       it with the correct servername - as is needed by hunt_server() */
-    if (MyUser(sptr) && (acptr = FindUser(parv[1])))
-      parv[1] = acptr->user->server->name;
-    if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
-        HUNTED_ISME)
-      return 0;
-    parv[1] = parv[2];
-  }
-
-  total = 0;
-  for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
+  struct Client *acptr; /* Current client we're concidering */
+  struct User *user;   /* the user portion of the client */
+  char *name;          /* the name of this client */
+  struct Membership* chan; 
+  int invis;           /* does +i apply? */
+  int member;          /* Is this user on any channels? */
+  int showperson;       /* Should we show this person? */
+  int found = 0 ;      /* How many were found? */
+  
+  /* Ech! This is hidious! */
+  for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
+      acptr = acptr->next)
   {
-    int invis, showperson, member, wilds;
-
-    found = 0;
-    collapse(nick);
-    wilds = (strchr(nick, '?') || strchr(nick, '*'));
-    /* Do a hash lookup if the nick does not contain wilds */
-    if (wilds)
+    if (!IsRegistered(acptr)) 
+      continue;
+      
+    if (IsServer(acptr))
+      continue;
+    /*
+     * I'm always last :-) and acptr->next == 0!!
+     *
+     * Isomer: Does this strike anyone else as being a horrible hidious
+     *         hack?
+     */
+    if (IsMe(acptr)) {
+      assert(!acptr->next);
+      break;
+    }
+    
+    /*
+     * 'Rules' established for sending a WHOIS reply:
+     *
+     * - if wildcards are being used dont send a reply if
+     *   the querier isnt any common channels and the
+     *   client in question is invisible.
+     *
+     * - only send replies about common or public channels
+     *   the target user(s) are on;
+     */
+    user = acptr->user;
+    name = (!*acptr->name) ? "?" : acptr->name;
+    assert(user);
+
+    invis = (acptr != sptr) && IsInvisible(acptr);
+    member = (user && user->channel) ? 1 : 0;
+    showperson = !invis && !member;
+    
+    /* Should we show this person now? */
+    if (showperson) {
+       found++;
+       do_whois(sptr,acptr);
+       if (count+found>MAX_WHOIS_LINES)
+         return found;
+       continue;
+    }
+    
+    /* Step through the channels this user is on */
+    for (chan = user->channel; chan; chan = chan->next_channel)
     {
-      /*
-       * We're no longer allowing remote users to generate requests with wildcards.
-       */
-      if (!MyConnect(sptr))
-        continue;
-      for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
-          acptr = acptr->next)
-      {
-        if (!IsRegistered(acptr) || IsServer(acptr))
-          continue;
-        /*
-         * I'm always last :-) and acptr->next == 0!!
-         */
-        if (IsMe(acptr))
-          break;
-        /*
-         * 'Rules' established for sending a WHOIS reply:
-         *
-         * - if wildcards are being used dont send a reply if
-         *   the querier isnt any common channels and the
-         *   client in question is invisible and wildcards are
-         *   in use (allow exact matches only);
-         *
-         * - only send replies about common or public channels
-         *   the target user(s) are on;
-         */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-
-        invis = acptr != sptr && IsInvisible(acptr);
-        member = (user && user->channel) ? 1 : 0;
-        showperson = (wilds && !invis && !member) || !wilds;
-        if (user) {
-          struct Membership* chan;
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            member = find_channel_member(sptr, chptr) ? 1 : 0;
-            if (invis && !member)
-              continue;
-            if (IsZombie(chan))
-              continue;
-            if (member || (!invis && PubChannel(chptr)))
-            {
-              showperson = 1;
-              break;
-            }
-            if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
-              showperson = 1;
-          }
-        }
-        if (!showperson)
-          continue;
-
-        if (user)
-        {
-          a2cptr = user->server;
-         send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                    acptr->info);
-        }
-        else
-        {
-          a2cptr = &me;
-         send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
-                    "<unknown>");
-        }
-
-        found = 1;
-
-exact_match:
-        if (user && !IsChannelService(acptr))
-        {
-          struct Membership* chan;
-          mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
-          len = 0;
-          *buf = '\0';
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            if (ShowChannel(sptr, chptr) &&
-                (acptr == sptr || !IsZombie(chan)))
-            {
-              if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
-              {
-               send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, 
-                          "%s :%s", name, buf);
-                *buf = '\0';
-                len = 0;
-              }
-              if (IsDeaf(acptr))
-                *(buf + len++) = '-';
-              if (is_chan_op(acptr, chptr))
-                *(buf + len++) = '@';
-              else if (has_voice(acptr, chptr))
-                *(buf + len++) = '+';
-              else if (IsZombie(chan))
-                *(buf + len++) = '!';
-              if (len)
-                *(buf + len) = '\0';
-              strcpy(buf + len, chptr->chname);
-              len += strlen(chptr->chname);
-              strcat(buf + len, " ");
-              len++;
-            }
-          }
-          if (buf[0] != '\0')
-           send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
-        }
+      struct Channel *chptr = chan->channel;
 
-       send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
-
-        if (user)
-        {
-          if (user->away)
-           send_reply(sptr, RPL_AWAY, name, user->away);
-
-          if (IsAnOper(acptr))
-           send_reply(sptr, RPL_WHOISOPERATOR, name);
-
-          if (MyConnect(acptr))
-           send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
-                      acptr->firsttime);
-        }
-        if (found == 2 || total++ >= MAX_WHOIS_LINES)
-          break;
+      /* If this is a public channel, show the person */
+      if (!invis && PubChannel(chptr)) {
+        showperson = 1;
+        break;
       }
-    }
-    else
-    {
-      /* No wildcards */
-      if ((acptr = FindUser(nick)))
-      {
-        found = 2;              /* Make sure we exit the loop after passing it once */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-        a2cptr = user->server;
-       send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                  acptr->info);
-        goto exact_match;
+      
+      /* if this channel is +p and not +s, show them */
+      if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) {
+          showperson = 1;
+          break;
       }
-    }
-    if (!found)
-      send_reply(sptr, ERR_NOSUCHNICK, nick);
-    if (p)
-      p[-1] = ',';
-    if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
-      break;
-  }
-  send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
+      
+      member = find_channel_member(sptr, chptr) ? 1 : 0;
+      if (invis && !member)
+        continue;
 
-  return 0;
+      /* If sptr isn't really on this channel, skip it */
+      if (IsZombie(chan))
+        continue;
+       
+      /* Is this a common channel? */ 
+      if (member) {
+        showperson = 1;
+        break;
+      }
+    } /* of for (chan in channels) */
+    
+    /* Don't show this person */
+    if (!showperson)
+      continue;
+      
+    do_whois(sptr,acptr);
+    found++;
+    if (count+found>MAX_WHOIS_LINES)
+       return found;  
+  } /* of global client list */
+  
+  return found;
 }
 
 /*
- * mo_whois - oper message handler
+ * m_whois - generic message handler
  *
  * parv[0] = sender prefix
  * parv[1] = nickname masklist
  *
  * or
  *
- * parv[1] = target server
+ * parv[1] = target server, or a nickname representing a server to target.
  * parv[2] = nickname masklist
  */
-int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+int m_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  struct User*    user;
-  struct Client*  acptr;
-  struct Client*  a2cptr;
-  struct Channel* chptr;
   char*           nick;
   char*           tmp;
-  const char*     name;
   char*           p = 0;
-  int             found;
-  int             len;
-  int             mlen;
-  int             total;
-  static char     buf[512];
+  int             found = 0;
+  int            total = 0;
 
   if (parc < 2)
   {
@@ -561,8 +344,11 @@ int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   {
     struct Client *acptr;
     /* For convenience: Accept a nickname as first parameter, by replacing
-       it with the correct servername - as is needed by hunt_server() */
-    if (MyUser(sptr) && (acptr = FindUser(parv[1])))
+     * it with the correct servername - as is needed by hunt_server().
+     * This is the secret behind the /whois nick nick trick.
+     */
+    acptr = FindUser(parv[1]);
+    if (acptr)
       parv[1] = acptr->user->server->name;
     if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
         HUNTED_ISME)
@@ -570,203 +356,64 @@ int mo_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
     parv[1] = parv[2];
   }
 
-  total = 0;
   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
   {
-    int invis, showperson, member, wilds;
+    int wilds;
 
     found = 0;
+    
     collapse(nick);
+    
     wilds = (strchr(nick, '?') || strchr(nick, '*'));
-    /* Do a hash lookup if the nick does not contain wilds */
-    if (wilds)
-    {
-      /*
-       * We're no longer allowing remote users to generate requests with wildcards.
-       */
-      if (!MyConnect(sptr))
-        continue;
-      for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
-          acptr = acptr->next)
-      {
-        if (!IsRegistered(acptr) || IsServer(acptr))
-          continue;
-        /*
-         * I'm always last :-) and acptr->next == 0!!
-         */
-        if (IsMe(acptr))
-          break;
-        /*
-         * 'Rules' established for sending a WHOIS reply:
-         *
-         * - if wildcards are being used dont send a reply if
-         *   the querier isnt any common channels and the
-         *   client in question is invisible and wildcards are
-         *   in use (allow exact matches only);
-         *
-         * - only send replies about common or public channels
-         *   the target user(s) are on;
-         */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-
-        invis = acptr != sptr && IsInvisible(acptr);
-        member = (user && user->channel) ? 1 : 0;
-        showperson = (wilds && !invis && !member) || !wilds;
-        if (user) {
-          struct Membership* chan;
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            member = find_channel_member(sptr, chptr) ? 1 : 0;
-            if (invis && !member)
-              continue;
-            if (IsZombie(chan))
-              continue;
-            if (member || (!invis && PubChannel(chptr)))
-            {
-              showperson = 1;
-              break;
-            }
-            if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
-              showperson = 1;
-          }
-        }
-        if (!showperson)
-          continue;
-
-        if (user)
-        {
-          a2cptr = user->server;
-         send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                    acptr->info);
-        }
-        else
-        {
-          a2cptr = &me;
-         send_reply(sptr, RPL_WHOISUSER, name, "<unknown>", "<unknown>",
-                    "<unknown>");
-        }
-
-        found = 1;
-
-exact_match:
-        if (user && !IsChannelService(acptr))
-        {
-          struct Membership* chan;
-          mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
-          len = 0;
-          *buf = '\0';
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            if (ShowChannel(sptr, chptr) &&
-                (acptr == sptr || !IsZombie(chan)))
-            {
-              if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
-              {
-               send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS,
-                          "%s :%s", name, buf);
-                *buf = '\0';
-                len = 0;
-              }
-              if (IsDeaf(acptr))
-                *(buf + len++) = '-';
-              if (is_chan_op(acptr, chptr))
-                *(buf + len++) = '@';
-              else if (has_voice(acptr, chptr))
-                *(buf + len++) = '+';
-              else if (IsZombie(chan))
-                *(buf + len++) = '!';
-              if (len)
-                *(buf + len) = '\0';
-              strcpy(buf + len, chptr->chname);
-              len += strlen(chptr->chname);
-              strcat(buf + len, " ");
-              len++;
-            }
-          }
-          if (buf[0] != '\0')
-           send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
-        }
-
-       send_reply(sptr, RPL_WHOISSERVER, name, a2cptr->name, a2cptr->info);
-
-        if (user)
-        {
-          if (user->away)
-           send_reply(sptr, RPL_AWAY, name, user->away);
-
-          if (IsAnOper(acptr))
-           send_reply(sptr, RPL_WHOISOPERATOR, name);
-
-          if (MyConnect(acptr))
-           send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
-                      acptr->firsttime);
-        }
-        if (found == 2 || total++ >= MAX_WHOIS_LINES)
-          break;
-      }
-    }
-    else
-    {
+    if (!wilds) {
+      struct Client *acptr = 0;
       /* No wildcards */
-      if ((acptr = FindUser(nick)))
-      {
-        found = 2;              /* Make sure we exit the loop after passing it once */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-        a2cptr = user->server;
-       send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
-                  acptr->info);
-        goto exact_match;
+      acptr = FindUser(nick);
+      if (acptr && !IsServer(acptr)) {
+        do_whois(sptr,acptr);
+        found = 1;
       }
     }
+    else /* wilds */
+       found=do_wilds(sptr,nick,total);
+
     if (!found)
       send_reply(sptr, ERR_NOSUCHNICK, nick);
+    total+=found;
+    if (total >= MAX_WHOIS_LINES) {
+      send_reply(sptr, ERR_QUERYTOOLONG, parv[1]);
+      break;
+    }
     if (p)
       p[-1] = ',';
-    if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
-      break;
-  }
+  } /* of tokenised parm[1] */
   send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
 
   return 0;
 }
 
-  
-
-#if 0
 /*
- * m_whois
+ * ms_whois - server message handler
  *
  * parv[0] = sender prefix
  * parv[1] = nickname masklist
  *
  * or
  *
- * parv[1] = target server
+ * parv[1] = target server, or a nickname representing a server to target.
  * parv[2] = nickname masklist
  */
-int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
+int ms_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  struct User*    user;
-  struct Client*  acptr;
-  struct Client*  a2cptr;
-  struct Channel* chptr;
   char*           nick;
   char*           tmp;
-  char*           name;
   char*           p = 0;
-  int             found;
-  int             len;
-  int             mlen;
-  int             total;
-  static char     buf[512];
+  int             found = 0;
+  int            total = 0;
 
   if (parc < 2)
   {
-    sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); /* XXX DEAD */
+    send_reply(sptr, ERR_NONICKNAMEGIVEN);
     return 0;
   }
 
@@ -774,181 +421,49 @@ int m_whois(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   {
     struct Client *acptr;
     /* For convenience: Accept a nickname as first parameter, by replacing
-       it with the correct servername - as is needed by hunt_server() */
-    if (MyUser(sptr) && (acptr = FindUser(parv[1])))
+     * it with the correct servername - as is needed by hunt_server().
+     * This is the secret behind the /whois nick nick trick.
+     */
+    acptr = FindUser(parv[1]);
+    if (acptr)
       parv[1] = acptr->user->server->name;
-    if (hunt_server(0, cptr, sptr, "%s%s " TOK_WHOIS " %s :%s", 1, parc, parv) != /* XXX DEAD */
+    if (hunt_server_cmd(sptr, CMD_WHOIS, cptr, 0, "%C :%s", 1, parc, parv) !=
         HUNTED_ISME)
       return 0;
     parv[1] = parv[2];
   }
 
   total = 0;
+  
   for (tmp = parv[1]; (nick = ircd_strtok(&p, tmp, ",")); tmp = 0)
   {
-    int invis, showperson, member, wilds;
+    struct Client *acptr = 0;
 
     found = 0;
+    
     collapse(nick);
-    wilds = (strchr(nick, '?') || strchr(nick, '*'));
-    /* Do a hash lookup if the nick does not contain wilds */
-    if (wilds)
-    {
-      /*
-       * We're no longer allowing remote users to generate requests with wildcards.
-       */
-      if (!MyConnect(sptr))
-        continue;
-      for (acptr = GlobalClientList; (acptr = next_client(acptr, nick));
-          acptr = acptr->next)
-      {
-        if (!IsRegistered(acptr) || IsServer(acptr))
-          continue;
-        /*
-         * I'm always last :-) and acptr->next == NULL!!
-         */
-        if (IsMe(acptr))
-          break;
-        /*
-         * 'Rules' established for sending a WHOIS reply:
-         *
-         * - if wildcards are being used dont send a reply if
-         *   the querier isnt any common channels and the
-         *   client in question is invisible and wildcards are
-         *   in use (allow exact matches only);
-         *
-         * - only send replies about common or public channels
-         *   the target user(s) are on;
-         */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
+    
 
-        invis = acptr != sptr && IsInvisible(acptr);
-        member = (user && user->channel) ? 1 : 0;
-        showperson = (wilds && !invis && !member) || !wilds;
-        if (user) {
-          struct Membership* chan;
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            member = find_channel_member(sptr, chptr) ? 1 : 0;
-            if (invis && !member)
-              continue;
-            if (IsZombie(chan))
-              continue;
-            if (member || (!invis && PubChannel(chptr)))
-            {
-              showperson = 1;
-              break;
-            }
-            if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
-              showperson = 1;
-          }
-        }
-        if (!showperson)
-          continue;
-
-        if (user)
-        {
-          a2cptr = user->server;
-          sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
-              parv[0], name, user->username, user->host, acptr->info);
-        }
-        else
-        {
-          a2cptr = &me;
-          sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
-              parv[0], name, "<unknown>", "<unknown>", "<unknown>");
-        }
-
-        found = 1;
-
-exact_match:
-        if (user && !IsChannelService(acptr))
-        {
-          struct Membership* chan;
-          mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
-          len = 0;
-          *buf = '\0';
-          for (chan = user->channel; chan; chan = chan->next_channel)
-          {
-            chptr = chan->channel;
-            if (ShowChannel(sptr, chptr) &&
-                (acptr == sptr || !IsZombie(chan)))
-            {
-              if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
-              {
-                sendto_one(sptr, ":%s %d %s %s :%s", /* XXX DEAD */
-                    me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
-                *buf = '\0';
-                len = 0;
-              }
-              if (IsDeaf(acptr))
-                *(buf + len++) = '-';
-              if (is_chan_op(acptr, chptr))
-                *(buf + len++) = '@';
-              else if (has_voice(acptr, chptr))
-                *(buf + len++) = '+';
-              else if (IsZombie(chan))
-                *(buf + len++) = '!';
-              if (len)
-                *(buf + len) = '\0';
-              strcpy(buf + len, chptr->chname);
-              len += strlen(chptr->chname);
-              strcat(buf + len, " ");
-              len++;
-            }
-          }
-          if (buf[0] != '\0')
-            sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS), /* XXX DEAD */
-                me.name, parv[0], name, buf);
-        }
-
-        sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, /* XXX DEAD */
-            parv[0], name, a2cptr->name, a2cptr->info);
-
-        if (user)
-        {
-          if (user->away)
-            sendto_one(sptr, rpl_str(RPL_AWAY), me.name, /* XXX DEAD */
-                parv[0], name, user->away);
-
-          if (IsAnOper(acptr))
-            sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR), /* XXX DEAD */
-                me.name, parv[0], name);
-
-          if (MyConnect(acptr))
-            sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name, /* XXX DEAD */
-                parv[0], name, CurrentTime - user->last, acptr->firsttime);
-        }
-        if (found == 2 || total++ >= MAX_WHOIS_LINES)
-          break;
-      }
-    }
-    else
-    {
-      /* No wildcards */
-      if ((acptr = FindUser(nick)))
-      {
-        found = 2;              /* Make sure we exit the loop after passing it once */
-        user = acptr->user;
-        name = (!*acptr->name) ? "?" : acptr->name;
-        a2cptr = user->server;
-        sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name, /* XXX DEAD */
-            parv[0], name, user->username, user->host, acptr->info);
-        goto exact_match;
-      }
+    acptr = FindUser(nick);
+    if (acptr && !IsServer(acptr)) {
+      found++;
+      do_whois(sptr,acptr);
     }
+
     if (!found)
-      sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); /* XXX DEAD */
+      send_reply(sptr, ERR_NOSUCHNICK, nick);
+      
+    total+=found;
+      
+    if (total >= MAX_WHOIS_LINES) {
+      send_reply(sptr, ERR_QUERYTOOLONG, parv[1]);
+      break;
+    }
+      
     if (p)
       p[-1] = ',';
-    if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
-      break;
-  }
-  sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]); /* XXX DEAD */
+  } /* of tokenised parm[1] */
+  send_reply(sptr, RPL_ENDOFWHOIS, parv[1]);
 
   return 0;
-}
-#endif /* 0 */
-
+}
\ No newline at end of file
index 15d53b812c28ecce38b68b0cf346a95cf1ebc518..5e39f22b5da549ad07ace730cfb798239ccaf3aa 100644 (file)
@@ -1417,6 +1417,8 @@ int connect_server(struct ConfItem* aconf, struct Client* by,
   LocalClientArray[cptr->fd] = cptr;
 
   Count_newunknown(UserStats);
+  ip_registry_add_local(aconf->ipnum.s_addr);
+
   add_client_to_list(cptr);
   hAddClient(cptr);
   nextping = CurrentTime;