added basic ssl support to ircu
[ircu2.10.12-pk.git] / ircd / m_trace.c
index 7d3e7ec2036438a70157e8304739ab139a4fa505..a0ba9757eef3ef0aa0247edb97bc8664741cfd1b 100644 (file)
  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
  *                    non-NULL pointers.
  */
-#if 0
-/*
- * No need to include handlers.h here the signatures must match
- * and we don't need to force a rebuild of all the handlers everytime
- * we add a new one to the list. --Bleep
- */
-#include "handlers.h"
-#endif /* 0 */
+#include "config.h"
+
 #include "class.h"
 #include "client.h"
 #include "hash.h"
 #include "ircd.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
 #include "match.h"
 #include "send.h"
 #include "version.h"
 
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <string.h>
 
-/*
- * m_trace - generic message handler
- *
- * parv[0] = sender prefix
- * parv[1] = nick or servername
- * parv[2] = 'target' servername
- */
-int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 {
   int i;
   struct Client *acptr;
-  struct ConfClass *cltmp;
-  char *tname;
-  int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
-  int cnt = 0, wilds, dow;
+  struct Client *acptr2;
+  const struct ConnectionClass* cl;
+  char* tname;
+  int doall;
+  int link_s[MAXCONNECTIONS];
+  int link_u[MAXCONNECTIONS];
+  int cnt = 0;
+  int wilds;
+  int dow;
 
-  if (parc < 2 || BadPtr(parv[1])) {
+  if (parc < 2 || BadPtr(parv[1]))
+  {
     /* just "TRACE" without parameters. Must be from local client */
     parc = 1;
     acptr = &me;
-    tname = me.name;
+    tname = cli_name(&me);
     i = HUNTED_ISME;
-  } else if (parc < 3 || BadPtr(parv[2])) {
+  }
+  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))) {
+        ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
+    {
       if (IsUser(acptr))
-        parv[2] = acptr->user->server->name;
+        parv[2] = cli_name(cli_user(acptr)->server);
       else
-        parv[2] = acptr->name;
+        parv[2] = cli_name(acptr);
       parc = 3;
       parv[3] = 0;
       if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
                               "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
-        return 0;
-    } else
+        return;
+    }
+    else
       i = HUNTED_ISME;
   } else {
     /* Got "TRACE <tname> :<target>" */
@@ -154,7 +153,7 @@ int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
       acptr = FindNServer(parv[2]);
     if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
                             parv)) == HUNTED_NOSUCH)
-      return 0;
+      return;
     tname = parv[1];
   }
 
@@ -162,62 +161,59 @@ int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
     if (!acptr)
       acptr = next_client(GlobalClientList, tname);
     else
-      acptr = acptr->from;
+      acptr = cli_from(acptr);
     send_reply(sptr, RPL_TRACELINK,
-#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 */
-              ); /* I really could do without GODMODE */
-    return 0;
+              version, debugmode, tname,
+              acptr ? cli_name(cli_from(acptr)) : "<No_match>");
+    return;
   }
 
-  doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
+  doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
   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;
+  if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) {
+    send_reply(sptr, RPL_TRACEEND);
+    return;
+  }
 
   for (i = 0; i < MAXCONNECTIONS; i++)
     link_s[i] = 0, link_u[i] = 0;
 
   if (doall) {
-    for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
+    for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
       if (IsUser(acptr))
-        link_u[acptr->from->fd]++;
+        link_u[cli_fd(cli_from(acptr))]++;
       else if (IsServer(acptr))
-        link_s[acptr->from->fd]++;
+        link_s[cli_fd(cli_from(acptr))]++;
     }
   }
 
   /* report all direct connections */
 
   for (i = 0; i <= HighestFd; i++) {
-    unsigned int conClass;
+    const char *conClass;
 
     if (!(acptr = LocalClientArray[i])) /* Local Connection? */
       continue;
     if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
         !IsAnOper(acptr) && (acptr != sptr))
       continue;
-    if (!doall && wilds && match(tname, acptr->name))
+    if (!doall && wilds && match(tname, cli_name(acptr)))
       continue;
-    if (!dow && 0 != ircd_strcmp(tname, acptr->name))
+    if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
       continue;
 
     conClass = get_client_class(acptr);
 
-    switch (acptr->status) {
+    switch (cli_status(acptr)) {
       case STAT_CONNECTING:
-       send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
+       send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
         cnt++;
         break;
       case STAT_HANDSHAKE:
-       send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
+       send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
         cnt++;
         break;
       case STAT_ME:
@@ -239,12 +235,12 @@ int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
             !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
           if (IsAnOper(acptr))
            send_reply(sptr, RPL_TRACEOPERATOR, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                      CurrentTime - acptr->lasttime);
+                      get_client_name(acptr, SHOW_IP),
+                      CurrentTime - cli_lasttime(acptr));
           else
            send_reply(sptr, RPL_TRACEUSER, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                      CurrentTime - acptr->lasttime);
+                      get_client_name(acptr, SHOW_IP),
+                      CurrentTime - cli_lasttime(acptr));
           cnt++;
         }
         break;
@@ -265,19 +261,24 @@ int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
          */
 
       case STAT_SERVER:
-        if (acptr->serv->user)
+        if (cli_serv(acptr)->user) {
+          if (!cli_serv(acptr)->by[0]
+              || !(acptr2 = findNUser(cli_serv(acptr)->by))
+              || (cli_user(acptr2) != cli_serv(acptr)->user))
+            acptr2 = NULL;
          send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ? acptr->serv->by : "*",
-                     acptr->serv->user->username, acptr->serv->user->host,
-                     CurrentTime - acptr->lasttime,
-                     CurrentTime - acptr->serv->timestamp);
-       else
+                     link_u[i], cli_name(acptr),
+                     acptr2 ? cli_name(acptr2) : "*",
+                     cli_serv(acptr)->user->username,
+                     cli_serv(acptr)->user->host,
+                     CurrentTime - cli_lasttime(acptr),
+                     CurrentTime - cli_serv(acptr)->timestamp);
+        } else
          send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
-                     me.name, CurrentTime - acptr->lasttime,
-                    CurrentTime - acptr->serv->timestamp);
+                     link_u[i], cli_name(acptr),
+                     (*(cli_serv(acptr))->by) ?  cli_serv(acptr)->by : "*", "*",
+                     cli_name(&me), CurrentTime - cli_lasttime(acptr),
+                    CurrentTime - cli_serv(acptr)->timestamp);
         cnt++;
         break;
       default:                  /* We actually shouldn't come here, -msa */
@@ -290,624 +291,54 @@ int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * 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 */
-      send_reply(sptr, RPL_TRACESERVER, 0, link_s[me.fd],
-                 link_u[me.fd], "<No_match>", *(me.serv->by) ?
-                 me.serv->by : "*", "*", me.name, 0, 0);
-    return 0;
+  if (IsAnOper(sptr) && doall) {
+    for (cl = get_class_list(); cl; cl = cl->next) {
+      if (Links(cl) > 1)
+       send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1);
+    }
   }
-  for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
-    if (Links(cltmp) > 0)
-     send_reply(sptr, RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
-  return 0;
+  send_reply(sptr, RPL_TRACEEND);
 }
 
 /*
- * ms_trace - server message handler
+ * m_trace - generic message handler
  *
  * parv[0] = sender prefix
  * parv[1] = nick or servername
  * parv[2] = 'target' servername
  */
-int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  int i;
-  struct Client *acptr;
-  struct ConfClass *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] = 0;
-
-      if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
-                              "%s :%C", 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_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
-                            parv)) == HUNTED_NOSUCH)
-      return 0;
-    tname = parv[1];
-  }
-
-  if (i == HUNTED_PASS) {
-    if (!acptr)
-      acptr = next_client(GlobalClientList, tname);
-    else
-      acptr = acptr->from;
-    send_reply(sptr, RPL_TRACELINK,
-#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 */
-              ); /* I really could do without GODMODE */
-    return 0;
-  }
-
-  doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
-  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 = GlobalClientList; 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 <= HighestFd; i++) {
-    unsigned int conClass;
-
-    if (!(acptr = LocalClientArray[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 && 0 != ircd_strcmp(tname, acptr->name))
-      continue;
-    conClass = get_client_class(acptr);
-
-    switch (acptr->status) {
-      case STAT_CONNECTING:
-       send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
-        cnt++;
-        break;
-      case STAT_HANDSHAKE:
-       send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
-        cnt++;
-        break;
-      case STAT_ME:
-        break;
-      case STAT_UNKNOWN:
-      case STAT_UNKNOWN_USER:
-       send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
-                  get_client_name(acptr, HIDE_IP));
-        cnt++;
-        break;
-      case STAT_UNKNOWN_SERVER:
-       send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
-        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))
-           send_reply(sptr, RPL_TRACEOPERATOR, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                      CurrentTime - acptr->lasttime);
-          else
-           send_reply(sptr, RPL_TRACEUSER, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                      CurrentTime - 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)
-         send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ? acptr->serv->by : "*",
-                     acptr->serv->user->username, acptr->serv->user->host,
-                     CurrentTime - acptr->lasttime,
-                     CurrentTime - acptr->serv->timestamp);
-        else
-         send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
-                     me.name, CurrentTime - acptr->lasttime,
-                     CurrentTime - acptr->serv->timestamp);
-        cnt++;
-        break;
-      default:                  /* We actually shouldn't come here, -msa */
-       send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
-        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 */
-      send_reply(sptr, RPL_TRACESERVER, 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)
-      send_reply(sptr, RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
+  if (feature_bool(FEAT_HIS_TRACE))
+    return send_reply(cptr, ERR_NOPRIVILEGES);
+  do_trace(cptr, sptr, parc, parv);
   return 0;
 }
 
 /*
- * mo_trace - oper message handler
+ * ms_trace - server message handler
  *
  * parv[0] = sender prefix
  * parv[1] = nick or servername
  * parv[2] = 'target' servername
  */
-int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  int i;
-  struct Client *acptr;
-  struct ConfClass *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] = 0;
-      if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
-                              "%s :%C", 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_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
-                            parv)) == HUNTED_NOSUCH)
-      return 0;
-    tname = parv[1];
-  }
-
-  if (i == HUNTED_PASS) {
-    if (!acptr)
-      acptr = next_client(GlobalClientList, tname);
-    else
-      acptr = acptr->from;
-    send_reply(sptr, RPL_TRACELINK,
-#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 */
-              ); /* I really could do without GODMODE */
-    return 0;
-  }
-
-  doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
-  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 = GlobalClientList; 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 <= HighestFd; i++) {
-    unsigned int conClass;
-
-    if (!(acptr = LocalClientArray[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 && 0 != ircd_strcmp(tname, acptr->name))
-      continue;
-    conClass = get_client_class(acptr);
-
-    switch (acptr->status) {
-      case STAT_CONNECTING:
-       send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
-        cnt++;
-        break;
-      case STAT_HANDSHAKE:
-       send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
-        cnt++;
-        break;
-      case STAT_ME:
-        break;
-      case STAT_UNKNOWN:
-      case STAT_UNKNOWN_USER:
-       send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
-                  get_client_name(acptr, HIDE_IP));
-        cnt++;
-        break;
-      case STAT_UNKNOWN_SERVER:
-       send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
-        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))
-           send_reply(sptr, RPL_TRACEOPERATOR, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                       CurrentTime - acptr->lasttime);
-          else
-           send_reply(sptr, RPL_TRACEUSER, conClass,
-                      get_client_name(acptr, HIDE_IP),
-                       CurrentTime - 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)
-         send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ? acptr->serv->by : "*",
-                     acptr->serv->user->username, acptr->serv->user->host,
-                     CurrentTime - acptr->lasttime,
-                     CurrentTime - acptr->serv->timestamp);
-        else
-         send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
-                     link_u[i], acptr->name,
-                     (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
-                     me.name, CurrentTime - acptr->lasttime,
-                     CurrentTime - acptr->serv->timestamp);
-        cnt++;
-        break;
-      default:                  /* We actually shouldn't come here, -msa */
-       send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
-        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 */
-      send_reply(sptr, RPL_TRACESERVER, 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)
-      send_reply(sptr, RPL_TRACECLASS, ConClass(cltmp), Links(cltmp));
+  do_trace(cptr, sptr, parc, parv);
   return 0;
 }
 
-  
-#if 0
 /*
- * m_trace
+ * mo_trace - oper message handler
  *
  * parv[0] = sender prefix
  * parv[1] = nick or servername
  * parv[2] = 'target' servername
  */
-int m_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
+int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  int i;
-  struct Client *acptr;
-  struct ConfClass *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] = 0;
-      if ((i = hunt_server(IsServer(acptr), cptr, sptr, /* XXX DEAD */
-          "%s%s " TOK_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, /* XXX DEAD */
-        "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
-      return 0;
-    tname = parv[1];
-  }
-
-  if (i == HUNTED_PASS)
-  {
-    if (!acptr)
-      acptr = next_client(GlobalClientList, tname);
-    else
-      acptr = acptr->from;
-    sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0], /* XXX DEAD */
-#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 = GlobalClientList; 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 <= HighestFd; i++)
-  {
-    const char* name;
-    unsigned int conClass;
-
-    if (!(acptr = LocalClientArray[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 && 0 != ircd_strcmp(tname, acptr->name))
-      continue;
-    conClass = get_client_class(acptr);
-
-    switch (acptr->status)
-    {
-      case STAT_CONNECTING:
-        sendto_one(sptr, rpl_str(RPL_TRACECONNECTING), /* XXX DEAD */
-            me.name, parv[0], conClass, name);
-        cnt++;
-        break;
-      case STAT_HANDSHAKE:
-        sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE), /* XXX DEAD */
-            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), /* XXX DEAD */
-            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), /* XXX DEAD */
-                me.name, parv[0], conClass, name, CurrentTime - acptr->lasttime);
-          else
-            sendto_one(sptr, rpl_str(RPL_TRACEUSER), /* XXX DEAD */
-                me.name, parv[0], conClass, name, CurrentTime - 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), /* XXX DEAD */
-              me.name, parv[0], conClass, link_s[i],
-              link_u[i], name, (*acptr->serv->by) ? acptr->serv->by : "*",
-              acptr->serv->user->username, acptr->serv->user->host,
-              CurrentTime - acptr->lasttime,
-              CurrentTime - acptr->serv->timestamp);
-        else
-          sendto_one(sptr, rpl_str(RPL_TRACESERVER), /* XXX DEAD */
-              me.name, parv[0], conClass, link_s[i],
-              link_u[i], name, (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
-              me.name, CurrentTime - acptr->lasttime,
-              CurrentTime - acptr->serv->timestamp);
-        cnt++;
-        break;
-      default:                  /* We actually shouldn't come here, -msa */
-        sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0], name); /* XXX DEAD */
-        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), /* XXX DEAD */
-          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, /* XXX DEAD */
-          parv[0], ConClass(cltmp), Links(cltmp));
+  if (feature_bool(FEAT_HIS_TRACE) && !IsAnOper(sptr))
+    return send_reply(cptr, ERR_NOPRIVILEGES);
+  do_trace(cptr, sptr, parc, parv);
   return 0;
 }
-#endif /* 0 */
-