2004-05-09 Michael Poole <mdpoole@troilus.org>
authorMichael Poole <mdpoole@troilus.org>
Mon, 10 May 2004 03:08:16 +0000 (03:08 +0000)
committerMichael Poole <mdpoole@troilus.org>
Mon, 10 May 2004 03:08:16 +0000 (03:08 +0000)
Forward port of Paul "Zoot" Chang's pseudo-command.patch and
pseudo-support.patch.

* doc/example.conf: Illustrate how to use the feature.

* include/handlers.h (m_pseudo): Declare new handler function.

* include/ircd_features.h (HIS_STATS_R): Add a feature to control
user visibility of the pseudo-commands.

* include/msg.h: Add flag and field for the extra information used
to select a pseudo-command's target.

* include/numeric.h (RPL_STATSRLINE, ERR_SERVICESDOWN): Add
definitions.

* include/parse.h (register_mapping, unregister_mapping): Declare.

* include/s_conf.h (struct nick_host, struct s_map,
GlobalServiceMapList): Define.

* ircd/Makefile.in: Add m_pseudo.c to IRCD_SRC.  Add generated
files to "make depend" dependency list.  Update dependencies.

* ircd/ircd_features.c (HIS_STATS_R): Define feature type and
default value.

* ircd/ircd_lexer.l (pseudo, prepend): Recognize new tokens.

* ircd/ircd_parser.y: Support "Pseudo" configuration blocks.

* ircd/parse.c (msgtab): Add initializer for field "extra" to all
commands.
(msg_tree_insert, msg_tree_remove, register_mapping,
unregister_mapping): New functions.
(parse_client): Implement MFLG_EXTRA extra argument passing.

* ircd/s_conf.c (GlobalServiceMapList): New variable.

* ircd/s_err.c (RPL_STATRLINE, ERR_SERVICESDOWN): Add format
strings for new numeric responses.

* ircd/s_stats.c (stats_mapping): New function.
(statsinfo): Add entry for /stats R and make old /stats r entry
case-sensitive.

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

16 files changed:
ChangeLog
doc/example.conf
include/handlers.h
include/ircd_features.h
include/msg.h
include/numeric.h
include/parse.h
include/s_conf.h
ircd/Makefile.in
ircd/ircd_features.c
ircd/ircd_lexer.l
ircd/ircd_parser.y
ircd/parse.c
ircd/s_conf.c
ircd/s_err.c
ircd/s_stats.c

index 2495f24ea5714d13e99121cfa915e27c72eb8520..680ce49878b62bcdb623cba1ae08491b2fd6d5e4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,51 @@
+2004-05-09  Michael Poole <mdpoole@troilus.org>
+
+       Forward port of Paul "Zoot" Chang's pseudo-command.patch and
+       pseudo-support.patch.
+
+       * doc/example.conf: Illustrate how to use the feature.
+
+       * include/handlers.h (m_pseudo): Declare new handler function.
+
+       * include/ircd_features.h (HIS_STATS_R): Add a feature to control
+       user visibility of the pseudo-commands.
+
+       * include/msg.h: Add flag and field for the extra information used
+       to select a pseudo-command's target.
+
+       * include/numeric.h (RPL_STATSRLINE, ERR_SERVICESDOWN): Add
+       definitions.
+
+       * include/parse.h (register_mapping, unregister_mapping): Declare.
+
+       * include/s_conf.h (struct nick_host, struct s_map,
+       GlobalServiceMapList): Define.
+
+       * ircd/Makefile.in: Add m_pseudo.c to IRCD_SRC.  Add generated
+       files to "make depend" dependency list.  Update dependencies.
+
+       * ircd/ircd_features.c (HIS_STATS_R): Define feature type and
+       default value.
+
+       * ircd/ircd_lexer.l (pseudo, prepend): Recognize new tokens.
+
+       * ircd/ircd_parser.y: Support "Pseudo" configuration blocks.
+
+       * ircd/parse.c (msgtab): Add initializer for field "extra" to all
+       commands.
+       (msg_tree_insert, msg_tree_remove, register_mapping,
+       unregister_mapping): New functions.
+       (parse_client): Implement MFLG_EXTRA extra argument passing.
+
+       * ircd/s_conf.c (GlobalServiceMapList): New variable.
+
+       * ircd/s_err.c (RPL_STATRLINE, ERR_SERVICESDOWN): Add format
+       strings for new numeric responses.
+
+       * ircd/s_stats.c (stats_mapping): New function.
+       (statsinfo): Add entry for /stats R and make old /stats r entry
+       case-sensitive.
+       
 2004-05-09  Michael Poole <mdpoole@troilus.org>
 
        * ircd/ircd_parser.y (parse_error): Convert to being a wrapper for
index 8212254a0afcdf51a5e1f996ff6b554b8075e3aa..904fd0e23fa9f282bf10dabc93a0b672eaa00bd4 100644 (file)
@@ -664,6 +664,22 @@ Port {
  port = 7000;
 };
 
+# This is a server-implemented alias to send a message to a service.
+# The string after Pseudo is the command name; the name entry inside
+# is the service name, used for error messages.  More than one nick
+# entry can be provided; the last one listed has highest priority.
+Pseudo "CHANSERV" {
+ name = "X";
+ nick = "X@channels.undernet.org";
+};
+
+# You can also prepend text before the user's message.
+Pseudo "LOGIN" {
+ name = "X";
+ prepend = "LOGIN ";
+ nick = "X@channels.undernet.org";
+};
+
 # [features]
 # IRC servers have a large number of options and features.  Most of these
 # are set at compile time through the use of #define's--see "make config"
index bbd483eff6c31278bce09da7604c9763f30166e0..ca72913359b2b3a95ae466f4c745c1181e0fa373 100644 (file)
@@ -118,6 +118,7 @@ extern int m_pong(struct Client*, struct Client*, int, char*[]);
 extern int m_private(struct Client*, struct Client*, int, char*[]);
 extern int m_privmsg(struct Client*, struct Client*, int, char*[]);
 extern int m_proto(struct Client*, struct Client*, int, char*[]);
+extern int m_pseudo(struct Client*, struct Client*, int, char*[]);
 extern int m_quit(struct Client*, struct Client*, int, char*[]);
 extern int m_registered(struct Client*, struct Client*, int, char*[]);
 extern int m_silence(struct Client*, struct Client*, int, char*[]);
index d9d3f068634b24a3a394a05e2bcd4d2ec2f81a04..e480b5bd8e3533279b7f6890d19fc4ecdb5fff80 100644 (file)
@@ -148,6 +148,7 @@ enum Feature {
   FEAT_HIS_STATS_o,
   FEAT_HIS_STATS_p,
   FEAT_HIS_STATS_q,
+  FEAT_HIS_STATS_R,
   FEAT_HIS_STATS_r,
   FEAT_HIS_STATS_d,
   FEAT_HIS_STATS_e,
index 9087926bd2a567c1044519779e6161ad7b542a64..207b0fbe9fcaffde40c5e84222839366db371d28 100644 (file)
@@ -362,6 +362,8 @@ struct Client;
                                          * clients.                           */
 #define   MFLG_IGNORE            0x04   /* silently ignore command from
                                          * unregistered clients */
+#define   MFLG_EXTRA             0x08   /* Handler requests that mptr->extra
+                                         * be passed in parv[1] */
 
 /*
  * Structures
@@ -375,6 +377,7 @@ struct Message {
                                    to be used only on the average of once per 2
                                    seconds -SRB */
   unsigned int bytes;         /* bytes received for this message */
+  void *extra;                /* extra pointer to be passed in parv[1] */
   /*
    * cptr = Connected client ptr
    * sptr = Source client ptr
index 72c5b5109b434485339beae6ef2ee737b2b943fa..05f62f5fd82b299c44a1c50721b6f47392fc4b9d 100644 (file)
@@ -172,6 +172,7 @@ extern const struct Numeric* get_error_numeric(int err);
 /*      RPL_END_NOTIFY       274           aircd */
 /*      RPL_STATSDELTA       274           IRCnet extension */
 #define RPL_STATSDLINE       275        /* Undernet extension */
+#define RPL_STATSRLINE       276        /* Undernet extension */
 
 #define RPL_GLIST            280        /* Undernet extension */
 #define RPL_ENDOFGLIST       281        /* Undernet extension */
@@ -351,7 +352,7 @@ extern const struct Numeric* get_error_numeric(int err);
 #define ERR_NICKTOOFAST      438        /* Undernet extension */
      /* ERR_DEAD             438           IRCnet reserved for later use */
 #define ERR_TARGETTOOFAST    439        /* Undernet extension */
-/*     ERR_SERVICESDOWN     440        Dalnet,unreal */
+#define ERR_SERVICESDOWN     440       /* Dalnet,unreal,Undernet */
 #define ERR_USERNOTINCHANNEL 441
 #define ERR_NOTONCHANNEL     442
 #define ERR_USERONCHANNEL    443
index 888d6dedd580d58a7732f0a1b9ede366a0d4aefb..75e3b0c520e20de5916e967b2760e01a412facf0 100644 (file)
@@ -7,6 +7,7 @@
 #define INCLUDED_parse_h
 
 struct Client;
+struct s_map;
 
 /*
  * Prototypes
@@ -16,4 +17,7 @@ extern int parse_client(struct Client *cptr, char *buffer, char *bufend);
 extern int parse_server(struct Client *cptr, char *buffer, char *bufend);
 extern void initmsgtree(void);
 
+extern int register_mapping(struct s_map *map);
+extern int unregister_mapping(struct s_map *map);
+
 #endif /* INCLUDED_parse_h */
index 8823ce74c1bff47a527190471a446b98ce1560d6..ff9a007a4a522db2b1570abd4cddef62fb19dbc7 100644 (file)
@@ -22,6 +22,7 @@
 struct Client;
 struct SLink;
 struct TRecord;
+struct Message;
 
 
 /*
@@ -160,6 +161,21 @@ enum AuthorizationCheckResult {
   ACR_BAD_SOCKET
 };
 
+struct nick_host {
+  struct nick_host *next;
+  int nicklen; /* offset of @ part of server string */
+  char nick[1]; /* start of nick@server string */
+};
+
+struct s_map {
+  struct s_map *next;
+  struct Message *msg;
+  char *name;
+  char *command;
+  char *prepend;
+  struct nick_host *services;
+};
+
 
 /*
  * GLOBALS
@@ -170,7 +186,8 @@ extern struct tm        motd_tm;
 extern struct MotdItem* motd;
 extern struct MotdItem* rmotd;
 extern struct TRecord*  tdata;
-extern struct qline*   GlobalQuarantineList;
+extern struct s_map*    GlobalServiceMapList;
+extern struct qline*    GlobalQuarantineList;
 
 /*
  * Proto types
index d10c9aa7101da1ae691ccaf9668d74d912c3d2d4..713c7dc6b7ffdb70e27df35d4ca31068e798bf83 100644 (file)
@@ -153,6 +153,7 @@ IRCD_SRC = \
        m_privmsg.c \
        m_privs.c \
        m_proto.c \
+       m_pseudo.c \
        m_quit.c \
        m_rehash.c \
        m_reset.c \
@@ -325,7 +326,7 @@ maintainer-clean: distclean
 
 # If I read this right, this will only work with gcc.  Still, how many admins
 # are going to be doing "make depend"?
-depend:
+depend: ${DEP_SRC} chattr.tab.c
        @cd ${srcdir}; \
        if [ -f Makefile.in.bak ]; then \
          echo "make depend: First remove ircd/Makefile.in.bak"; \
@@ -354,7 +355,6 @@ depend:
 #      @cd ../config; ${MAKE} config.h
 
 # DO NOT DELETE THIS LINE -- make depend depends on it.
-
 IPcheck.o: IPcheck.c ../config.h ../include/IPcheck.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../include/ircd_handler.h ../include/ircd.h \
@@ -692,7 +692,7 @@ m_kick.o: m_kick.c ../config.h ../include/channel.h \
   ../include/hash.h ../include/ircd.h ../include/struct.h \
   ../include/ircd_reply.h ../include/ircd_string.h \
   ../include/ircd_chattr.h ../include/msg.h ../include/numeric.h \
-  ../include/numnicks.h ../include/send.h
+  ../include/numnicks.h ../include/send.h ../include/ircd_features.h
 m_kill.o: m_kill.c ../config.h ../include/client.h ../include/ircd_defs.h \
   ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
   ../include/ircd_handler.h ../include/hash.h ../include/ircd.h \
@@ -786,7 +786,8 @@ m_opmode.o: m_opmode.c ../config.h ../include/client.h \
   ../include/hash.h ../include/ircd.h ../include/struct.h \
   ../include/ircd_features.h ../include/ircd_reply.h \
   ../include/ircd_string.h ../include/ircd_chattr.h ../include/msg.h \
-  ../include/numeric.h ../include/numnicks.h ../include/send.h
+  ../include/numeric.h ../include/numnicks.h ../include/send.h \
+  ../include/s_conf.h
 m_part.o: m_part.c ../config.h ../include/channel.h \
   ../include/ircd_defs.h ../include/client.h ../include/dbuf.h \
   ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
@@ -833,6 +834,14 @@ m_proto.o: m_proto.c ../config.h ../include/client.h \
   ../include/numeric.h ../include/numnicks.h ../include/s_debug.h \
   ../include/s_misc.h ../include/send.h ../include/supported.h \
   ../include/channel.h ../include/version.h
+m_pseudo.o: m_pseudo.c ../config.h ../include/client.h \
+  ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
+  ../include/ircd_events.h ../include/ircd_handler.h ../include/hash.h \
+  ../include/ircd.h ../include/struct.h ../include/ircd_features.h \
+  ../include/ircd_relay.h ../include/ircd_reply.h \
+  ../include/ircd_string.h ../include/ircd_chattr.h \
+  ../include/ircd_snprintf.h ../include/msg.h ../include/numeric.h \
+  ../include/numnicks.h ../include/s_conf.h ../include/s_user.h
 m_quit.o: m_quit.c ../config.h ../include/channel.h \
   ../include/ircd_defs.h ../include/client.h ../include/dbuf.h \
   ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
@@ -1108,17 +1117,17 @@ s_bsd.o: s_bsd.c ../config.h ../include/s_bsd.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../include/ircd_handler.h ../include/IPcheck.h \
   ../include/channel.h ../include/class.h ../include/hash.h \
-  ../include/ircd_log.h ../include/ircd_features.h \
-  ../include/ircd_osdep.h ../include/ircd_reply.h \
-  ../include/ircd_snprintf.h ../include/ircd_string.h \
-  ../include/ircd_chattr.h ../include/ircd.h ../include/struct.h \
-  ../include/list.h ../include/listener.h ../include/msg.h \
-  ../include/numeric.h ../include/numnicks.h ../include/packet.h \
-  ../include/parse.h ../include/querycmds.h ../include/res.h \
-  ../include/s_auth.h ../include/s_conf.h ../include/s_debug.h \
-  ../include/s_misc.h ../include/s_user.h ../include/send.h \
-  ../include/support.h ../include/sys.h ../include/uping.h \
-  ../include/version.h
+  ../include/ircd_alloc.h ../include/ircd_log.h \
+  ../include/ircd_features.h ../include/ircd_osdep.h \
+  ../include/ircd_reply.h ../include/ircd_snprintf.h \
+  ../include/ircd_string.h ../include/ircd_chattr.h ../include/ircd.h \
+  ../include/struct.h ../include/list.h ../include/listener.h \
+  ../include/msg.h ../include/numeric.h ../include/numnicks.h \
+  ../include/packet.h ../include/parse.h ../include/querycmds.h \
+  ../include/res.h ../include/s_auth.h ../include/s_conf.h \
+  ../include/s_debug.h ../include/s_misc.h ../include/s_user.h \
+  ../include/send.h ../include/support.h ../include/sys.h \
+  ../include/uping.h ../include/version.h
 s_conf.o: s_conf.c ../config.h ../include/s_conf.h ../include/client.h \
   ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
   ../include/ircd_events.h ../include/ircd_handler.h ../include/IPcheck.h \
index c128e8b809bb2249f92be2c8c0a1707778d9ea44..ff5abae474c505a5eb0060092fce370605be6252 100644 (file)
@@ -354,6 +354,7 @@ static struct FeatureDesc {
   F_B(HIS_STATS_o, 0, 1, 0),
   F_B(HIS_STATS_p, 0, 1, 0),
   F_B(HIS_STATS_q, 0, 1, 0),
+  F_B(HIS_STATS_R, 0, 1, 0),
   F_B(HIS_STATS_r, 0, 1, 0),
   F_B(HIS_STATS_d, 0, 1, 0),
   F_B(HIS_STATS_e, 0, 1, 0),
index 35ecc04194dd57d1df3910688a11483c0aa79152..774ed3df3521c4107ba05c42a17d929e81b43bd1 100644 (file)
@@ -149,5 +149,7 @@ unlimited_who_queries return TPRIV_UNLIMIT_QUERY;
 oper_status_display return TPRIV_DISPLAY;
 see_other_opers return TPRIV_SEE_OPERS;
 wide_glines return TPRIV_WIDE_GLINE;
+pseudo return PSEUDO;
+prepend return PREPEND;
 \n lineno++;
 . return yytext[0];
index f545c003cf61e7d2c4dd4f69a47459039b4103ac..c79272ff353605c77befbeb88403186548c24fc3 100644 (file)
@@ -63,7 +63,8 @@
   extern struct DenyConf*   denyConfList;
   extern struct CRuleConf*  cruleConfList;
   extern struct ServerConf* serverConfList;
-  extern struct qline     *GlobalQuarantineList;
+  extern struct s_map*      GlobalServiceMapList;
+  extern struct qline*      GlobalQuarantineList;
  
 
   int yylex(void);
@@ -77,6 +78,7 @@
   struct DenyConf *dconf;
   struct ServerConf *sconf;
   struct qline *qconf = NULL;
+  struct s_map *smap;
 
 static void parse_error(char *pattern,...) {
   static char error_buffer[1024];
@@ -150,6 +152,8 @@ static void parse_error(char *pattern,...) {
 %token IP
 %token FEATURES
 %token QUARANTINE
+%token PSEUDO
+%token PREPEND
 /* and now a lot of priviledges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_KILL TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -176,7 +180,7 @@ blocks: blocks block | block;
 block: adminblock | generalblock | classblock | connectblock |
        serverblock | operblock | portblock | jupeblock | clientblock |
        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
-       error;
+       pseudoblock | error;
 
 /* The timespec, sizespec and expr was ripped straight from
  * ircd-hybrid-7. */
@@ -922,3 +926,60 @@ quarantineitems: CHANNEL NAME '=' QSTRING ';'
 {
   DupString(qconf->reason, yylval.text);
 };
+
+pseudoblock: PSEUDO QSTRING '{'
+{
+  smap = MyCalloc(1, sizeof(struct s_map));
+  DupString(smap->command, $2);
+}
+pseudoitems '}' ';'
+{
+  if (!smap->name || !smap->services)
+  {
+    log_write(LS_CONFIG, L_ERROR, 0, "pseudo commands need a service name and list of target nicks.");
+    return 0;
+  }
+  if (register_mapping(smap))
+  {
+    smap->next = GlobalServiceMapList;
+    GlobalServiceMapList = smap;
+  }
+  else
+  {
+    struct nick_host *nh, *next;
+    for (nh = smap->services; nh; nh = next)
+    {
+      next = nh->next;
+      MyFree(nh);
+    }
+    MyFree(smap->name);
+    MyFree(smap->command);
+    MyFree(smap->prepend);
+    MyFree(smap);
+  }
+  smap = NULL;
+};
+
+pseudoitems: pseudoitem pseudoitems | pseudoitem;
+pseudoitem: pseudoname | pseudoprepend | pseudonick | error;
+pseudoname: NAME '=' QSTRING ';'
+{
+  DupString(smap->name, yylval.text);
+};
+pseudoprepend: PREPEND '=' QSTRING ';'
+{
+  DupString(smap->prepend, yylval.text);
+};
+pseudonick: NICK '=' QSTRING ';'
+{
+  char *sep = strchr(yylval.text, '@');
+
+  if (sep != NULL) {
+    size_t slen = strlen(yylval.text);
+    struct nick_host *nh = MyMalloc(sizeof(*nh) + slen);
+    memcpy(nh->nick, yylval.text, slen + 1);
+    nh->nicklen = sep - yylval.text;
+    nh->next = smap->services;
+    smap->services = nh;
+  }
+};
index 59976eec0eb373f5c74e8169377b69e395cebf2b..6add537dd4146ffac84ed6eb6f6dcb36235a5658 100644 (file)
@@ -101,518 +101,518 @@ struct Message msgtab[] = {
   {
     MSG_PRIVATE,
     TOK_PRIVATE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore }
   },
   {
     MSG_NICK,
     TOK_NICK,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_nick, m_nick, ms_nick, m_nick, m_ignore }
   },
   {
     MSG_NOTICE,
     TOK_NOTICE,
-    0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_notice, ms_notice, mo_notice, m_ignore }
   },
   {
     MSG_WALLCHOPS,
     TOK_WALLCHOPS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore }
   },
   {
     MSG_WALLVOICES,
     TOK_WALLVOICES,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore }
   },
   {
     MSG_CPRIVMSG,
     TOK_CPRIVMSG,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore }
   },
   {
     MSG_CNOTICE,
     TOK_CNOTICE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore }
   },
   {
     MSG_JOIN,
     TOK_JOIN,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_join, ms_join, m_join, m_ignore }
   },
   {
     MSG_MODE,
     TOK_MODE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_mode, ms_mode, m_mode, m_ignore }
   },
   {
     MSG_BURST,
     TOK_BURST,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_burst, m_ignore, m_ignore }
   },
   {
     MSG_CREATE,
     TOK_CREATE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_create, m_ignore, m_ignore }
   },
   {
     MSG_DESTRUCT,
     TOK_DESTRUCT,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_destruct, m_ignore, m_ignore }
   },
   {
     MSG_QUIT,
     TOK_QUIT,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_quit, m_quit, ms_quit, m_quit, m_ignore }
   },
   {
     MSG_PART,
     TOK_PART,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_part, ms_part, m_part, m_ignore }
   },
   {
     MSG_TOPIC,
     TOK_TOPIC,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_topic, ms_topic, m_topic, m_ignore }
   },
   {
     MSG_INVITE,
     TOK_INVITE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_invite, ms_invite, m_invite, m_ignore }
   },
   {
     MSG_KICK,
     TOK_KICK,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_kick, ms_kick, m_kick, m_ignore }
   },
   {
     MSG_WALLOPS,
     TOK_WALLOPS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore }
   },
   {
     MSG_WALLUSERS,
     TOK_WALLUSERS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore }
   },
   {
     MSG_DESYNCH,
     TOK_DESYNCH,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore }
   },
   {
     MSG_PING,
     TOK_PING,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_ping, ms_ping, mo_ping, m_ignore }
   },
   {
     MSG_PONG,
     TOK_PONG,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { mr_pong, m_pong, ms_pong, m_pong, m_ignore }
   },
   {
     MSG_ERROR,
     TOK_ERROR,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { mr_error, m_ignore, ms_error, m_ignore, m_ignore }
   },
   {
     MSG_KILL,
     TOK_KILL,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore }
   },
   {
     MSG_USER,
     TOK_USER,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_user, m_registered, m_ignore, m_registered, m_ignore }
   },
   {
     MSG_AWAY,
     TOK_AWAY,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_away, ms_away, m_away, m_ignore }
   },
   {
     MSG_ISON,
     TOK_ISON,
-    0, 1, MFLG_SLOW, 0,
+    0, 1, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_ison, m_ignore, m_ison, m_ignore }
   },
   {
     MSG_SERVER,
     TOK_SERVER,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { mr_server, m_registered, ms_server, m_registered, m_ignore }
   },
   {
     MSG_SQUIT,
     TOK_SQUIT,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_squit, mo_squit, m_ignore }
   },
   {
     MSG_WHOIS,
     TOK_WHOIS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_whois, ms_whois, m_whois, m_ignore }
   },
   {
     MSG_WHO,
     TOK_WHO,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_who, m_ignore, m_who, m_ignore }
   },
   {
     MSG_WHOWAS,
     TOK_WHOWAS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore }
   },
   {
     MSG_LIST,
     TOK_LIST,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_list, m_ignore, m_list, m_ignore }
   },
   {
     MSG_NAMES,
     TOK_NAMES,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_names, ms_names, m_names, m_ignore }
   },
   {
     MSG_USERHOST,
     TOK_USERHOST,
-    0, 1, MFLG_SLOW, 0,
+    0, 1, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore }
   },
   {
     MSG_USERIP,
     TOK_USERIP,
-    0, 1, MFLG_SLOW, 0,
+    0, 1, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_userip, m_ignore, m_userip, m_ignore }
   },
   {
     MSG_TRACE,
     TOK_TRACE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_trace, ms_trace, mo_trace, m_ignore }
   },
   {
     MSG_PASS,
     TOK_PASS,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { mr_pass, m_registered, m_ignore, m_registered, m_ignore }
   },
   {
     MSG_LUSERS,
     TOK_LUSERS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore }
   },
   {
     MSG_TIME,
     TOK_TIME,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_time, m_time, m_time, m_ignore }
   },
   {
     MSG_SETTIME,
     TOK_SETTIME,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_ignore, ms_settime, mo_settime, m_ignore }
   },
   {
     MSG_RPING,
     TOK_RPING,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_rping, mo_rping, m_ignore }
   },
   {
     MSG_RPONG,
     TOK_RPONG,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore }
   },
   {
     MSG_OPER,
     TOK_OPER,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_oper, ms_oper, mo_oper, m_ignore }
   },
   {
     MSG_CONNECT,
     TOK_CONNECT,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_connect, mo_connect, m_ignore }
   },
   {
     MSG_MAP,
     TOK_MAP,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_map, m_ignore, m_map, m_ignore }
   },
   {
     MSG_VERSION,
     TOK_VERSION,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_version, m_version, ms_version, mo_version, m_ignore }
   },
   {
     MSG_STATS,
     TOK_STATS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_stats, m_stats, m_stats, m_ignore }
   },
   {
     MSG_LINKS,
     TOK_LINKS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_links, ms_links, m_links, m_ignore }
   },
   {
     MSG_ADMIN,
     TOK_ADMIN,
-    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,
+    0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,  NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_admin, m_admin, ms_admin, mo_admin, m_ignore }
   },
   {
     MSG_HELP,
     TOK_HELP,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_help, m_ignore, m_help, m_ignore }
   },
   {
     MSG_INFO,
     TOK_INFO,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_info, ms_info, mo_info, m_ignore }
   },
   {
     MSG_MOTD,
     TOK_MOTD,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_motd, m_motd, m_motd, m_ignore }
   },
   {
     MSG_CLOSE,
     TOK_CLOSE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_close, m_ignore }
   },
   {
     MSG_SILENCE,
     TOK_SILENCE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_silence, ms_silence, m_silence, m_ignore }
   },
   {
     MSG_GLINE,
     TOK_GLINE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_gline, ms_gline, mo_gline, m_ignore }
   },
   {
     MSG_JUPE,
     TOK_JUPE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_jupe, ms_jupe, mo_jupe, m_ignore }
   },
   {
     MSG_OPMODE,
     TOK_OPMODE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_opmode, mo_opmode, m_ignore }
   },
   {
     MSG_CLEARMODE,
     TOK_CLEARMODE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_clearmode, mo_clearmode, m_ignore }
   },
   {
     MSG_UPING,
     TOK_UPING,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, ms_uping, mo_uping, m_ignore }
   },
   {
     MSG_END_OF_BURST,
     TOK_END_OF_BURST,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_end_of_burst, m_ignore, m_ignore }
   },
   {
     MSG_END_OF_BURST_ACK,
     TOK_END_OF_BURST_ACK,
-    0, MAXPARA, 1, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_end_of_burst_ack, m_ignore, m_ignore }
   },
   {
     MSG_HASH,
     TOK_HASH,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_hash, m_hash, m_hash, m_ignore }
   },
   {
     MSG_DNS,
     TOK_DNS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_dns, m_dns, m_dns, m_ignore }
   },
   {
     MSG_REHASH,
     TOK_REHASH,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_rehash, m_ignore }
   },
   {
     MSG_RESTART,
     TOK_RESTART,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore }
   },
   {
     MSG_DIE,
     TOK_DIE,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore }
   },
   {
     MSG_PROTO,
     TOK_PROTO,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_proto, m_proto, m_proto, m_proto, m_ignore }
   },
   {
     MSG_SET,
     TOK_SET,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_set, m_ignore }
   },
   {
     MSG_RESET,
     TOK_RESET,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_reset, m_ignore }
   },
   {
     MSG_GET,
     TOK_GET,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_get, m_ignore }
   },
   {
     MSG_PRIVS,
     TOK_PRIVS,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_unregistered, m_not_oper, m_ignore, mo_privs, m_ignore }
   },
   {
     MSG_ACCOUNT,
     TOK_ACCOUNT,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
   },
   {
     MSG_ASLL,
     TOK_ASLL,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore }
    },
@@ -625,7 +625,7 @@ struct Message msgtab[] = {
   {
     MSG_POST,
     TOK_POST,
-    0, MAXPARA, MFLG_SLOW, 0,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_quit, m_ignore, m_ignore, m_ignore, m_ignore }
   },
@@ -748,6 +748,104 @@ msg_tree_parse(char *cmd, struct MessageTree *root)
   return NULL;
 }
 
+/* Inserts a single entry into a message tree; must use this function
+   when inserting messages at runtime. */
+static void msg_tree_insert(struct MessageTree *mtree, int pfxlen,
+    char *key, struct Message *mptr)
+{
+  struct MessageTree *child;
+  int c;
+
+  if (!key[pfxlen])
+  {
+    mtree->msg = mptr;
+    return;
+  }
+  c = key[pfxlen];
+  child = mtree->pointers[c & (MAXPTRLEN-1)];
+  if(!child)
+  {
+    child = (struct MessageTree *)MyCalloc(1, sizeof(struct MessageTree));
+    mtree->pointers[c & (MAXPTRLEN-1)] = child;
+  }
+  msg_tree_insert(child, pfxlen+1, key, mptr);
+}
+
+/* Removes an entry from the message tree; suitable for use at runtime. */
+static struct MessageTree *msg_tree_remove(struct MessageTree *root, char *key)
+{
+  int c;
+
+  if (*key)
+  {
+    struct MessageTree *child = root->pointers[*key & (MAXPTRLEN-1)];
+    if (msg_tree_remove(child, key + 1))
+      return root;
+    root->pointers[*key & (MAXPTRLEN-1)] = NULL;
+  }
+  else
+  {
+    root->msg = NULL;
+  }
+  for (c = 0; c < MAXPTRLEN; ++c)
+  {
+    if (root->pointers[c])
+      return root;
+  }
+  MyFree(root);
+  return NULL;
+}
+
+/* Registers a service mapping to the pseudocommand handler. */
+int register_mapping(struct s_map *map)
+{
+  struct Message *msg;
+
+  if (msg_tree_parse(map->command, &msg_tree))
+    return 0;
+
+  msg = (struct Message *)MyMalloc(sizeof(struct Message));
+  msg->cmd = map->command;
+  msg->tok = map->command;
+  msg->count = 0;
+  msg->parameters = 2;
+  msg->flags = MFLG_SLOW | MFLG_EXTRA;
+  msg->bytes = 0;
+  msg->extra = map;
+
+  msg->handlers[UNREGISTERED_HANDLER] = m_ignore;
+  msg->handlers[CLIENT_HANDLER] = m_pseudo;
+  msg->handlers[SERVER_HANDLER] = m_ignore;
+  msg->handlers[OPER_HANDLER] = m_pseudo;
+  msg->handlers[SERVICE_HANDLER] = m_ignore;
+
+  /* Service mappings are only applicable to clients; insert the
+     pseudocommand into the command tree only. */
+  msg_tree_insert(&msg_tree, 0, msg->cmd, msg);
+  map->msg = msg;
+
+  return 1;
+}
+
+/* Removes a service mapping. */
+int unregister_mapping(struct s_map *map)
+{
+  if (!msg_tree_parse(map->command, &msg_tree))
+  {
+    /* This simply should never happen. */
+    assert(0);
+    return 0;
+  }
+
+  msg_tree_remove(&msg_tree, map->msg->cmd);
+
+  map->msg->extra = NULL;
+  MyFree(map->msg);
+  map->msg = NULL;
+
+  return 1;
+}
+
 /*
  * parse a buffer.
  *
@@ -839,7 +937,14 @@ parse_client(struct Client *cptr, char *buffer, char *bufend)
 
   /* Note initially true: s==NULL || *(s-1) == '\0' !! */
 
-  i = 0;
+  if (mptr->flags & MFLG_EXTRA) {
+    /* This is a horrid kludge to avoid changing the command handler
+     * argument list. */
+    para[1] = (char*)mptr->extra;
+    i = 1;
+  } else {
+    i = 0;
+  }
   if (s)
   {
     if (paramcount > MAXPARA)
index 64839a1259fa1edcf063186382711cd00edd29b0..7733c9f8e26cfc01e277db2ade0bcf73311db099 100644 (file)
@@ -71,6 +71,7 @@
 
 struct ConfItem  *GlobalConfList  = 0;
 int              GlobalConfCount = 0;
+struct s_map     *GlobalServiceMapList = 0;
 struct qline     *GlobalQuarantineList = 0;
 
 void yyparse(void);
index d20f623afd7eeb9c87d7807f53ef98dda03cb0cb..0c8868129557c14ea6c9189b808805c86201b1fa 100644 (file)
@@ -584,7 +584,7 @@ static Numeric replyTable[] = {
 /* 275 */
   { RPL_STATSDLINE, "%c %s %s", "275" },
 /* 276 */
-  { 0 },
+  { RPL_STATSRLINE, "%-9s %-9s %-10s %s", "276" },
 /* 277 */
   { 0 },
 /* 278 */
@@ -912,7 +912,7 @@ static Numeric replyTable[] = {
 /* 439 */
   { ERR_TARGETTOOFAST, "%s :Target change too fast. Please wait %d seconds.", "439" },
 /* 440 */
-  { 0 },
+  { ERR_SERVICESDOWN, "%s :Services are currently unavailable.", "440" },
 /* 441 */
   { ERR_USERNOTINCHANNEL, "%s %s :They aren't on that channel", "441" },
 /* 442 */
index 886abda90b3f392873b0014bd8602bfad46e0611..34eb34a16e23e442784d8a9f4bfb5b1224e1dc79 100644 (file)
@@ -318,9 +318,24 @@ stats_quarantine(struct Client* to, struct StatDesc* sd, int stat, char* param)
     if (param && match(param, qline->chname)) /* narrow search */
       continue;
     send_reply(to, RPL_STATSQLINE, qline->chname, qline->reason);
-   }
- }
+  }
+}
  
+static void
+stats_mapping(struct Client *to, struct StatDesc* sd, int stat, char* param)
+{
+  struct s_map *map;
+
+  send_reply(to, RPL_STATSRLINE, "Command", "Name", "Prepend", "Target");
+  for (map = GlobalServiceMapList; map; map = map->next) {
+    struct nick_host *nh;
+    for (nh = map->services; nh; nh = nh->next) {
+      send_reply(to, RPL_STATSRLINE, map->command, map->name,
+                 (map->prepend ? map->prepend : "*"), nh->nick);
+    }
+  }
+}
+
 static void
 stats_uptime(struct Client* to, struct StatDesc* sd, int stat, char* param)
 {
@@ -453,8 +468,11 @@ struct StatDesc statsinfo[] = {
   { 'q', (STAT_FLAG_OPERONLY | STAT_FLAG_VARPARAM), FEAT_HIS_STATS_q,
     stats_quarantine, 0,
     "Quarantined channels list." },
+  { 'R', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_R,
+    stats_mapping, 0,
+    "Service mappings." },
 #ifdef DEBUGMODE
-  { 'r', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_r,
+  { 'r', (STAT_FLAG_OPERFEAT | STAT_FLAG_CASESENS), FEAT_HIS_STATS_r,
     send_usage, 0,
     "System resource usage (Debug only)." },
 #endif