This commit was generated by cvs2svn to compensate for changes in r2,
[ircu2.10.12-pk.git] / ircd / opercmds.c
diff --git a/ircd/opercmds.c b/ircd/opercmds.c
new file mode 100644 (file)
index 0000000..53098fd
--- /dev/null
@@ -0,0 +1,1843 @@
+/*
+ * IRC - Internet Relay Chat, ircd/opercmds.c (formerly ircd/s_serv.c)
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ *                    University of Oulu, Computing Center
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * 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 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "sys.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "opercmds.h"
+#include "struct.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "match.h"
+#include "s_misc.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_user.h"
+#include "common.h"
+#include "msg.h"
+#include "sprintf_irc.h"
+#include "userload.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "crule.h"
+#include "version.h"
+#include "support.h"
+#include "s_serv.h"
+#include "hash.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ *  m_squit
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = server name
+ *    parv[2] = timestamp
+ *    parv[parc-1] = comment
+ */
+int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aConfItem *aconf;
+  char *server;
+  Reg2 aClient *acptr;
+  char *comment = (parc > ((!IsServer(cptr)) ? 2 : 3) &&
+      !BadPtr(parv[parc - 1])) ? parv[parc - 1] : cptr->name;
+
+  if (!IsPrivileged(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  if (parc > (IsServer(cptr) ? 2 : 1))
+  {
+    server = parv[1];
+    /*
+     * To accomodate host masking, a squit for a masked server
+     * name is expanded if the incoming mask is the same as
+     * the server name for that link to the name of link.
+     */
+    if ((*server == '*') && IsServer(cptr) && (aconf = cptr->serv->nline) &&
+       !strCasediff(server, my_name_for_link(me.name, aconf)))
+    {
+      server = cptr->name;
+      acptr = cptr;
+    }
+    else
+    {
+      /*
+       * The following allows wild cards in SQUIT. Only usefull
+       * when the command is issued by an oper.
+       */
+      for (acptr = client; (acptr = next_client(acptr, server));
+         acptr = acptr->next)
+       if (IsServer(acptr) || IsMe(acptr))
+         break;
+
+      if (acptr)
+      {
+       if (IsMe(acptr))
+       {
+         if (IsServer(cptr))
+         {
+           acptr = cptr;
+           server = cptr->sockhost;
+         }
+         else
+           acptr = NULL;
+       }
+       else
+       {
+         /*
+          * Look for a matching server that is closer,
+          * that way we won't accidently squit two close
+          * servers like davis.* and davis-r.* when typing
+          * /SQUIT davis*
+          */
+         aClient *acptr2;
+         for (acptr2 = acptr->serv->up; acptr2 != &me;
+             acptr2 = acptr2->serv->up)
+           if (!match(server, acptr2->name))
+             acptr = acptr2;
+       }
+      }
+    }
+    /* If atoi(parv[2]) == 0 we must indeed squit !
+     * It wil be our neighbour.
+     */
+    if (acptr && IsServer(cptr) &&
+       atoi(parv[2]) && atoi(parv[2]) != acptr->serv->timestamp)
+    {
+      Debug((DEBUG_NOTICE, "Ignoring SQUIT with wrong timestamp"));
+      return 0;
+    }
+  }
+  else
+  {
+    sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SQUIT");
+    if (IsServer(cptr))
+    {
+      /*
+       * This is actually protocol error. But, well, closing
+       * the link is very proper answer to that...
+       */
+      server = cptr->sockhost;
+      acptr = cptr;
+    }
+    else
+      return 0;
+  }
+  if (!acptr)
+  {
+    if (IsUser(sptr))
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], server);
+    return 0;
+  }
+  if (IsLocOp(sptr) && !MyConnect(acptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  return exit_client(cptr, acptr, sptr, comment);
+}
+
+/*
+ * m_stats/stats_conf
+ *
+ * Report N/C-configuration lines from this server. This could
+ * report other configuration lines too, but converting the
+ * status back to "char" is a bit akward--not worth the code
+ * it needs...
+ *
+ * Note: The info is reported in the order the server uses
+ *       it--not reversed as in ircd.conf!
+ */
+
+static unsigned int report_array[17][3] = {
+  {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
+  {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
+  {CONF_CLIENT, RPL_STATSILINE, 'I'},
+  {CONF_KILL, RPL_STATSKLINE, 'K'},
+  {CONF_IPKILL, RPL_STATSKLINE, 'k'},
+  {CONF_LEAF, RPL_STATSLLINE, 'L'},
+  {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
+  {CONF_HUB, RPL_STATSHLINE, 'H'},
+  {CONF_LOCOP, RPL_STATSOLINE, 'o'},
+  {CONF_CRULEALL, RPL_STATSDLINE, 'D'},
+  {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
+  {CONF_UWORLD, RPL_STATSULINE, 'U'},
+  {CONF_TLINES, RPL_STATSTLINE, 'T'},
+  {CONF_LISTEN_PORT, RPL_STATSPLINE, 'P'},
+  {0, 0}
+};
+
+static void report_configured_links(aClient *sptr, int mask)
+{
+  static char null[] = "<NULL>";
+  aConfItem *tmp;
+  unsigned int *p;
+  unsigned short int port;
+  char c, *host, *pass, *name;
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+    if ((tmp->status & mask))
+    {
+      for (p = &report_array[0][0]; *p; p += 3)
+       if (*p == tmp->status)
+         break;
+      if (!*p)
+       continue;
+      c = (char)*(p + 2);
+      host = BadPtr(tmp->host) ? null : tmp->host;
+      pass = BadPtr(tmp->passwd) ? null : tmp->passwd;
+      name = BadPtr(tmp->name) ? null : tmp->name;
+      port = tmp->port;
+      /*
+       * On K line the passwd contents can be
+       * displayed on STATS reply.    -Vesa
+       */
+      /* Special-case 'k' or 'K' lines as appropriate... -Kev */
+      if ((tmp->status & CONF_KLINE))
+       sendto_one(sptr, rpl_str(p[1]), me.name,
+           sptr->name, c, host, pass, name, port, get_conf_class(tmp));
+      /* connect rules are classless */
+      else if ((tmp->status & CONF_CRULE))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name);
+      else if ((tmp->status & CONF_TLINES))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, pass);
+      else if ((tmp->status & CONF_LISTEN_PORT))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, port,
+           tmp->clients, tmp->status);
+      else if ((tmp->status & CONF_UWORLD))
+       sendto_one(sptr, rpl_str(p[1]),
+           me.name, sptr->name, c, host, pass, name, port,
+           get_conf_class(tmp));
+      else
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name,
+           port, get_conf_class(tmp));
+    }
+  return;
+}
+
+/*
+ * m_stats
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = statistics selector (defaults to Message frequency)
+ *    parv[2] = target server (current server defaulted, if omitted)
+ * And 'stats l' and 'stats' L:
+ *    parv[3] = server mask ("*" defaulted, if omitted)
+ * Or for stats p,P:
+ *    parv[3] = port mask (returns p-lines when its port is matched by this)
+ * Or for stats k,K,i and I:
+ *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
+ *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
+ *              (defaults to old reply if ommitted, when local or Oper)
+ *              A remote mask (something containing wildcards) is only
+ *              allowed for IRC Operators.
+ * Or for stats M:
+ *    parv[3] = time param
+ *    parv[4] = time param 
+ *    (see report_memleak_stats() in runmalloc.c for details)
+ *
+ * This function is getting really ugly. -Ghostwolf
+ */
+int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
+      "RcveM RcveKBytes :Open since";
+  static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
+  aMessage *mptr;
+  aClient *acptr;
+  aGline *agline, *a2gline;
+  aConfItem *aconf;
+  char stat = parc > 1 ? parv[1][0] : '\0';
+  Reg1 int i;
+
+/* m_stats is so obnoxiously full of special cases that the different
+ * hunt_server() possiblites were becoming very messy. It now uses a
+ * switch() so as to be easier to read and update as params change. 
+ * -Ghostwolf 
+ */
+  switch (stat)
+  {
+      /* open to all, standard # of params */
+    case 'U':
+    case 'u':
+    {
+      if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+
+      /* open to all, varying # of params */
+    case 'k':
+    case 'K':
+    case 'i':
+    case 'I':
+    case 'p':
+    case 'P':
+    {
+      if (parc > 3)
+      {
+       if (hunt_server(0, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      else
+      {
+       if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      break;
+    }
+
+      /* oper only, varying # of params */
+    case 'l':
+    case 'L':
+    case 'M':
+    {
+      if (parc == 4)
+      {
+       if (hunt_server(1, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      else if (parc > 4)
+      {
+       if (hunt_server(1, cptr, sptr, ":%s STATS %s %s %s :%s", 2, parc,
+           parv) != HUNTED_ISME)
+         return 0;
+      }
+      else if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+
+      /* oper only, standard # of params */
+    default:
+    {
+      if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+  }
+
+  switch (stat)
+  {
+    case 'L':
+    case 'l':
+    {
+      int doall = 0, wilds = 0;
+      char *name = "*";
+      if (parc > 3 && *parv[3])
+      {
+       char *p;
+       name = parv[3];
+       wilds = (*name == '*' || *name == '?');
+       for (p = name + 1; *p; ++p)
+         if ((*p == '*' || *p == '?') && p[-1] != '\\')
+         {
+           wilds = 1;
+           break;
+         }
+      }
+      else
+       doall = 1;
+      /*
+       * Send info about connections which match, or all if the
+       * mask matches me.name.  Only restrictions are on those who
+       * are invisible not being visible to 'foreigners' who use
+       * a wild card based search to list it.
+       */
+      sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
+      for (i = 0; i <= highest_fd; i++)
+      {
+       if (!(acptr = loc_clients[i]))
+         continue;
+       /* Don't return clients when this is a request for `all' */
+       if (doall && IsUser(acptr))
+         continue;
+       /* Don't show invisible people to unauthorized people when using
+        * wildcards  -- Is this still needed now /stats is oper only ? */
+       if (IsInvisible(acptr) && (doall || wilds) &&
+           !(MyConnect(sptr) && IsOper(sptr)) &&
+           !IsAnOper(acptr) && (acptr != sptr))
+         continue;
+       /* Only show the ones that match the given mask - if any */
+       if (!doall && wilds && match(name, acptr->name))
+         continue;
+       /* Skip all that do not match the specific query */
+       if (!(doall || wilds) && strCasediff(name, acptr->name))
+         continue;
+       sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
+           (isUpper(stat)) ?
+           get_client_name(acptr, TRUE) : get_client_name(acptr, FALSE),
+           (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
+           (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
+           time(NULL) - acptr->firsttime);
+      }
+      break;
+    }
+    case 'C':
+    case 'c':
+      report_configured_links(sptr,
+         CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER);
+      break;
+    case 'G':
+    case 'g':
+      /* send glines */
+      for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+      {
+       if (agline->expire <= TStime())
+       {                       /* handle expired glines */
+         free_gline(agline, a2gline);
+         agline = a2gline ? a2gline : gline;   /* make sure to splice
+                                                  list together */
+         if (!agline)
+           break;              /* last gline; break out of loop */
+         continue;             /* continue! */
+       }
+       sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
+           sptr->name, 'G', agline->name, agline->host,
+           agline->expire, agline->reason);
+       a2gline = agline;
+      }
+      break;
+    case 'H':
+    case 'h':
+      report_configured_links(sptr, CONF_HUB | CONF_LEAF);
+      break;
+    case 'I':
+    case 'i':
+    case 'K':
+    case 'k':                  /* display CONF_IPKILL as well
+                                  as CONF_KILL -Kev */
+    {
+      int wilds, count;
+      char *user, *host, *p;
+      int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
+      if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+      {
+       report_configured_links(sptr, conf_status);
+       break;
+      }
+      if (parc < 4 || *parv[3] == '\0')
+      {
+       sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+           (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
+       return 0;
+      }
+      wilds = 0;
+      for (p = parv[3]; *p; p++)
+      {
+       if (*p == '\\')
+       {
+         if (!*++p)
+           break;
+         continue;
+       }
+       if (*p == '?' || *p == '*')
+       {
+         wilds = 1;
+         break;
+       }
+      }
+      if (!(MyConnect(sptr) || IsOper(sptr)))
+      {
+       wilds = 0;
+       count = 3;
+      }
+      else
+       count = 1000;
+
+      if (conf_status == CONF_CLIENT)
+      {
+       user = NULL;            /* Not used, but to avoid compiler warning. */
+
+       host = parv[3];
+      }
+      else
+      {
+       if ((host = strchr(parv[3], '@')))
+       {
+         user = parv[3];
+         *host++ = 0;;
+       }
+       else
+       {
+         user = NULL;
+         host = parv[3];
+       }
+      }
+      for (aconf = conf; aconf; aconf = aconf->next)
+      {
+       if ((aconf->status & conf_status))
+       {
+         if (conf_status == CONF_KLINE)
+         {
+           if ((!wilds && ((user || aconf->host[1]) &&
+               !match(aconf->host, host) &&
+               (!user || !match(aconf->name, user)))) ||
+               (wilds && !mmatch(host, aconf->host) &&
+               (!user || !mmatch(user, aconf->name))))
+           {
+             sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
+                 sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
+                 aconf->port, get_conf_class(aconf));
+             if (--count == 0)
+               break;
+           }
+         }
+         else if (conf_status == CONF_CLIENT)
+         {
+           if ((!wilds && (!match(aconf->host, host) ||
+               !match(aconf->name, host))) ||
+               (wilds && (!mmatch(host, aconf->host) ||
+               !mmatch(host, aconf->name))))
+           {
+             sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
+                 sptr->name, 'I', aconf->host, aconf->name,
+                 aconf->port, get_conf_class(aconf));
+             if (--count == 0)
+               break;
+           }
+         }
+       }
+      }
+      break;
+    }
+    case 'M':
+#ifdef MEMSIZESTATS
+      sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
+         me.name, parv[0], get_mem_size(), get_alloc_cnt());
+#endif
+#ifdef MEMLEAKSTATS
+      report_memleak_stats(sptr, parc, parv);
+#endif
+#if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
+      sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
+         "is not enabled on this server", me.name, parv[0]);
+#endif
+      break;
+    case 'm':
+      for (mptr = msgtab; mptr->cmd; mptr++)
+       if (mptr->count)
+         sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
+             me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
+      break;
+    case 'o':
+    case 'O':
+      report_configured_links(sptr, CONF_OPS);
+      break;
+    case 'p':
+    case 'P':
+    {
+      int count = 100;
+      char port[6];
+      if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+      {
+       report_configured_links(sptr, CONF_LISTEN_PORT);
+       break;
+      }
+      if (!(MyConnect(sptr) || IsOper(sptr)))
+       count = 3;
+      for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status == CONF_LISTEN_PORT)
+       {
+         if (parc >= 4 && *parv[3] != '\0')
+         {
+           sprintf_irc(port, "%u", aconf->port);
+           if (match(parv[3], port))
+             continue;
+         }
+         sendto_one(sptr, rpl_str(RPL_STATSPLINE), me.name, sptr->name, 'P',
+             aconf->port, aconf->clients, aconf->status);
+         if (--count == 0)
+           break;
+       }
+      break;
+    }
+    case 'R':
+    case 'r':
+#ifdef DEBUGMODE
+      send_usage(sptr, parv[0]);
+#endif
+      break;
+    case 'D':
+      report_configured_links(sptr, CONF_CRULEALL);
+      break;
+    case 'd':
+      report_configured_links(sptr, CONF_CRULE);
+      break;
+    case 't':
+      tstats(sptr, parv[0]);
+      break;
+    case 'T':
+      report_configured_links(sptr, CONF_TLINES);
+      break;
+    case 'U':
+      report_configured_links(sptr, CONF_UWORLD);
+      break;
+    case 'u':
+    {
+      register time_t nowr;
+
+      nowr = now - me.since;
+      sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
+         nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
+      sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
+         max_connection_count, max_client_count);
+      break;
+    }
+    case 'W':
+    case 'w':
+      calc_load(sptr);
+      break;
+    case 'X':
+    case 'x':
+#ifdef DEBUGMODE
+      send_listinfo(sptr, parv[0]);
+#endif
+      break;
+    case 'Y':
+    case 'y':
+      report_classes(sptr);
+      break;
+    case 'Z':
+    case 'z':
+      count_memory(sptr, parv[0]);
+      break;
+    default:
+      stat = '*';
+      break;
+  }
+  sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
+  return 0;
+}
+
+/*
+ *  m_connect                           - Added by Jto 11 Feb 1989
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = servername
+ *    parv[2] = port number
+ *    parv[3] = remote server
+ */
+int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  int retval;
+  unsigned short int port, tmpport;
+  aConfItem *aconf, *cconf;
+  aClient *acptr;
+
+  if (!IsPrivileged(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return -1;
+  }
+
+  if (IsLocOp(sptr) && parc > 3)       /* Only allow LocOps to make */
+    return 0;                  /* local CONNECTS --SRB      */
+
+  if (parc > 3 && MyUser(sptr))
+  {
+    aClient *acptr2, *acptr3;
+    if (!(acptr3 = find_match_server(parv[3])))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[3]);
+      return 0;
+    }
+
+    /* Look for closest matching server */
+    for (acptr2 = acptr3; acptr2 != &me; acptr2 = acptr2->serv->up)
+      if (!match(parv[3], acptr2->name))
+       acptr3 = acptr2;
+
+    parv[3] = acptr3->name;
+  }
+
+  if (hunt_server(1, cptr, sptr, ":%s CONNECT %s %s :%s", 3, parc, parv) !=
+      HUNTED_ISME)
+    return 0;
+
+  if (parc < 2 || *parv[1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "CONNECT");
+    return -1;
+  }
+
+  if ((acptr = FindServer(parv[1])))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+         me.name, parv[0], parv[1], "already exists from", acptr->from->name);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :Connect: Server %s %s %s.",
+         NumServ(&me), NumNick(sptr), parv[1], "already exists from",
+         acptr->from->name);
+    return 0;
+  }
+
+  for (aconf = conf; aconf; aconf = aconf->next)
+    if (aconf->status == CONF_CONNECT_SERVER &&
+       match(parv[1], aconf->name) == 0)
+      break;
+  /* Checked first servernames, then try hostnames. */
+  if (!aconf)
+    for (aconf = conf; aconf; aconf = aconf->next)
+      if (aconf->status == CONF_CONNECT_SERVER &&
+         (match(parv[1], aconf->host) == 0 ||
+         match(parv[1], strchr(aconf->host, '@') + 1) == 0))
+       break;
+
+  if (!aconf)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr,
+         ":%s NOTICE %s :Connect: Host %s not listed in ircd.conf",
+         me.name, parv[0], parv[1]);
+    else
+      sendto_one(sptr,
+         "%s NOTICE %s%s :Connect: Host %s not listed in ircd.conf",
+         NumServ(&me), NumNick(sptr), parv[1]);
+    return 0;
+  }
+  /*
+   *  Get port number from user, if given. If not specified,
+   *  use the default from configuration structure. If missing
+   *  from there, then use the precompiled default.
+   */
+  tmpport = port = aconf->port;
+  if (parc > 2 && !BadPtr(parv[2]))
+  {
+    if ((port = atoi(parv[2])) == 0)
+    {
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :Connect: Invalid port number", me.name, parv[0]);
+      else
+       sendto_one(sptr, "%s NOTICE %s%s :Connect: Invalid port number",
+           NumServ(&me), NumNick(sptr));
+      return 0;
+    }
+  }
+  else if (port == 0 && (port = PORTNUM) == 0)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
+         me.name, parv[0]);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :Connect: missing port number",
+         NumServ(&me), NumNick(sptr));
+    return 0;
+  }
+
+  /*
+   * Evaluate connection rules...  If no rules found, allow the
+   * connect.   Otherwise stop with the first true rule (ie: rules
+   * are ored together.  Oper connects are effected only by D
+   * lines (CRULEALL) not d lines (CRULEAUTO).
+   */
+  for (cconf = conf; cconf; cconf = cconf->next)
+    if ((cconf->status == CONF_CRULEALL) &&
+       (match(cconf->host, aconf->name) == 0))
+      if (crule_eval(cconf->passwd))
+      {
+       if (MyUser(sptr) || Protocol(cptr) < 10)
+         sendto_one(sptr, ":%s NOTICE %s :Connect: Disallowed by rule: %s",
+             me.name, parv[0], cconf->name);
+       else
+         sendto_one(sptr, "%s NOTICE %s%s :Connect: Disallowed by rule: %s",
+             NumServ(&me), NumNick(sptr), cconf->name);
+       return 0;
+      }
+
+  /*
+   * Notify all operators about remote connect requests
+   */
+  if (!IsAnOper(cptr))
+  {
+    sendto_ops_butone(NULL, &me, ":%s WALLOPS :Remote CONNECT %s %s from %s",
+       me.name, parv[1], parv[2] ? parv[2] : "", get_client_name(sptr, FALSE));
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
+    syslog(LOG_DEBUG,
+       "CONNECT From %s : %s %d", parv[0], parv[1], parv[2] ? parv[2] : "");
+#endif
+  }
+  aconf->port = port;
+  switch (retval = connect_server(aconf, sptr, NULL))
+  {
+    case 0:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :*** Connecting to %s[%s].",
+           me.name, parv[0], aconf->host, aconf->name);
+      else
+       sendto_one(sptr,
+           "%s NOTICE %s%s :*** Connecting to %s[%s].",
+           NumServ(&me), NumNick(sptr), aconf->host, aconf->name);
+      break;
+    case -1:
+      /* Comments already sent */
+      break;
+    case -2:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+           me.name, parv[0], aconf->host);
+      else
+       sendto_one(sptr, "%s NOTICE %s%s :*** Host %s is unknown.",
+           NumServ(&me), NumNick(sptr), aconf->host);
+      break;
+    default:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :*** Connection to %s failed: %s",
+           me.name, parv[0], aconf->host, strerror(retval));
+      else
+       sendto_one(sptr,
+           "%s NOTICE %s%s :*** Connection to %s failed: %s",
+           NumServ(&me), NumNick(sptr), aconf->host, strerror(retval));
+  }
+  aconf->port = tmpport;
+  return 0;
+}
+
+/*
+ * m_wallops
+ *
+ * Writes to all +w users currently online
+ *
+ * parv[0] = sender prefix
+ * parv[1] = message text
+ */
+int m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  char *message;
+
+  message = parc > 1 ? parv[1] : NULL;
+
+  if (BadPtr(message))
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "WALLOPS");
+    return 0;
+  }
+
+  if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+  sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
+      ":%s WALLOPS :%s", parv[0], message);
+  return 0;
+}
+
+/*
+ * m_time
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_time(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  if (hunt_server(0, cptr, sptr, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME)
+    sendto_one(sptr, rpl_str(RPL_TIME), me.name,
+       parv[0], me.name, TStime(), TSoffset, date((long)0));
+  return 0;
+}
+
+/*
+ * m_settime
+ *
+ * parv[0] = sender prefix
+ * parv[1] = new time
+ * parv[2] = servername (Only used when sptr is an Oper).
+ */
+int m_settime(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  time_t t;
+  long int dt;
+  static char tbuf[11];
+  Dlink *lp;
+
+  if (!IsPrivileged(sptr))
+    return 0;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SETTIME");
+    return 0;
+  }
+
+  if (parc == 2 && MyUser(sptr))
+    parv[parc++] = me.name;
+
+  t = atoi(parv[1]);
+  dt = TStime() - t;
+
+  if (t < 779557906 || dt < -9000000)
+  {
+    sendto_one(sptr, ":%s NOTICE %s :SETTIME: Bad value", me.name, parv[0]);
+    return 0;
+  }
+
+  if (IsServer(sptr))          /* send to unlagged servers */
+  {
+#ifdef RELIABLE_CLOCK
+    sprintf_irc(tbuf, TIME_T_FMT, TStime());
+    parv[1] = tbuf;
+#endif
+    for (lp = me.serv->down; lp; lp = lp->next)
+      if (cptr != lp->value.cptr && DBufLength(&lp->value.cptr->sendQ) < 8000)
+       sendto_one(lp->value.cptr, ":%s SETTIME %s", parv[0], parv[1]);
+  }
+  else
+  {
+    sprintf_irc(tbuf, TIME_T_FMT, TStime());
+    parv[1] = tbuf;
+    if (hunt_server(1, cptr, sptr, ":%s SETTIME %s %s", 2, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+  }
+
+#ifdef RELIABLE_CLOCK
+  if ((dt > 600) || (dt < -600))
+    sendto_serv_butone((aClient *)NULL,
+       ":%s WALLOPS :Bad SETTIME from %s: " TIME_T_FMT, me.name, sptr->name,
+       t);
+  if (IsUser(sptr))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :clock is not set %ld seconds %s : "
+         "RELIABLE_CLOCK is defined", me.name, parv[0],
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :clock is not set %ld seconds %s : "
+         "RELIABLE_CLOCK is defined", NumServ(&me), NumNick(sptr),
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+  }
+#else
+  sendto_ops("SETTIME from %s, clock is set %ld seconds %s",
+      get_client_name(sptr, FALSE), (dt < 0) ? -dt : dt,
+      (dt < 0) ? "forwards" : "backwards");
+  TSoffset -= dt;
+  if (IsUser(sptr))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :clock is set %ld seconds %s", me.name,
+         parv[0], (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :clock is set %ld seconds %s",
+         NumServ(&me), NumNick(sptr),
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+  }
+#endif
+  return 0;
+}
+
+static char *militime(char *sec, char *usec)
+{
+  struct timeval tv;
+  static char timebuf[18];
+
+  gettimeofday(&tv, NULL);
+  if (sec && usec)
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+    sprintf(timebuf, "%ld",
+       (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#else
+    sprintf_irc(timebuf, "%d",
+       (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#endif
+  else
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+    sprintf(timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
+#else
+    sprintf_irc(timebuf, "%d %d", tv.tv_sec, tv.tv_usec);
+#endif
+  return timebuf;
+}
+
+/*
+ * m_rping  -- by Run
+ *
+ *    parv[0] = sender (sptr->name thus)
+ * if sender is a person: (traveling towards start server)
+ *    parv[1] = pinged server[mask]
+ *    parv[2] = start server (current target)
+ *    parv[3] = optional remark
+ * if sender is a server: (traveling towards pinged server)
+ *    parv[1] = pinged server (current target)
+ *    parv[2] = original sender (person)
+ *    parv[3] = start time in s
+ *    parv[4] = start time in us
+ *    parv[5] = the optional remark
+ */
+int m_rping(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+
+  if (!IsPrivileged(sptr))
+    return 0;
+
+  if (parc < (IsAnOper(sptr) ? (MyConnect(sptr) ? 2 : 3) : 6))
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RPING");
+    return 0;
+  }
+  if (MyUser(sptr))
+  {
+    if (parc == 2)
+      parv[parc++] = me.name;
+    else if (!(acptr = find_match_server(parv[2])))
+    {
+      parv[3] = parv[2];
+      parv[2] = me.name;
+      parc++;
+    }
+    else
+      parv[2] = acptr->name;
+    if (parc == 3)
+      parv[parc++] = "<No client start time>";
+  }
+
+  if (IsAnOper(sptr))
+  {
+    if (hunt_server(1, cptr, sptr, ":%s RPING %s %s :%s", 2, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+    if (!(acptr = find_match_server(parv[1])) || !IsServer(acptr))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+      return 0;
+    }
+    if (Protocol(acptr->from) < 10)
+      sendto_one(acptr, ":%s RPING %s %s %s :%s",
+         me.name, acptr->name, sptr->name, militime(NULL, NULL), parv[3]);
+    else
+      sendto_one(acptr, ":%s RPING %s %s %s :%s",
+         me.name, NumServ(acptr), sptr->name, militime(NULL, NULL), parv[3]);
+  }
+  else
+  {
+    if (hunt_server(1, cptr, sptr, ":%s RPING %s %s %s %s :%s", 1, parc, parv)
+       != HUNTED_ISME)
+      return 0;
+    sendto_one(cptr, ":%s RPONG %s %s %s %s :%s", me.name, parv[0],
+       parv[2], parv[3], parv[4], parv[5]);
+  }
+  return 0;
+}
+
+/*
+ * m_rpong  -- by Run too :)
+ *
+ * parv[0] = sender prefix
+ * parv[1] = from pinged server: start server; from start server: sender
+ * parv[2] = from pinged server: sender; from start server: pinged server
+ * parv[3] = pingtime in ms
+ * parv[4] = client info (for instance start time)
+ */
+int m_rpong(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+
+  if (!IsServer(sptr))
+    return 0;
+
+  if (parc < 5)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RPING");
+    return 0;
+  }
+
+  if (!(acptr = FindClient(parv[1])))
+    return 0;
+
+  if (!IsMe(acptr))
+  {
+    if (IsServer(acptr) && parc > 5)
+    {
+      sendto_one(acptr, ":%s RPONG %s %s %s %s :%s",
+         parv[0], parv[1], parv[2], parv[3], parv[4], parv[5]);
+      return 0;
+    }
+  }
+  else
+  {
+    parv[1] = parv[2];
+    parv[2] = sptr->name;
+    parv[0] = me.name;
+    parv[3] = militime(parv[3], parv[4]);
+    parv[4] = parv[5];
+    if (!(acptr = FindUser(parv[1])))
+      return 0;                        /* No bouncing between servers ! */
+  }
+
+  sendto_one(acptr, ":%s RPONG %s %s %s :%s",
+      parv[0], parv[1], parv[2], parv[3], parv[4]);
+  return 0;
+}
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+/*
+ * m_rehash
+ */
+int m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#ifndef LOCOP_REHASH
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_REHASH
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+  sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], configfile);
+  sendto_ops("%s is rehashing Server config file", parv[0]);
+#ifdef USE_SYSLOG
+  syslog(LOG_INFO, "REHASH From %s\n", get_client_name(sptr, FALSE));
+#endif
+  return rehash(cptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
+}
+#endif
+
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+/*
+ * m_restart
+ */
+int m_restart(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc),
+    char *parv[])
+{
+#ifndef LOCOP_RESTART
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_RESTART
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+#ifdef USE_SYSLOG
+  syslog(LOG_WARNING, "Server RESTART by %s\n", get_client_name(sptr, FALSE));
+#endif
+  server_reboot();
+  return 0;
+}
+#endif
+
+/*
+ * m_trace
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick or servername
+ * parv[2] = 'target' servername
+ */
+int m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 int i;
+  Reg2 aClient *acptr;
+  aConfClass *cltmp;
+  char *tname;
+  int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+  int cnt = 0, wilds, dow;
+
+  if (parc < 2 || BadPtr(parv[1]))
+  {
+    /* just "TRACE" without parameters. Must be from local client */
+    parc = 1;
+    acptr = &me;
+    tname = me.name;
+    i = HUNTED_ISME;
+  }
+  else if (parc < 3 || BadPtr(parv[2]))
+  {
+    /* No target specified. Make one before propagating. */
+    parc = 2;
+    tname = parv[1];
+    if ((acptr = find_match_server(parv[1])) ||
+       ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
+    {
+      if (IsUser(acptr))
+       parv[2] = acptr->user->server->name;
+      else
+       parv[2] = acptr->name;
+      parc = 3;
+      parv[3] = NULL;
+      if ((i = hunt_server(IsServer(acptr), cptr, sptr,
+         ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+       return 0;
+    }
+    else
+      i = HUNTED_ISME;
+  }
+  else
+  {
+    /* Got "TRACE <tname> :<target>" */
+    parc = 3;
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      acptr = find_match_server(parv[2]);
+    else
+      acptr = FindNServer(parv[2]);
+    if ((i = hunt_server(0, cptr, sptr,
+       ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+      return 0;
+    tname = parv[1];
+  }
+
+  if (i == HUNTED_PASS)
+  {
+    if (!acptr)
+      acptr = next_client(client, tname);
+    else
+      acptr = acptr->from;
+    sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
+#ifndef GODMODE
+       version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
+#else /* GODMODE */
+       version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
+       (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
+#endif /* GODMODE */
+    return 0;
+  }
+
+  doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
+  wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
+  dow = wilds || doall;
+
+  /* Don't give (long) remote listings to lusers */
+  if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
+    return 0;
+
+  for (i = 0; i < MAXCONNECTIONS; i++)
+    link_s[i] = 0, link_u[i] = 0;
+
+  if (doall)
+  {
+    for (acptr = client; acptr; acptr = acptr->next)
+      if (IsUser(acptr))
+       link_u[acptr->from->fd]++;
+      else if (IsServer(acptr))
+       link_s[acptr->from->fd]++;
+  }
+
+  /* report all direct connections */
+
+  for (i = 0; i <= highest_fd; i++)
+  {
+    char *name;
+    unsigned int conClass;
+
+    if (!(acptr = loc_clients[i]))     /* Local Connection? */
+      continue;
+    if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
+       !IsAnOper(acptr) && (acptr != sptr))
+      continue;
+    if (!doall && wilds && match(tname, acptr->name))
+      continue;
+    if (!dow && strCasediff(tname, acptr->name))
+      continue;
+    name = get_client_name(acptr, FALSE);
+    conClass = get_client_class(acptr);
+
+    switch (acptr->status)
+    {
+      case STAT_CONNECTING:
+       sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_HANDSHAKE:
+       sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_ME:
+       break;
+      case STAT_UNKNOWN:
+      case STAT_UNKNOWN_USER:
+      case STAT_UNKNOWN_SERVER:
+       sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_USER:
+       /* Only opers see users if there is a wildcard
+          but anyone can see all the opers. */
+       if ((IsAnOper(sptr) && (MyUser(sptr) ||
+           !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
+       {
+         if (IsAnOper(acptr))
+           sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
+               me.name, parv[0], conClass, name, now - acptr->lasttime);
+         else
+           sendto_one(sptr, rpl_str(RPL_TRACEUSER),
+               me.name, parv[0], conClass, name, now - acptr->lasttime);
+         cnt++;
+       }
+       break;
+       /*
+        * Connection is a server
+        *
+        * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
+        *
+        * class        Class the server is in
+        * nS           Number of servers reached via this link
+        * nC           Number of clients reached via this link
+        * name         Name of the server linked
+        * ConnBy       Who established this link
+        * last         Seconds since we got something from this link
+        * age          Seconds this link has been alive
+        *
+        * Additional comments etc......        -Cym-<cym@acrux.net>
+        */
+
+      case STAT_SERVER:
+       if (acptr->serv->user)
+         sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+             me.name, parv[0], conClass, link_s[i],
+             link_u[i], name, acptr->serv->by,
+             acptr->serv->user->username,
+             acptr->serv->user->host,
+             now - acptr->lasttime, now - acptr->serv->timestamp);
+       else
+         sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+             me.name, parv[0], conClass, link_s[i],
+             link_u[i], name, *(acptr->serv->by) ?
+             acptr->serv->by : "*", "*", me.name,
+             now - acptr->lasttime, now - acptr->serv->timestamp);
+       cnt++;
+       break;
+      case STAT_LOG:
+       sendto_one(sptr, rpl_str(RPL_TRACELOG),
+           me.name, parv[0], LOGFILE, acptr->port);
+       cnt++;
+       break;
+      case STAT_PING:
+       sendto_one(sptr, rpl_str(RPL_TRACEPING), me.name,
+           parv[0], name, (acptr->acpt) ? acptr->acpt->name : "<null>");
+       break;
+      default:                 /* We actually shouldn't come here, -msa */
+       sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0], name);
+       cnt++;
+       break;
+    }
+  }
+  /*
+   * Add these lines to summarize the above which can get rather long
+   * and messy when done remotely - Avalon
+   */
+  if (!IsAnOper(sptr) || !cnt)
+  {
+    if (!cnt)
+      /* let the user have some idea that its at the end of the trace */
+      sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+         me.name, parv[0], 0, link_s[me.fd],
+         link_u[me.fd], "<No_match>", *(me.serv->by) ?
+         me.serv->by : "*", "*", me.name, 0, 0);
+    return 0;
+  }
+  for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
+    if (Links(cltmp) > 0)
+      sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
+         parv[0], ConClass(cltmp), Links(cltmp));
+  return 0;
+}
+
+/*
+ *  m_close                              - added by Darren Reed Jul 13 1992.
+ */
+int m_close(aClient *cptr, aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  Reg1 aClient *acptr;
+  Reg2 int i;
+  int closed = 0;
+
+  if (!MyOper(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  for (i = highest_fd; i; i--)
+  {
+    if (!(acptr = loc_clients[i]))
+      continue;
+    if (!IsUnknown(acptr) && !IsConnecting(acptr) && !IsHandshake(acptr))
+      continue;
+    sendto_one(sptr, rpl_str(RPL_CLOSING), me.name, parv[0],
+       get_client_name(acptr, TRUE), acptr->status);
+    exit_client(cptr, acptr, &me, "Oper Closing");
+    closed++;
+  }
+  sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
+  return 0;
+}
+
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+/*
+ * m_die
+ */
+int m_die(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  Reg1 aClient *acptr;
+  Reg2 int i;
+
+#ifndef LOCOP_DIE
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_DIE
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(acptr = loc_clients[i]))
+      continue;
+    if (IsUser(acptr))
+      sendto_one(acptr, ":%s NOTICE %s :Server Terminating. %s",
+         me.name, acptr->name, get_client_name(sptr, TRUE));
+    else if (IsServer(acptr))
+      sendto_one(acptr, ":%s ERROR :Terminated by %s",
+         me.name, get_client_name(sptr, TRUE));
+  }
+#ifdef __cplusplus
+  s_die(0);
+#else
+  s_die();
+#endif
+  return 0;
+}
+#endif
+
+static void add_gline(aClient *sptr, int ip_mask, char *host, char *comment,
+    char *user, time_t expire, int local)
+{
+  aClient *acptr;
+  aGline *agline;
+  int fd;
+
+  /* Inform ops */
+  sendto_op_mask(SNO_GLINE,
+      "%s adding %sGLINE for %s@%s, expiring at " TIME_T_FMT ": %s", sptr->name,
+      local ? "local " : "", user, host, expire, comment);
+
+#ifdef GPATH
+  write_log(GPATH,
+      "# " TIME_T_FMT " %s adding %s GLINE for %s@%s, expiring at " TIME_T_FMT
+      ": %s\n", TStime(), sptr->name, local ? "local" : "global",
+      user, host, expire, comment);
+
+  /* this can be inserted into the conf */
+  write_log(GPATH, "%c:%s:%s:%s\n", ip_mask ? 'k' : 'K', host, comment, user);
+#endif /* GPATH */
+
+  agline = make_gline(ip_mask, host, comment, user, expire);
+  if (local)
+    SetGlineIsLocal(agline);
+
+  for (fd = highest_fd; fd >= 0; --fd) /* get the users! */
+    if ((acptr = loc_clients[fd]) && !IsMe(acptr))
+    {
+
+      if (!acptr->user || strlen(acptr->sockhost) > (size_t)HOSTLEN ||
+         (acptr->user->username ? strlen(acptr->user->username) : 0) >
+         (size_t)HOSTLEN)
+       continue;               /* these tests right out of
+                                  find_kill for safety's sake */
+
+      if ((GlineIsIpMask(agline) ?
+         match(agline->host, inetntoa(acptr->ip)) :
+         match(agline->host, acptr->sockhost)) == 0 &&
+         (!acptr->user->username ||
+         match(agline->name, acptr->user->username) == 0))
+      {
+
+       /* ok, he was the one that got G-lined */
+       sendto_one(acptr, ":%s %d %s :*** %s.", me.name,
+           ERR_YOUREBANNEDCREEP, acptr->name, agline->reason);
+
+       /* let the ops know about my first kill */
+       sendto_op_mask(SNO_GLINE, "G-line active for %s",
+           get_client_name(acptr, FALSE));
+
+       /* and get rid of him */
+       if (sptr != acptr)
+         exit_client(sptr->from, acptr, &me, "G-lined");
+      }
+    }
+}
+
+/*
+ * m_gline
+ *
+ * parv[0] = Send prefix
+ *
+ * From server:
+ *
+ * parv[1] = Target: server numeric
+ * parv[2] = [+|-]<G-line mask>
+ * parv[3] = Expiration offset
+ * parv[4] = Comment
+ *
+ * From client:
+ *
+ * parv[1] = [+|-]<G-line mask>
+ * parv[2] = Expiration offset
+ * parv[3] = Comment
+ *
+ */
+int m_gline(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr = NULL;       /* Init. to avoid compiler warning. */
+
+  aGline *agline, *a2gline;
+  char *user, *host;
+  int active, ip_mask;
+  time_t expire = 0;
+
+  /* Remove expired G-lines */
+  for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+  {
+    if (agline->expire <= TStime())
+    {
+      free_gline(agline, a2gline);
+      agline = a2gline ? a2gline : gline;
+      if (!agline)
+       break;
+      continue;
+    }
+    a2gline = agline;
+  }
+
+  if (IsServer(cptr))
+  {
+    if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+    {
+      if (parc < 3 || (*parv[2] != '-' && (parc < 5 || *parv[4] == '\0')))
+      {
+       sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+           "GLINE");
+       return 0;
+      }
+
+      if (*parv[2] == '-')     /* add mode or delete mode? */
+       active = 0;
+      else
+       active = 1;
+
+      if (*parv[2] == '+' || *parv[2] == '-')
+       parv[2]++;              /* step past mode indicator */
+
+      /* forward the message appropriately */
+      if (!strCasediff(parv[1], "*"))
+       sendto_serv_butone(cptr, active ? ":%s GLINE %s +%s %s :%s" :
+           ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]);   /* global! */
+      else if ((
+#if 1
+         /*
+          * REMOVE THIS after all servers upgraded to 2.10.01 and
+          * Uworld uses a numeric too
+          */
+         (strlen(parv[1]) != 1 && !(acptr = FindClient(parv[1])))) ||
+         (strlen(parv[1]) == 1 &&
+#endif
+         !(acptr = FindNServer(parv[1]))))
+       return 0;               /* no such server/user exists; forget it */
+      else
+#if 1
+/*
+ * REMOVE THIS after all servers upgraded to 2.10.01 and
+ * Uworld uses a numeric too
+ */
+      if (IsServer(acptr) || !MyConnect(acptr))
+#endif
+      {
+       sendto_one(acptr, active ? ":%s GLINE %s +%s %s :%s" :
+           ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]);   /* single destination */
+       return 0;               /* only the intended  destination
+                                  should add this gline */
+      }
+
+      if (!(host = strchr(parv[2], '@')))
+      {                                /* convert user@host */
+       user = "*";             /* no @'s; assume username is '*' */
+       host = parv[2];
+      }
+      else
+      {
+       user = parv[2];
+       *(host++) = '\0';       /* break up string at the '@' */
+      }
+      ip_mask = check_if_ipmask(host); /* Store this boolean */
+
+      for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+      {
+       if (!strCasediff(agline->name, user)
+           && !strCasediff(agline->host, host))
+         break;
+       a2gline = agline;
+      }
+
+      if (!active && agline)
+      {                                /* removing the gline */
+       sendto_op_mask(SNO_GLINE, "%s removing GLINE for %s@%s", parv[0],
+           agline->name, agline->host);        /* notify opers */
+
+#ifdef GPATH
+       write_log(GPATH, "# " TIME_T_FMT " %s removing GLINE for %s@%s\n",
+           TStime(), parv[0], agline->name, agline->host);
+#endif /* GPATH */
+
+       free_gline(agline, a2gline);    /* remove the gline */
+      }
+      else if (active)
+      {                                /* must be adding a gline */
+       expire = atoi(parv[3]) + TStime();      /* expire time? */
+       if (agline && agline->expire < expire)
+       {                       /* new expire time? */
+         /* yes, notify the opers */
+         sendto_op_mask(SNO_GLINE,
+             "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+             parv[0], agline->name, agline->host, expire);
+
+#ifdef GPATH
+         write_log(GPATH, "# " TIME_T_FMT " %s resetting expiration time "
+             "on GLINE for %s@%s to " TIME_T_FMT "\n",
+             TStime(), parv[0], agline->name, agline->host, expire);
+#endif /* GPATH */
+
+         agline->expire = expire;      /* reset the expire time */
+       }
+       else if (!agline)
+       {                       /* create gline */
+         for (agline = gline; agline; agline = agline->next)
+           if (!mmatch(agline->name, user) &&
+               (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+               !mmatch(agline->host, host))
+             return 0;         /* found an existing G-line that matches */
+
+         /* add the line: */
+         add_gline(sptr, ip_mask, host, parv[4], user, expire, 0);
+       }
+      }
+    }
+  }
+  else if (parc < 2 || *parv[1] == '\0')
+  {
+    /* Not enough args and a user; list glines */
+    for (agline = gline; agline; agline = agline->next)
+      sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0],
+         agline->name, agline->host, agline->expire, agline->reason,
+         GlineIsActive(agline) ? (GlineIsLocal(agline) ? " (local)" : "") :
+         " (Inactive)");
+    sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+  }
+  else
+  {
+    int priv;
+
+#ifdef LOCOP_LGLINE
+    priv = IsAnOper(cptr);
+#else
+    priv = IsOper(cptr);
+#endif
+
+    if (priv)
+    {                          /* non-oper not permitted to change things */
+      if (*parv[1] == '-')
+      {                                /* oper wants to deactivate the gline */
+       active = 0;
+       parv[1]++;
+      }
+      else if (*parv[1] == '+')
+      {                                /* oper wants to activate inactive gline */
+       active = 1;
+       parv[1]++;
+      }
+      else
+       active = -1;
+
+      if (parc > 2)
+       expire = atoi(parv[2]) + TStime();      /* oper wants to reset
+                                                  expire TS */
+    }
+    else
+      active = -1;
+
+    if (!(host = strchr(parv[1], '@')))
+    {
+      user = "*";              /* no @'s; assume username is '*' */
+      host = parv[1];
+    }
+    else
+    {
+      user = parv[1];
+      *(host++) = '\0';                /* break up string at the '@' */
+    }
+    ip_mask = check_if_ipmask(host);   /* Store this boolean */
+
+    for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+    {
+      if (!mmatch(agline->name, user) &&
+         (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+         !mmatch(agline->host, host))
+       break;
+      a2gline = agline;
+    }
+
+    if (!agline)
+    {
+#ifdef OPER_LGLINE
+      if (priv && active && expire > now)
+      {
+       /* Add local G-line */
+       if (parc < 4 || !strchr(parv[3], ' '))
+       {
+         sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+             me.name, parv[0], "GLINE");
+         return 0;
+       }
+       add_gline(sptr, ip_mask, host, parv[3], user, expire, 1);
+      }
+      else
+#endif
+       sendto_one(cptr, err_str(ERR_NOSUCHGLINE), me.name, parv[0], user,
+           host);
+
+      return 0;
+    }
+
+    if (expire <= agline->expire)
+      expire = 0;
+
+    if ((active == -1 ||
+       (active ? GlineIsActive(agline) : !GlineIsActive(agline))) &&
+       expire == 0)
+    {
+      /* oper wants a list of one gline only */
+      sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0], agline->name,
+         agline->host, agline->expire, agline->reason,
+         GlineIsActive(agline) ? "" : " (Inactive)");
+      sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+      return 0;
+    }
+
+    if (active != -1 &&
+       (active ? !GlineIsActive(agline) : GlineIsActive(agline)))
+    {
+      if (active)              /* reset activation on gline */
+       SetActive(agline);
+#ifdef OPER_LGLINE
+      else if (GlineIsLocal(agline))
+      {
+       /* Remove local G-line */
+       sendto_op_mask(SNO_GLINE, "%s removed local GLINE for %s@%s",
+           parv[0], agline->name, agline->host);
+#ifdef GPATH
+       write_log(GPATH, "# " TIME_T_FMT
+           " %s!%s@%s removed local GLINE for %s@%s\n",
+           TStime(), parv[0], cptr->user->username, cptr->user->host,
+           agline->name, agline->host);
+#endif /* GPATH */
+       free_gline(agline, a2gline);    /* remove the gline */
+       return 0;
+      }
+#endif
+      else
+       ClearActive(agline);
+    }
+    else
+      active = -1;             /* for later sendto_ops and logging functions */
+
+    if (expire)
+      agline->expire = expire; /* reset expiration time */
+
+    /* inform the operators what's up */
+    if (active != -1)
+    {                          /* changing the activation */
+      sendto_op_mask(SNO_GLINE, !expire ? "%s %sactivating GLINE for %s@%s" :
+         "%s %sactivating GLINE for %s@%s and "
+         "resetting expiration time to " TIME_T_FMT,
+         parv[0], active ? "re" : "de", agline->name,
+         agline->host, agline->expire);
+#ifdef GPATH
+      write_log(GPATH, !expire ? "# " TIME_T_FMT " %s!%s@%s %sactivating "
+         "GLINE for %s@%s\n" : "# " TIME_T_FMT " %s!%s@%s %sactivating GLINE "
+         "for %s@%s and resetting expiration time to " TIME_T_FMT "\n",
+         TStime(), parv[0], cptr->user->username, cptr->user->host,
+         active ? "re" : "de", agline->name, agline->host, agline->expire);
+#endif /* GPATH */
+
+    }
+    else if (expire)
+    {                          /* changing only the expiration */
+      sendto_op_mask(SNO_GLINE,
+         "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+         parv[0], agline->name, agline->host, agline->expire);
+#ifdef GPATH
+      write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s resetting expiration "
+         "time on GLINE for %s@%s to " TIME_T_FMT "\n", TStime(), parv[0],
+         cptr->user->username, cptr->user->host, agline->name,
+         agline->host, agline->expire);
+#endif /* GPATH */
+    }
+  }
+
+  return 0;
+}