]> jfr.im git - irc/quakenet/snircd-patchqueue.git/commitdiff
cmdhelp.patch almost finished
authorwiebe <redacted>
Sun, 25 Jan 2009 22:56:55 +0000 (23:56 +0100)
committerwiebe <redacted>
Sun, 25 Jan 2009 22:56:55 +0000 (23:56 +0100)
cmdhelp.patch
series
welcome.patch [new file with mode: 0644]

index e982ddc052a4d3bec6a91185c852ffaef16b760f..38be1f1c0b22a1c2b79d1f9d6e4492625df52565 100644 (file)
 make /HELP <command> work - oper only feature?
 need to make small test case to see how hard/easy this would be to add.
 
-diff -r ce13b0a1253e ircd/m_help.c
---- a/ircd/m_help.c    Wed Jan 14 14:19:43 2009 +0100
-+++ b/ircd/m_help.c    Wed Jan 14 15:01:46 2009 +0100
-@@ -100,7 +100,7 @@
+diff -r 7fa4708bac45 include/handlers.h
+--- a/include/handlers.h       Sun Jan 25 12:48:01 2009 +0100
++++ b/include/handlers.h       Sun Jan 25 23:52:18 2009 +0100
+@@ -244,5 +244,77 @@
+ extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
+ extern int ms_whois(struct Client*, struct Client*, int, char*[]);
++extern int mh_nohelp(struct Client*, struct Client*, int, char*[]);
++extern int mh_not_oper(struct Client*, struct Client*, int, char*[]);
++extern int mh_not_server(struct Client*, struct Client*, int, char*[]);
++extern int mh_admin(struct Client*, struct Client*, int, char*[]);
++extern int mh_asll(struct Client*, struct Client*, int, char*[]);
++extern int mh_away(struct Client*, struct Client*, int, char*[]);
++extern int mh_check(struct Client*, struct Client*, int, char*[]);
++extern int mh_clearmode(struct Client*, struct Client*, int, char*[]);
++extern int mh_close(struct Client*, struct Client*, int, char*[]);
++extern int mh_cnotice(struct Client*, struct Client*, int, char*[]);
++extern int mh_connect(struct Client*, struct Client*, int, char*[]);
++extern int mh_cprivmsg(struct Client*, struct Client*, int, char*[]);
++extern int mh_die(struct Client*, struct Client*, int, char*[]);
++extern int mh_get(struct Client*, struct Client*, int, char*[]);
++extern int mh_gline(struct Client*, struct Client*, int, char*[]);
++extern int mh_hash(struct Client*, struct Client*, int, char*[]);
++extern int mh_help(struct Client*, struct Client*, int, char*[]);
++extern int mh_info(struct Client*, struct Client*, int, char*[]);
++extern int mh_invite(struct Client*, struct Client*, int, char*[]);
++extern int mh_ison(struct Client*, struct Client*, int, char*[]);
++extern int mh_join(struct Client*, struct Client*, int, char*[]);
++extern int mh_jupe(struct Client*, struct Client*, int, char*[]);
++extern int mh_kick(struct Client*, struct Client*, int, char*[]);
++extern int mh_kill(struct Client*, struct Client*, int, char*[]);
++extern int mh_links(struct Client*, struct Client*, int, char*[]);
++extern int mh_list(struct Client*, struct Client*, int, char*[]);
++extern int mh_lusers(struct Client*, struct Client*, int, char*[]);
++extern int mh_map(struct Client*, struct Client*, int, char*[]);
++extern int mh_mode(struct Client*, struct Client*, int, char*[]);
++extern int mh_motd(struct Client*, struct Client*, int, char*[]);
++extern int mh_names(struct Client*, struct Client*, int, char*[]);
++extern int mh_nick(struct Client*, struct Client*, int, char*[]);
++extern int mh_notice(struct Client*, struct Client*, int, char*[]);
++extern int mh_oper(struct Client*, struct Client*, int, char*[]);
++extern int mh_opmode(struct Client*, struct Client*, int, char*[]);
++extern int mh_part(struct Client*, struct Client*, int, char*[]);
++extern int mh_pass(struct Client*, struct Client*, int, char*[]);
++extern int mh_ping(struct Client*, struct Client*, int, char*[]);
++extern int mh_pong(struct Client*, struct Client*, int, char*[]);
++extern int mh_privmsg(struct Client*, struct Client*, int, char*[]);
++extern int mh_privs(struct Client*, struct Client*, int, char*[]);
++extern int mh_quit(struct Client*, struct Client*, int, char*[]);
++extern int mh_rehash(struct Client*, struct Client*, int, char*[]);
++extern int mh_reset(struct Client*, struct Client*, int, char*[]);
++extern int mh_restart(struct Client*, struct Client*, int, char*[]);
++extern int mh_rping(struct Client*, struct Client*, int, char*[]);
++extern int mh_set(struct Client*, struct Client*, int, char*[]);
++extern int mh_sethost(struct Client*, struct Client*, int, char*[]);
++extern int mh_settime(struct Client*, struct Client*, int, char*[]);
++extern int mh_silence(struct Client*, struct Client*, int, char*[]);
++extern int mh_squit(struct Client*, struct Client*, int, char*[]);
++extern int mh_stats(struct Client*, struct Client*, int, char*[]);
++extern int mh_time(struct Client*, struct Client*, int, char*[]);
++extern int mh_topic(struct Client*, struct Client*, int, char*[]);
++extern int mh_trace(struct Client*, struct Client*, int, char*[]);
++extern int mh_uping(struct Client*, struct Client*, int, char*[]);
++extern int mh_user(struct Client*, struct Client*, int, char*[]);
++extern int mh_userhost(struct Client*, struct Client*, int, char*[]);
++extern int mh_userip(struct Client*, struct Client*, int, char*[]);
++extern int mh_version(struct Client*, struct Client*, int, char*[]);
++extern int mh_wallchops(struct Client*, struct Client*, int, char*[]);
++extern int mh_wallops(struct Client*, struct Client*, int, char*[]);
++extern int mh_wallusers(struct Client*, struct Client*, int, char*[]);
++extern int mh_wallvoices(struct Client*, struct Client*, int, char*[]);
++extern int mh_who(struct Client*, struct Client*, int, char*[]);
++extern int mh_whois(struct Client*, struct Client*, int, char*[]);
++extern int mh_whowas(struct Client*, struct Client*, int, char*[]);
++
++
++
++
++
+ #endif /* INCLUDED_handlers_h */
+diff -r 7fa4708bac45 include/ircd_handler.h
+--- a/include/ircd_handler.h   Sun Jan 25 12:48:01 2009 +0100
++++ b/include/ircd_handler.h   Sun Jan 25 23:52:18 2009 +0100
+@@ -36,6 +36,7 @@
+   SERVER_HANDLER,       /**< Used for server conections. */
+   OPER_HANDLER,         /**< Used for IRC operators. */
+   SERVICE_HANDLER,      /**< Used for services connections. */
++  HELP_HANDLER,         /**< Used for /HELP <command> */
+   LAST_HANDLER_TYPE     /**< NUmber of handler types. */
+ } HandlerType;
+diff -r 7fa4708bac45 include/msg.h
+--- a/include/msg.h    Sun Jan 25 12:48:01 2009 +0100
++++ b/include/msg.h    Sun Jan 25 23:52:18 2009 +0100
+@@ -410,7 +410,7 @@
+    * parv = parameter variable array
+    */
+   /* handlers:
+-   * UNREGISTERED, CLIENT, SERVER, OPER, SERVICE, LAST
++   * UNREGISTERED, CLIENT, SERVER, OPER, SERVICE, HELP, LAST
+    */
+   MessageHandler handlers[LAST_HANDLER_TYPE];
+ };
+diff -r 7fa4708bac45 include/numeric.h
+--- a/include/numeric.h        Sun Jan 25 12:48:01 2009 +0100
++++ b/include/numeric.h        Sun Jan 25 23:52:18 2009 +0100
+@@ -475,7 +475,6 @@
+ /*      ERR_NOMANAGER_LONG   565      no longer used */
+ #define ERR_NOMANAGER        566      /* Undernet extension */
+ #define ERR_UPASS_SAME_APASS 567        /* Undernet extension */
+-#define ERR_LASTERROR        568
+ /*    RPL_LOGON            600        dalnet,unreal
+       RPL_LOGOFF           601        dalnet,unreal
+@@ -499,4 +498,10 @@
+       RPL_DUMPRPL          641        unreal
+       RPL_EODUMP           642        unreal
+ */
++#define RPL_HELP             601  /* QuakeNet extension */
++#define RPL_ENDOFHELP        602  /* QuakeNet extension */
++#define ERR_NOHELP           603  /* QuakeNet extension */
++
++#define ERR_LASTERROR        604
++
+ #endif /* INCLUDED_numeric_h */
+diff -r 7fa4708bac45 include/parse.h
+--- a/include/parse.h  Sun Jan 25 12:48:01 2009 +0100
++++ b/include/parse.h  Sun Jan 25 23:52:18 2009 +0100
+@@ -15,6 +15,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 struct Message *find_message_by_cmd(char *cmd);
+ extern int register_mapping(struct s_map *map);
+ extern int unregister_mapping(struct s_map *map);
+diff -r 7fa4708bac45 ircd/m_defaults.c
+--- a/ircd/m_defaults.c        Sun Jan 25 12:48:01 2009 +0100
++++ b/ircd/m_defaults.c        Sun Jan 25 23:52:18 2009 +0100
+@@ -119,3 +119,18 @@
+ {
+   return 0;
+ }
++
++int mh_nohelp(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return send_reply(sptr, ERR_NOHELP, parv[1]);
++}
++
++int mh_not_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return send_reply(sptr, SND_EXPLICIT | RPL_HELP, "%s :This command is for IRC Operators only.", parv[1]);
++}
++
++int mh_not_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return send_reply(sptr, SND_EXPLICIT | RPL_HELP, "%s :This command is for servers only.", parv[1]);
++}
+\ No newline at end of file
+diff -r 7fa4708bac45 ircd/m_help.c
+--- a/ircd/m_help.c    Sun Jan 25 12:48:01 2009 +0100
++++ b/ircd/m_help.c    Sun Jan 25 23:52:18 2009 +0100
+@@ -90,6 +90,7 @@
+ #include "msg.h"
+ #include "numeric.h"
+ #include "numnicks.h"
++#include "parse.h"
+ #include "send.h"
+ /* #include <assert.h> -- Now using assert in ircd_log.h */
+@@ -100,9 +101,1124 @@
  int m_help(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
  {
    int i;
--
-+/* check here for param and find command, then call the function set for that entry? */
-   for (i = 0; msgtab[i].cmd; i++)
-     sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, msgtab[i].cmd);
++  struct Message* mptr;
++  MessageHandler handler = 0;
++  char *command;
+-  for (i = 0; msgtab[i].cmd; i++)
+-    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, msgtab[i].cmd);
++  /* list all commands */
++  if (parc < 2) {
++    for (i = 0; msgtab[i].cmd; i++)
++      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, msgtab[i].cmd);
++    return 0;
++  }
++  command = parv[1];
++
++  /* nothing found */
++  if ((mptr = find_message_by_cmd(command)) == NULL)
++    return send_reply(sptr, ERR_NOHELP, command);
++
++  /* found a help handler function */
++  handler = mptr->handlers[HELP_HANDLER];
++
++  assert(0 != handler);
++  
++  /* call the help handler function */
++  (*handler) (cptr, sptr, parc, parv);
++  return send_reply(sptr, RPL_ENDOFHELP, command);
++}
++
++
++/*
++ * mh_help - help message handler
++ */
++int mh_help(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "HELP :HELP [<command>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "HELP :Lists all server commands or info on the given command.");
    return 0;
-diff -r ce13b0a1253e ircd/parse.c
---- a/ircd/parse.c     Wed Jan 14 14:19:43 2009 +0100
-+++ b/ircd/parse.c     Wed Jan 14 15:01:46 2009 +0100
-@@ -105,6 +105,7 @@
- static struct MessageTree tok_tree;
+ }
  
- /** Array of all supported commands. */
-+/* add mh_<command> here? mh = message help */
- struct Message msgtab[] = {
-   {
++
++/*
++ * TODO: move all the mh_ functions into the files of the commands they belong to
++ */
++
++
++/*
++ * mh_admin - help message handler
++ * TODO: check HIS remote
++ */
++int mh_admin(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "ADMIN :ADMIN");
++  else
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "ADMIN :ADMIN [<server>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ADMIN :Shows administrative contact for the server.");
++  return 0;
++}
++
++
++/*
++ * mh_asll - help message handler
++ */
++int mh_asll(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ASLL :ASLL <mask> [<server>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ASLL :Shows Asymmetric Link Latency information.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ASLL :Parameter <mask> is matched against directly linked servers, and may have more than one match.");
++  return 0;
++}
++
++
++/*
++ * mh_away - help message handler
++ */
++int mh_away(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "AWAY :AWAY [<message>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "AWAY :Sets or clears the away message.");
++  return 0;
++}
++
++
++/*
++ * mh_check - help message handler
++ */
++int mh_check(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CHECK :CHECK [<server>] <mask> [<flags>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CHECK :Shows detailed information about a user, a channel, a hostmask, or a server.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CHECK :Flags are: -C show clones, -c show channels, -s show servernames, -e show more, "
++    "-i show IPs, -I show hostnames and IPs, -u do not show users, -o show only channel operators.");
++  return 0;
++}
++
++
++/*
++ * mh_clearmode - help message handler
++ */
++int mh_clearmode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLEARMODE :CLEARMODE [!]<channel> [<modes>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLEARMODE :Clears modes on a channel.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLEARMODE :Default modes are ovpsmikbl - o deops all channel operators, "
++    "v devoices all channel voices, and b removes all channel bans.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLEARMODE :The ! prefix may be used to override a channel quarantine.");
++  return 0;
++}
++
++
++/*
++ * mh_close - help message handler
++ */
++int mh_close(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLOSE :CLOSE");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CLOSE :Closes unregistered connections to the server.");
++  return 0;
++}
++
++
++/*
++ * mh_cnotice - help message handler
++ */
++int mh_cnotice(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CNOTICE :CNOTICE <nick> <channel> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CNOTICE :Sends a private notice to a user.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CNOTICE :A channel voice or operator can use this to bypass the target flood limit "
++    "(target change too fast error) when the user is on the channel.");
++  return 0;
++}
++
++
++/*
++ * mh_connect - help message handler
++ */
++int mh_connect(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CONNECT :CONNECT <server> [<port> [<remote server>]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CONNECT :Connects the server to another server.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CONNECT :Special port 0 can be used to let the remote server workout the port to use.");
++  return 0;
++}
++
++
++/*
++ * mh_cprivmsg - help message handler
++ */
++int mh_cprivmsg(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CPRIVMSG :CPRIVMSG <nick> <channel> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CPRIVMSG :Sends a private message to a user.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "CPRIVMSG :A channel voice or operator can use this to bypass the target flood limit "
++    "(target change too fast error) when the user is on the channel.");
++  return 0;
++}
++
++
++/*
++ * mh_die - help message handler
++ */
++int mh_die(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "DIE :DIE <server>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "DIE :Terminates the server. The servername needs to be given as sanity check.");
++  return 0;
++}
++
++
++/*
++ * mh_get - help message handler
++ * TODO: check HIS
++ */
++int mh_get(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "GET :GET <feature> [<parameters>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "GET :Returns the value of a feature.");
++  return 0;
++}
++
++
++/*
++ * mh_gline - help message handler
++ * TODO: check HIS
++ */
++int mh_gline(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "GLINE :GLINE [[!][+|-|>|<]<mask> [<target>] [<duration> [:<reason>]]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "GLINE :Views, sets or changes a gline.");
++  return 0;
++}
++
++
++/*
++ * mh_hash - help message handler
++ * TODO: check HIS
++ */
++int mh_hash(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "HASH :HASH");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "HASH :Shows hash table statistics.");
++  return 0;
++}
++
++
++/*
++ * mh_info - help message handler
++ * TODO: check HIS remote
++ */
++int mh_info(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP, "INFO :INFO");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP, "INFO :Shows info about the IRCd.");
++    return mh_not_oper(cptr, sptr, parc, parv);
++  } else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP, "INFO :INFO [<server>]");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "INFO :Show info about the IRCd. Lists a file hash and version number for its source files.");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_invite - help message handler
++ */
++int mh_invite(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "INVITE :INVITE <nick> <channel>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "INVITE :Invites a user to a channel.");
++  return 0;
++}
++
++
++/*
++ * mh_ison - help message handler
++ * TODO: check that syntax is clear - no limit on number of nicks
++ */
++int mh_ison(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ISON :ISON <nick> [<nick> [..]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "ISON :Returns the nicks that are on the network.");
++  return 0;
++}
++
++
++/*
++ * mh_join - help message handler
++ */
++int mh_join(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "JOIN :JOIN <channel> [<key>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "JOIN :Joins a channel, where <channel> and <key> are a comma separated list of one or more elements.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "JOIN :Joining channel 0 can be used to leave all channels.");
++  return 0;
++}
++
++
++/*
++ * mh_jupe - help message handler
++ * TODO: check HIS?
++ */
++int mh_jupe(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "JUPE :JUPE [[+|-]<server> [[<target>] <duration> :<reason>]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "JUPE :Views or manipulates server jupes.");
++  return 0;
++}
++
++
++/*
++ * mh_kick - help message handler
++ */
++int mh_kick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "KICK :KICK <channel> <nick> [:<reason>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "KICK :Kicks a user from a channel.");
++  return 0;
++}
++
++
++/*
++ * mh_kill - help message handler
++ */
++int mh_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "KILL :KILL <nick> :<reason>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "KILL :Disconnects a user from the network.");
++  return 0;
++}
++
++
++/*
++ * mh_links - help message handler
++ * TODO: check HIS feature
++ */
++int mh_links(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LINKS :LINKS [[<remote server>] <mask>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LINKS :Shows server links on the network.");
++  return 0;
++}
++
++
++/*
++ * mh_list - help message handler
++ */
++int mh_list(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LIST :LIST :[<channel>|<parameters>|STOP]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LIST :Lists channels on the network. See \"/QUOTE LIST :\" for more info.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LIST :Parameter <channel> is a comma separated list of one or more channels.");
++  return 0;
++}
++
++
++/*
++ * mh_lusers - help message handler
++ * TODO: check HIS remote feature
++ */
++int mh_lusers(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "LUSERS :LUSERS");
++  else
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "LUSERS :LUSERS [<dummy> [<remote server>]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "LUSERS :Shows information about the number of users on the network.");
++  return 0;
++}
++
++
++/*
++ * mh_map - help message handler
++ * TODO: check HIS feature
++ */
++int mh_map(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "MAP :MAP [<mask>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "MAP :Shows a map of the servers on the network.");
++  return 0;
++}
++
++
++/*
++ * mh_mode - help message handler
++ * TODO: check syntax is clear
++ */
++int mh_mode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "MODE :MODE <channel>|<nick> [<modes>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "MODE :Views or changes modes of a channel or user.");
++  return 0;
++}
++
++
++/*
++ * mh_motd - help message handler
++ * TODO: check HIS remote feature
++ */
++int mh_motd(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "MOTD :MOTD");
++  else
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "MOTD :MOTD [<server>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "MOTD :Shows the Message Of The Day.");
++  return 0;
++}
++
++
++/*
++ * mh_names - help message handler
++ * TODO: check HIS remote feature
++ */
++int mh_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "NAMES :NAMES [[-D] <channel>]");
++  else
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "NAMES :NAMES [[[-D] [channel]] [<server>]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NAMES :Shows the names of users on the channel.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NAMES :Parameter <channel> is a comma separated list of one or more channels,"
++    " and the -D option is for listing delayedjoin users.");
++  return 0;
++}
++
++
++/*
++ * mh_nick - help message handler
++ */
++int mh_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NICK :NICK <newnick>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NICK :Changes nickname.");
++  return 0;
++}
++
++
++/*
++ * mh_notice - help message handler
++ */
++int mh_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NOTICE :NOTICE <target> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NOTICE :Sends a notice to a target, where <target> can be a comma separated list of one or more nicks or channels.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NOTICE :Parameter <target> can be @<channel> to send a message to all channel operators (same as WALLCHOPS).");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "NOTICE :Parameter <target> can be in the form of <nick>@<server> to send to a service.");
++  if (IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "NOTICE :Parameter <target> can be a $<servername> to broadcast a message to all users on a server.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "NOTICE :Parameter <target> can be a $@<hostmask> to broadcast a message to all users with a matching host.");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_oper - help message handler
++ */
++int mh_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "OPER :OPER <username> <password>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "OPER :Logs in as IRC Operator.");
++  return 0;
++}
++
++
++/*
++ * mh_opmode - help message handler
++ */
++int mh_opmode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "OPMODE :OPMODE [!]<channel> <modes>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "OPMODE :Changes modes on a channel.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "OPMODE :The ! prefix may be used to override a channel quarantine.");
++  return 0;
++}
++
++/*
++ * mh_part - help message handler
++ */
++int mh_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PART :PART <channel> [:<message>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PART :Parts a channel with an optional message.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PART :Parameter <channel> is a comma separated list of one or more channels.");
++  return 0;
++}
++
++
++/*
++ * mh_pass - help message handler
++ */
++int mh_pass(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PASS :PASS :<password>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PASS :Provides the password required to connect to the server.");
++  return 0;
++}
++
++
++/*
++ * mh_ping - help message handler
++ * TODO: <text> ?
++ */
++int mh_ping(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PING :PING :<text>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PING :Sends a ping to the server.");
++  return 0;
++}
++
++
++/*
++ * mh_pong - help message handler
++ * TODO: <text> ?
++ */
++int mh_pong(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PONG :PONG :<text>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PONG :Sends a pong as reply to a PING from the server.");
++  return 0;
++}
++
++
++/*
++ * mh_privmsg - help message handler
++ */
++int mh_privmsg(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVMSG :PRIVMSG <target> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVMSG :Sends a message to a target, where <target> can be a comma separated list of one or more nicks or channels.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVMSG :Parameter <target> can be @<channel> to send a message to all channel operators (same as WALLCHOPS).");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVMSG :Parameter <target> can be in the form of <nick>@<server> to send to a service.");
++  if (IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "PRIVMSG :Parameter <target> can be a $<servername> to broadcast a message to all users on a server.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "PRIVMSG :Parameter <target> can be a $@<hostmask> to broadcast a message to all users with a matching host.");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_privs - help message handler
++ */
++int mh_privs(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVS :PRIVS <nick> [<nick> [..]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "PRIVS :Shows the privileges an IRC Operator has.");
++  return 0;
++}
++
++
++/*
++ * mh_quit - help message handler
++ * TODO: Disconnect or Disconnects?
++ */
++int mh_quit(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "QUIT :QUIT :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "QUIT :Disconnect from the network with an optional message.");
++  return 0;
++}
++
++
++/*
++ * mh_rehash - help message handler
++ * TODO: check the options, etc.
++ */
++int mh_rehash(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "REHASH :REHASH [L|M|Q]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "REHASH :Reloads the server conf file, MOTD, and log files.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "REHASH :Where L reopens the log files, M flushes the MOTD cache, Q the DNS resolver is not restarted.");
++  return 0;
++}
++
++
++/*
++ * mh_reset - help message handler
++ */
++int mh_reset(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RESET :RESET <feature> [<parameters>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RESET :Resets a feature to its default hardcoded value.");
++  return 0;
++}
++
++
++/*
++ * mh_restart - help message handler
++ */
++int mh_restart(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RESTART :RESTART <server>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RESTART :Restarts the server. The servername needs to be given as sanity check.");
++  return 0;
++}
++
++
++/*
++ * mh_rping - help message handler
++ * TODO: <text> ?
++ */
++int mh_rping(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RPING :RPING <server> [<remote server>] [:<text>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "RPING :Pings a remote server and returns the Round Trip Time in milliseconds.");
++  return 0;
++}
++
++
++/*
++ * mh_set - help message handler
++ */
++int mh_set(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SET :SET <feature> [<parameters>] :<value>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SET :Changes the value of a feature.");
++  return 0;
++}
++
++
++/*
++ * mh_sethost - help message handler
++ */
++int mh_sethost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "SETHOST :SETHOST <spoofhost> <password>");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "SETHOST :Sets a spoof host.");
++  } else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "SETHOST :SETHOST <user> <host>");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "SETHOST :Sets a spoof user@host.");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_settime - help message handler
++ */
++int mh_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SETTIME :SETTIME <timestamp> [<server>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SETTIME :Changes the time on a server."
++    "When parameter <timestamp> is 0 the local server "
++    "uses its own timestamp before sending it to the remote server.");
++  return 0;
++}
++
++
++/*
++ * mh_silence - help message handler
++ * TODO: mention ~ exempt mask prefix
++ */
++int mh_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SILENCE :SILENCE [[+|-]<mask>|<nick>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SILENCE :Views or changes the silence list.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SILENCE :Parameter <mask> is a comma separated list of one or more masks.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SILENCE :Silence is a server side ignore -"
++    " any message sent privately or invites from a user matching a silence mask is dropped.");
++  return 0;
++}
++
++
++/*
++ * mh_squit - help message handler
++ */
++int mh_squit(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SQUIT :SQUIT <server> [:<reason>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "SQUIT :Disconnects a server from the network.");
++  return 0;
++}
++
++
++/*
++ * mh_stats - help message handler
++ * TODO: check HIS (too many per stats type) / HIS remote?
++ */
++int mh_stats(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "STATS :STATS [<type> [[<server>] [<mask>]]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "STATS :Shows server configuration and statistics.");
++  return 0;
++}
++
++
++/*
++ * mh_time - help message handler
++ * TODO: check HIS remote feature
++ */
++int mh_time(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "TIME :TIME");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "TIME :Shows the time on the server.");
++  }
++  else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "TIME :TIME [<server>]");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "TIME :Shows the time on the server.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "TIME :Parameter <server> can be a * to view the time from all servers.");
++  }
++
++  return 0;
++}
++
++
++/*
++ * mh_topic - help message handler
++ */
++int mh_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "TOPIC :TOPIC <channel> [:<text>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "TOPIC :Views or changes the topic on a channel.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "TOPIC :Parameter <channel> is a comma separated list of one or more channels.");
++  return 0;
++}
++
++
++/*
++ * mh_trace - help message handler
++ * TODO: check HIS / HIS remote?
++ * TODO: check <to server> parameter
++ */
++int mh_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "TRACE :TRACE [<nick>|<server> <to server>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "TRACE :Shows the route to a user or server.");
++  return 0;
++}
++
++
++/*
++ * mh_uping - help message handler
++ * TODO: default port 7007, define UDP_PORT
++ */
++int mh_uping(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "UPING :UPING <server> [<port>] [<remote server>] [<number of packets>]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "UPING :Pings the host of the server with UDP packets - server does not have to be linked.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "UPING :By default port 7007 is used, and 5 packets are sent (maximum is 20).");
++  return 0;
++}
++
++
++/*
++ * mh_user - help message handler
++ */
++int mh_user(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USER :USER <username> <hostname> <servername> :<realname>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USER :Registers a new client connection. Parameters <hostname> and <servername> are ignored.");
++  return 0;
++}
++
++
++/*
++ * mh_userhost - help message handler
++ * TODO: syntax clear?
++ * TODO: max 5
++ */
++int mh_userhost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USERHOST :USERHOST <nick> [<nick> [..]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USERHOST :Returns the user@host for a user.");
++  return 0;
++}
++
++
++/*
++ * mh_userip - help message handler
++ * TODO: syntax clear?
++ * TODO: max 5
++ */
++int mh_userip(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USERIP :USERIP <nick> [<nick> [..]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "USERIP :Returns the user@ip for a user.");
++  return 0;
++}
++
++
++/*
++ * mh_version - help message handler
++ * TODO: check HIS remote feature
++ */
++int mh_version(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "VERSION :VERSION");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "VERSION :Shows information about the server, and the supported features and settings.");
++  }
++  else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "VERSION :VERSION [<server>]");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "VERSION :Shows information about the server, and the supported features and settings.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "VERSION :Parameter <server> can be a * to view the version info from all servers.");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_wallchops - help message handler
++ * TODO: mention NOTICE @#channel?
++ */
++int mh_wallchops(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLCHOPS :WALLCHOPS <channel> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLCHOPS :Sends a channel notice to the operators on a channel.");
++  return 0;
++}
++
++
++/*
++ * mh_wallops - help message handler
++ * TODO: check HIS ?
++ */
++int mh_wallops(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLOPS :WALLOPS :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLOPS :Sends a message to all IRC Operators with usermode +w set.");
++  return 0;
++}
++
++
++/*
++ * mh_wallusers - help message handler
++ */
++int mh_wallusers(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr))
++    return mh_not_oper(cptr, sptr, parc, parv);
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLUSERS :WALLUSERS :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLUSERS :Sends a message to all users with usermode +w set.");
++  return 0;
++}
++
++
++/*
++ * mh_wallvoices - help message handler
++ */
++int mh_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLVOICES :WALLVOICES <channel> :<message>");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WALLVOICES :Sends a channel notice to the voices and operators on a channel.");
++  return 0;
++}
++
++
++/*
++ * mh_who - help message handler
++ * TODO: this is all way too much to add here.. :(
++ * TODO: mention <mask> - chan, nick, user, host, ip, server, realname
++ * TODO: filter
++d   Join-delayed channel members
++o   IRC Operator (specifying this one means only opers are matched)
++
++ * TODO: x for operators
++ 
++ * TODO: match
++n   Nick, u   Username, h   Hostname, i   Numeric IP,
++s   Servername, r   Realname, a   Account name
++
++
++ * TODO: return
++t   Include the querytype in the reply
++c   Include (first) channel name (* when none can be shown)
++u   Include userID with eventual ~
++i   Include IP
++h   Include hostname
++s   Include server name
++n   Include nick
++f   Include flags
++d   Include "distance" in hops
++l   Include idle time (0 for remote users)
++a   Include account name (0 when the user has no account set)
++o   Include oplevel
++r   Include real name
++b   Include account ID
++
++ * TODO: HIS settings
++ */
++int mh_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WHO :WHO <mask> [[<match>][%<fields>[,<querytype>]] [:<mask>]]");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WHO :Returns information on a user.");
++  send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++    "WHO :Parameter <mask> can be a comma separated list of one or more nicks and channels.");
++  return 0;
++}
++
++
++/*
++ * mh_whois - help message handler
++ * TODO: check HIS / HIS remote
++ */
++int mh_whois(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :WHOIS [<dummy>] <nick>");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :Shows who someone is.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :Parameter <nick> is a comma separated list of one or more nicks,"
++      " <dummy> can be specified to get a reply from the user's server to show idle and signon time");
++  }
++  else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :WHOIS [<server>] <nick>");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :Shows who someone is.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOIS :Parameter <nick> is a comma separated list of one or more nicks,"
++      " the same nick can be specified as <server> to get a reply from the user's server to show idle and signon time");
++  }
++  return 0;
++}
++
++
++/*
++ * mh_whowas - help message handler
++ * TODO: check HIS / HIS remote
++ * TODO: see if 20 is a define
++ */
++int mh_whowas(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  if (!IsAnOper(sptr)) {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :WHOWAS <nick> [<max>]");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :Shows who someone was.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :Parameter <nick> is a comma separated list of one or more nicks,"
++      " <max> is 0 by default (unlimited).");
++  }
++  else {
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :WHOIS <nick> [<max> [<server>]]");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :Shows who someone was.");
++    send_reply(sptr, SND_EXPLICIT | RPL_HELP,
++      "WHOWAS :Parameter <nick> is a comma separated list of one or more nicks,"
++      " <max> is 0 by default (unlimited),"
++      " maximum number of results from a remote server is 20.");
++  }
++  return 0;
++}
+\ No newline at end of file
+diff -r 7fa4708bac45 ircd/parse.c
+--- a/ircd/parse.c     Sun Jan 25 12:48:01 2009 +0100
++++ b/ircd/parse.c     Sun Jan 25 23:52:18 2009 +0100
+@@ -110,534 +110,534 @@
      MSG_PRIVATE,
+     TOK_PRIVATE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore, mh_privmsg }
+   },
+   {
+     MSG_NICK,
+     TOK_NICK,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_nick, m_nick, ms_nick, m_nick, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_nick, m_nick, ms_nick, m_nick, m_ignore, mh_nick }
+   },
+   {
+     MSG_NOTICE,
+     TOK_NOTICE,
+     0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_notice, ms_notice, mo_notice, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_notice, ms_notice, mo_notice, m_ignore, mh_notice }
+   },
+   {
+     MSG_WALLCHOPS,
+     TOK_WALLCHOPS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore, mh_wallchops }
+   },
+   {
+     MSG_WALLVOICES,
+     TOK_WALLVOICES,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore, mh_wallvoices }
+   },
+   {
+     MSG_CPRIVMSG,
+     TOK_CPRIVMSG,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore, mh_cprivmsg }
+   },
+   {
+     MSG_CNOTICE,
+     TOK_CNOTICE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore, mh_cnotice }
+   },
+   {
+     MSG_JOIN,
+     TOK_JOIN,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_join, ms_join, m_join, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_join, ms_join, m_join, m_ignore, mh_join }
+   },
+   {
+     MSG_MODE,
+     TOK_MODE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_mode, ms_mode, m_mode, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_mode, ms_mode, m_mode, m_ignore, mh_mode }
+   },
+   {
+     MSG_BURST,
+     TOK_BURST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_burst, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_burst, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_CREATE,
+     TOK_CREATE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_create, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_create, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_DESTRUCT,
+     TOK_DESTRUCT,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_destruct, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_destruct, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_QUIT,
+     TOK_QUIT,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_quit, m_quit, ms_quit, m_quit, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_quit, m_quit, ms_quit, m_quit, m_ignore, mh_quit }
+   },
+   {
+     MSG_PART,
+     TOK_PART,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_part, ms_part, m_part, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_part, ms_part, m_part, m_ignore, mh_part }
+   },
+   {
+     MSG_TOPIC,
+     TOK_TOPIC,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_topic, ms_topic, m_topic, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_topic, ms_topic, m_topic, m_ignore, mh_topic }
+   },
+   {
+     MSG_INVITE,
+     TOK_INVITE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_invite, ms_invite, m_invite, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_invite, ms_invite, m_invite, m_ignore, mh_invite }
+   },
+   {
+     MSG_KICK,
+     TOK_KICK,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_kick, ms_kick, m_kick, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_kick, ms_kick, m_kick, m_ignore, mh_kick }
+   },
+   {
+     MSG_WALLOPS,
+     TOK_WALLOPS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore, mh_wallops }
+   },
+   {
+     MSG_WALLUSERS,
+     TOK_WALLUSERS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore, mh_wallusers }
+   },
+   {
+     MSG_DESYNCH,
+     TOK_DESYNCH,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_PING,
+     TOK_PING,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_ping, ms_ping, mo_ping, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_ping, ms_ping, mo_ping, m_ignore, mh_ping }
+   },
+   {
+     MSG_PONG,
+     TOK_PONG,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { mr_pong, m_pong, ms_pong, m_pong, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { mr_pong, m_pong, ms_pong, m_pong, m_ignore, mh_pong }
+   },
+   {
+     MSG_ERROR,
+     TOK_ERROR,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { mr_error, m_ignore, ms_error, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { mr_error, m_ignore, ms_error, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_KILL,
+     TOK_KILL,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore, mh_kill }
+   },
+   {
+     MSG_USER,
+     TOK_USER,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_user, m_registered, m_ignore, m_registered, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_user, m_registered, m_ignore, m_registered, m_ignore, mh_user }
+   },
+   {
+     MSG_AWAY,
+     TOK_AWAY,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_away, ms_away, m_away, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_away, ms_away, m_away, m_ignore, mh_away }
+   },
+   {
+     MSG_ISON,
+     TOK_ISON,
+     0, 1, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_ison, m_ignore, m_ison, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_ison, m_ignore, m_ison, m_ignore, mh_ison }
+   },
+   {
+     MSG_SERVER,
+     TOK_SERVER,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { mr_server, m_registered, ms_server, m_registered, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { mr_server, m_registered, ms_server, m_registered, m_ignore, mh_not_server }
+   },
+   {
+     MSG_SQUIT,
+     TOK_SQUIT,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_squit, mo_squit, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_squit, mo_squit, m_ignore, mh_squit }
+   },
+   {
+     MSG_WHOIS,
+     TOK_WHOIS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_whois, ms_whois, m_whois, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_whois, ms_whois, m_whois, m_ignore, mh_whois }
+   },
+   {
+     MSG_WHO,
+     TOK_WHO,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_who, m_ignore, m_who, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_who, m_ignore, m_who, m_ignore, mh_who }
+   },
+   {
+     MSG_WHOWAS,
+     TOK_WHOWAS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore, mh_whowas }
+   },
+   {
+     MSG_LIST,
+     TOK_LIST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_list, m_ignore, m_list, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_list, m_ignore, m_list, m_ignore, mh_list }
+   },
+   {
+     MSG_NAMES,
+     TOK_NAMES,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_names, m_names, m_names, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_names, m_names, m_names, m_ignore, mh_names }
+   },
+   {
+     MSG_USERHOST,
+     TOK_USERHOST,
+     0, 1, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore, mh_userhost }
+   },
+   {
+     MSG_USERIP,
+     TOK_USERIP,
+     0, 1, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_userip, m_ignore, m_userip, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_userip, m_ignore, m_userip, m_ignore, mh_userip }
+   },
+   {
+     MSG_TRACE,
+     TOK_TRACE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_trace, ms_trace, mo_trace, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_trace, ms_trace, mo_trace, m_ignore, mh_trace }
+   },
+   {
+     MSG_PASS,
+     TOK_PASS,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { mr_pass, m_registered, m_ignore, m_registered, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { mr_pass, m_registered, m_ignore, m_registered, m_ignore, mh_pass }
+   },
+   {
+     MSG_LUSERS,
+     TOK_LUSERS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore, mh_lusers }
+   },
+   {
+     MSG_TIME,
+     TOK_TIME,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_time, m_time, m_time, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_time, m_time, m_time, m_ignore, mh_time }
+   },
+   {
+     MSG_SETTIME,
+     TOK_SETTIME,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_settime, mo_settime, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_settime, mo_settime, m_ignore, mh_settime }
+   },
+   {
+     MSG_RPING,
+     TOK_RPING,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_rping, mo_rping, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_rping, mo_rping, m_ignore, mh_rping }
+   },
+   {
+     MSG_RPONG,
+     TOK_RPONG,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_OPER,
+     TOK_OPER,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_oper, ms_oper, mo_oper, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_oper, ms_oper, mo_oper, m_ignore, mh_oper }
+   },
+   {
+     MSG_CONNECT,
+     TOK_CONNECT,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_connect, mo_connect, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_connect, mo_connect, m_ignore, mh_connect }
+   },
+   {
+     MSG_MAP,
+     TOK_MAP,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_map, m_ignore, m_map, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_map, m_ignore, m_map, m_ignore, mh_map }
+   },
+   {
+     MSG_VERSION,
+     TOK_VERSION,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_version, m_version, ms_version, mo_version, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_version, m_version, ms_version, mo_version, m_ignore, mh_version }
+   },
+   {
+     MSG_STATS,
+     TOK_STATS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_stats, m_stats, m_stats, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_stats, m_stats, m_stats, m_ignore, mh_stats }
+   },
+   {
+     MSG_LINKS,
+     TOK_LINKS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_links, ms_links, m_links, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_links, ms_links, m_links, m_ignore, mh_links }
+   },
+   {
+     MSG_ADMIN,
+     TOK_ADMIN,
+     0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0,  NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_admin, m_admin, ms_admin, mo_admin, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_admin, m_admin, ms_admin, mo_admin, m_ignore, mh_admin }
+   },
+   {
+     MSG_HELP,
+     TOK_HELP,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_help, m_ignore, m_help, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_help, m_ignore, m_help, m_ignore, mh_help }
+   },
+   {
+     MSG_INFO,
+     TOK_INFO,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_info, ms_info, mo_info, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_info, ms_info, mo_info, m_ignore, mh_info }
+   },
+   {
+     MSG_MOTD,
+     TOK_MOTD,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_motd, m_motd, m_motd, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_motd, m_motd, m_motd, m_ignore, mh_motd }
+   },
+   {
+     MSG_CLOSE,
+     TOK_CLOSE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_close, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_close, m_ignore, mh_close }
+   },
+   {
+     MSG_SILENCE,
+     TOK_SILENCE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_silence, ms_silence, m_silence, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_silence, ms_silence, m_silence, m_ignore, mh_silence }
+   },
+   {
+     MSG_GLINE,
+     TOK_GLINE,
+     0, MAXPARA,         0, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_gline, ms_gline, mo_gline, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_gline, ms_gline, mo_gline, m_ignore, mh_gline }
+   },
+   {
+     MSG_JUPE,
+     TOK_JUPE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore, mh_jupe }
+   },
+   {
+     MSG_OPMODE,
+     TOK_OPMODE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_opmode, mo_opmode, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_opmode, mo_opmode, m_ignore, mh_opmode }
+   },
+   {
+     MSG_CLEARMODE,
+     TOK_CLEARMODE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_clearmode, mo_clearmode, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_clearmode, mo_clearmode, m_ignore, mh_clearmode }
+   },
+   {
+     MSG_UPING,
+     TOK_UPING,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_uping, mo_uping, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_uping, mo_uping, m_ignore, mh_uping }
+   },
+   {
+     MSG_END_OF_BURST,
+     TOK_END_OF_BURST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_end_of_burst, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_end_of_burst, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_END_OF_BURST_ACK,
+     TOK_END_OF_BURST_ACK,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_end_of_burst_ack, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_end_of_burst_ack, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_REBURST,
+     TOK_REBURST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_reburst, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_reburst, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_HASH,
+     TOK_HASH,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_hash, m_hash, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_hash, m_hash, m_ignore, mh_hash }
+   },
+   {
+     MSG_REHASH,
+     TOK_REHASH,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_rehash, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_rehash, m_ignore, mh_rehash }
+   },
+   {
+     MSG_RESTART,
+     TOK_RESTART,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore, mh_restart }
+   },
+   {
+     MSG_DIE,
+     TOK_DIE,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore, mh_die }
+   },
+   {
+     MSG_PROTO,
+     TOK_PROTO,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_proto, m_proto, m_proto, m_proto, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_proto, m_proto, m_proto, m_proto, m_ignore, mh_nohelp }
+   },
+   {
+     MSG_SET,
+     TOK_SET,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_set, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_set, m_ignore, mh_set }
+   },
+   {
+     MSG_RESET,
+     TOK_RESET,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_reset, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_reset, m_ignore, mh_reset }
+   },
+   {
+     MSG_GET,
+     TOK_GET,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, m_ignore, mo_get, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, m_ignore, mo_get, m_ignore, mh_get }
+   },
+   {
+     MSG_PRIVS,
+     TOK_PRIVS,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_privs, mo_privs, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_privs, mo_privs, m_ignore, mh_privs }
+   },
+   {
+     MSG_ACCOUNT,
+     TOK_ACCOUNT,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_ignore, ms_account, m_ignore, m_ignore, mh_not_server }
+   },
+   {
+     MSG_ASLL,
+     TOK_ASLL,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore, mh_asll }
+    },
+   {
+     MSG_SETHOST,
+     TOK_SETHOST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_sethost, ms_sethost, m_sethost, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_sethost, ms_sethost, m_sethost, m_ignore, mh_sethost }
+   },
+ #if WE_HAVE_A_REAL_CAPABILITY_NOW
+   {
+     MSG_CAP,
+     TOK_CAP,
+     0, MAXPARA, 0, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_cap, m_cap, m_ignore, m_cap, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_cap, m_cap, m_ignore, m_cap, m_ignore, mh_nohelp }
+   },
+ #endif
+@@ -654,7 +654,7 @@
+     MSG_CHECK,
+     TOK_CHECK,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    { m_unregistered, m_not_oper, m_check, m_check, m_ignore }
++    { m_unregistered, m_not_oper, m_check, m_check, m_ignore, mh_check }
+   },
+   
+   /*
+@@ -664,8 +664,8 @@
+     MSG_OPKICK,
+     TOK_OPKICK,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_unregistered, m_not_oper, ms_opkick, mo_opkick, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_unregistered, m_not_oper, ms_opkick, mo_opkick, m_ignore, mh_nohelp }
+   },
+   /* This command is an alias for QUIT during the unregistered part of
+@@ -678,8 +678,8 @@
+     MSG_POST,
+     TOK_POST,
+     0, MAXPARA, MFLG_SLOW, 0, NULL,
+-    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+-    { m_quit, m_ignore, m_ignore, m_ignore, m_ignore }
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE, HELP */
++    { m_quit, m_ignore, m_ignore, m_ignore, m_ignore, mh_nohelp }
+   },
+   { 0 }
+ };
+@@ -779,6 +779,14 @@
+   return NULL;
+ }
++/** Look up a command in the message trie.
++ * @param cmd Text of command to look up.
++ * @return Pointer to matching message, or NULL if non exists.
++ */
++struct Message *find_message_by_cmd(char *cmd) {
++  return msg_tree_parse(cmd, &msg_tree);
++}
++
+ /** Registers a service mapping to the pseudocommand handler.
+  * @param[in] map Service mapping to add.
+  * @return Non-zero on success; zero if a command already used the name.
+@@ -806,6 +814,7 @@
+   msg->handlers[SERVER_HANDLER] = m_ignore;
+   msg->handlers[OPER_HANDLER]   = m_pseudo;
+   msg->handlers[SERVICE_HANDLER] = m_ignore;
++  msg->handlers[HELP_HANDLER] = mh_nohelp;
+   add_msg_element(&msg_tree, msg, msg->cmd);
+   map->msg = msg;
+diff -r 7fa4708bac45 ircd/s_err.c
+--- a/ircd/s_err.c     Sun Jan 25 12:48:01 2009 +0100
++++ b/ircd/s_err.c     Sun Jan 25 23:52:18 2009 +0100
+@@ -1230,6 +1230,16 @@
+ /* 598 */
+   { 0 },
+ /* 599 */
++  { 0 },
++/* 600 */
++  { 0 },
++/* 601 */
++  { RPL_HELP, 0, "601" },
++/* 602 */
++  { RPL_ENDOFHELP, "%s :End of /HELP report.", "602" },
++/* 603 */
++  { ERR_NOHELP, "%s :No help found.", "603" },
++/* 604 */
+   { 0 }
+ };
diff --git a/series b/series
index 8476a5bac3f24b5c07c654ca3fa459f1cc3642c5..dfe582ecde6d3b901363fb872339f2999a942b25 100644 (file)
--- a/series
+++ b/series
@@ -17,7 +17,7 @@ commonchansumode.patch
 remoteglinejupe.patch
 whoban.patch
 opername.patch
-cmdhelp.patch
+cmdhelp.patch #+help
 noticepluschan.patch
 whomatch.patch
 privlocalchan.patch
@@ -27,5 +27,6 @@ operglinenick.patch
 operping.patch
 minoplevel.patch
 oplevelforward.patch
+welcome.patch
 accountcollision.patch
 split.patch
diff --git a/welcome.patch b/welcome.patch
new file mode 100644 (file)
index 0000000..705d379
--- /dev/null
@@ -0,0 +1,689 @@
+diff -r a662d02e9a76 include/handlers.h
+--- a/include/handlers.h       Sat Jan 24 21:39:56 2009 +0100
++++ b/include/handlers.h       Sat Jan 24 22:38:10 2009 +0100
+@@ -151,6 +151,7 @@
+ extern int m_version(struct Client*, struct Client*, int, char*[]);
+ extern int m_wallchops(struct Client*, struct Client*, int, char*[]);
+ extern int m_wallvoices(struct Client*, struct Client*, int, char*[]);
++extern int m_welcome(struct Client*, struct Client*, int, char*[]);
+ extern int m_who(struct Client*, struct Client*, int, char*[]);
+ extern int m_whois(struct Client*, struct Client*, int, char*[]);
+ extern int m_whowas(struct Client*, struct Client*, int, char*[]);
+@@ -185,6 +186,7 @@
+ extern int mo_version(struct Client*, struct Client*, int, char*[]);
+ extern int mo_wallops(struct Client*, struct Client*, int, char*[]);
+ extern int mo_wallusers(struct Client*, struct Client*, int, char*[]);
++extern int mo_welcome(struct Client*, struct Client*, int, char*[]);
+ extern int mr_error(struct Client*, struct Client*, int, char*[]);
+ extern int mr_error(struct Client*, struct Client*, int, char*[]);
+ extern int mr_pong(struct Client*, struct Client*, int, char*[]);
+@@ -242,6 +244,7 @@
+ extern int ms_wallops(struct Client*, struct Client*, int, char*[]);
+ extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
+ extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
++extern int ms_welcome(struct Client*, struct Client*, int, char*[]);
+ extern int ms_whois(struct Client*, struct Client*, int, char*[]);
+ #endif /* INCLUDED_handlers_h */
+diff -r a662d02e9a76 include/msg.h
+--- a/include/msg.h    Sat Jan 24 21:39:56 2009 +0100
++++ b/include/msg.h    Sat Jan 24 22:38:10 2009 +0100
+@@ -195,6 +195,10 @@
+ #define MSG_NOTICE              "NOTICE"        /* NOTI */
+ #define TOK_NOTICE              "O"
+ #define CMD_NOTICE            MSG_NOTICE, TOK_NOTICE
++
++#define MSG_WELCOME             "WELCOME"       /* WELC */
++#define TOK_WELCOME             "WE"
++#define CMD_WELCOME           MSG_WELCOME, TOK_WELCOME
+ #define MSG_WALLCHOPS           "WALLCHOPS"     /* WC */
+ #define TOK_WALLCHOPS           "WC"
+diff -r a662d02e9a76 include/welcome.h
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/include/welcome.h        Sat Jan 24 22:38:10 2009 +0100
+@@ -0,0 +1,43 @@
++#ifndef INCLUDED_welcome_h
++#define INCLUDED_welcome_h
++/*
++ * IRC - Internet Relay Chat, include/welcome.h
++ * Copyright (C) 1990 Jarkko Oikarinen and
++ *                    University of Oulu, Computing Center
++ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
++ *
++ * 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 2, 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.
++ */
++/** @file
++ * @brief  Interface and declarations for welcome server handling.
++ * @version $Id: jupe.h 1208 2004-10-03 14:12:35Z entrope $
++ */
++#ifndef INCLUDED_sys_types_h
++#include <sys/types.h>
++#define INCLUDED_sys_types_h
++#endif
++
++
++
++
++
++extern int welcome_set(struct Client *cptr, struct Client *sptr,
++  char *reason, time_t lastmod);
++extern void welcome_burst(struct Client *cptr);
++extern int welcome_resend(struct Client *cptr);
++extern int welcome_list(struct Client *sptr);
++extern int welcome_memory_count(size_t *ju_size);
++
++#endif /* INCLUDED_welcome_h */
+\ No newline at end of file
+diff -r a662d02e9a76 ircd/m_welcome.c
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/ircd/m_welcome.c Sat Jan 24 22:38:10 2009 +0100
+@@ -0,0 +1,142 @@
++/*
++ * IRC - Internet Relay Chat, ircd/m_welcome.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.
++ *
++ * $Id: m_welcome.c 1903 2009-01-13 03:54:45Z entrope $
++ */
++
++/*
++ * m_functions execute protocol messages on this server:
++ *
++ *    cptr    is always NON-NULL, pointing to a *LOCAL* client
++ *            structure (with an open socket connected!). This
++ *            identifies the physical socket where the message
++ *            originated (or which caused the m_function to be
++ *            executed--some m_functions may call others...).
++ *
++ *    sptr    is the source of the message, defined by the
++ *            prefix part of the message if present. If not
++ *            or prefix not found, then sptr==cptr.
++ *
++ *            (!IsServer(cptr)) => (cptr == sptr), because
++ *            prefixes are taken *only* from servers...
++ *
++ *            (IsServer(cptr))
++ *                    (sptr == cptr) => the message didn't
++ *                    have the prefix.
++ *
++ *                    (sptr != cptr && IsServer(sptr) means
++ *                    the prefix specified servername. (?)
++ *
++ *                    (sptr != cptr && !IsServer(sptr) means
++ *                    that message originated from a remote
++ *                    user (not local).
++ *
++ *            combining
++ *
++ *            (!IsServer(sptr)) means that, sptr can safely
++ *            taken as defining the target structure of the
++ *            message in this server.
++ *
++ *    *Always* true (if 'parse' and others are working correct):
++ *
++ *    1)      sptr->from == cptr  (note: cptr->from == cptr)
++ *
++ *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
++ *            *cannot* be a local connection, unless it's
++ *            actually cptr!). [MyConnect(x) should probably
++ *            be defined as (x == x->from) --msa ]
++ *
++ *    parc    number of variable parameter strings (if zero,
++ *            parv is allowed to be NULL)
++ *
++ *    parv    a NULL terminated list of parameter pointers,
++ *
++ *                    parv[0], sender (prefix string), if not present
++ *                            this points to an empty string.
++ *                    parv[1]...parv[parc-1]
++ *                            pointers to additional parameters
++ *                    parv[parc] == NULL, *always*
++ *
++ *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
++ *                    non-NULL pointers.
++ */
++#include "config.h"
++
++#include "channel.h"
++#include "client.h"
++#include "hash.h"
++#include "ircd.h"
++#include "ircd_log.h"
++#include "ircd_reply.h"
++#include "ircd_string.h"
++#include "msg.h"
++#include "numeric.h"
++#include "numnicks.h"
++#include "s_user.h"
++#include "send.h"
++
++/* #include <assert.h> -- Now using assert in ircd_log.h */
++
++/*
++ * m_welcome - local generic message handler
++ *
++ * parv[0] = Send prefix
++ */
++int m_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return 0;
++}
++
++
++/*
++ * mo_welcome - oper message handler
++ *
++ * listing:
++ * parv[0] = Send prefix
++ *
++ * remote listing:
++ * parv[0] = Send prefix
++ * parv[1] = Target
++ *
++ * set global or on remote server:
++ * parv[0] = Send prefix
++ * parv[1] = Target: server or * for global
++ * parv[2] = Text
++ */
++int mo_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return 0;
++}
++
++
++/*
++ * ms_welcome - server message handler
++ *
++ * parv[0] = Send prefix
++ * parv[1] = Target: server numeric or * for global
++ * parv[2] = Timestamp
++ * parv[3] = Text
++ */
++int ms_welcome(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
++{
++  return 0;
++}
+diff -r a662d02e9a76 ircd/parse.c
+--- a/ircd/parse.c     Sat Jan 24 21:39:56 2009 +0100
++++ b/ircd/parse.c     Sat Jan 24 22:38:10 2009 +0100
+@@ -668,6 +668,15 @@
+     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+     { m_unregistered, m_not_oper, ms_opkick, mo_opkick, m_ignore }
+   },
++  
++  /* add command for WELCOME */
++  {
++    MSG_WELCOME,
++    TOK_WELCOME,
++    0, MAXPARA, MFLG_SLOW, 0, NULL,
++    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
++    { m_unregistered, m_welcome, ms_welcome, mo_welcome, m_ignore }
++  },
+   /* This command is an alias for QUIT during the unregistered part of
+    * of the server.  This is because someone jumping via a broken web
+diff -r a662d02e9a76 ircd/welcome.c
+--- /dev/null  Thu Jan 01 00:00:00 1970 +0000
++++ b/ircd/welcome.c   Sat Jan 24 22:38:10 2009 +0100
+@@ -0,0 +1,430 @@
++/*
++ * IRC - Internet Relay Chat, ircd/jupe.c
++ * Copyright (C) 1990 Jarkko Oikarinen and
++ *                    University of Oulu, Finland
++ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
++ *
++ * 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.
++ */
++/** @file
++ * @brief Implementation of juped server handling functions.
++ * @version $Id: jupe.c 1633 2006-03-25 03:46:56Z entrope $
++ */
++#include "config.h"
++
++#include "client.h"
++#include "hash.h"
++#include "ircd.h"
++#include "ircd_alloc.h"
++#include "ircd_features.h"
++#include "ircd_log.h"
++#include "ircd_reply.h"
++#include "ircd_string.h"
++#include "match.h"
++#include "msg.h"
++#include "numeric.h"
++#include "numnicks.h"
++#include "s_bsd.h"
++#include "s_misc.h"
++#include "send.h"
++#include "struct.h"
++#include "sys.h"    /* FALSE bleah */
++#include "welcome.h"
++
++/* #include <assert.h> -- Now using assert in ircd_log.h */
++#include <string.h>
++
++/** List of jupes. */
++static struct Jupe *GlobalJupeList = 0;
++
++/** Allocate a new jupe with the given parameters.
++ * @param[in] server Server name to jupe.
++ * @param[in] reason Reason for jupe.
++ * @param[in] expire Expiration time for jupe.
++ * @param[in] lastmod Last modification time for jupe.
++ * @param[in] flags Flags to set for the jupe.
++ */
++static struct Jupe *
++make_jupe(char *server, char *reason, time_t expire, time_t lastmod,
++        unsigned int flags)
++{
++  struct Jupe *ajupe;
++
++  ajupe = (struct Jupe*) MyMalloc(sizeof(struct Jupe)); /* alloc memory */
++  assert(0 != ajupe);
++
++  memset(ajupe, 0, sizeof(*ajupe));
++  DupString(ajupe->ju_server, server); /* copy vital information */
++  DupString(ajupe->ju_reason, reason);
++  ajupe->ju_expire = expire;
++  ajupe->ju_lastmod = lastmod;
++  ajupe->ju_flags = flags & JUPE_MASK; /* set jupe flags */
++
++  ajupe->ju_next = GlobalJupeList; /* link it into the list */
++  ajupe->ju_prev_p = &GlobalJupeList;
++  if (GlobalJupeList)
++    GlobalJupeList->ju_prev_p = &ajupe->ju_next;
++  GlobalJupeList = ajupe;
++
++  return ajupe;
++}
++
++/** Apply a jupe.
++ * @param[in] cptr Local client that sent us the jupe.
++ * @param[in] sptr Originator of the jupe.
++ * @param[in] jupe Jupe to check.
++ */
++static int
++do_jupe(struct Client *cptr, struct Client *sptr, struct Jupe *jupe)
++{
++  struct Client *acptr;
++
++  if (!JupeIsActive(jupe)) /* no action to be taken on inactive jupes */
++    return 0;
++
++  acptr = FindServer(jupe->ju_server);
++
++  /* server isn't online or isn't local or is me */
++  if (!acptr || !MyConnect(acptr) || IsMe(acptr))
++    return 0;
++
++  return exit_client_msg(cptr, acptr, &me, "Juped: %s", jupe->ju_reason);
++}
++
++/** Forward a jupe to another server.
++ * @param[in] cptr Local client that sent us the jupe.
++ * @param[in] sptr Originator of the jupe.
++ * @param[in] jupe Jupe to forward.
++ */
++static void
++propagate_jupe(struct Client *cptr, struct Client *sptr, struct Jupe *jupe)
++{
++  if (JupeIsLocal(jupe)) /* don't propagate local jupes */
++    return;
++
++  sendcmdto_serv_butone(sptr, CMD_JUPE, cptr, "* %c%s %Tu %Tu :%s",
++                      JupeIsRemActive(jupe) ? '+' : '-', jupe->ju_server,
++                      jupe->ju_expire - CurrentTime, jupe->ju_lastmod,
++                      jupe->ju_reason);
++}
++
++/** Add a new server jupe.
++ * @param[in] cptr Local client that sent us the jupe.
++ * @param[in] sptr Originator of the jupe.
++ * @param[in] server Server name to jupe.
++ * @param[in] reason Reason for the jupe.
++ * @param[in] expire Jupe duration in seconds.
++ * @param[in] lastmod Last modification timestamp (or NULL).
++ * @param[in] flags Flags to set on jupe.
++ * @return Zero, unless the jupe causes \a cptr to be SQUIT, in which
++ * case CPTR_KILLED.
++ */
++int
++jupe_add(struct Client *cptr, struct Client *sptr, char *server, char *reason,
++       time_t expire, time_t lastmod, unsigned int flags)
++{
++  struct Jupe *ajupe;
++
++  assert(0 != server);
++  assert(0 != reason);
++
++  /*
++   * You cannot set a negative (or zero) expire time, nor can you set an
++   * expiration time for greater than JUPE_MAX_EXPIRE.
++   */
++  if (expire <= 0 || expire > JUPE_MAX_EXPIRE) {
++    if (!IsServer(cptr) && MyConnect(cptr))
++      send_reply(cptr, ERR_BADEXPIRE, expire);
++    return 0;
++  }
++
++  expire += CurrentTime; /* convert from lifetime to timestamp */
++
++  /* Inform ops and log it */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s adding %sJUPE for %s, expiring at "
++                       "%Tu: %s",
++                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++                         get_client_name_and_opername(sptr) :
++                         cli_name((cli_user(sptr))->server),
++                     flags & JUPE_LOCAL ? "local " : "", server,
++                     expire + TSoffset, reason);
++
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++          "%#C adding %sJUPE for %s, expiring at %Tu: %s", sptr,
++          flags & JUPE_LOCAL ? "local " : "", server, expire + TSoffset,
++          reason);
++
++  /* local jupe set by remote user, inform oper of success */
++  if ((flags & JUPE_LOCAL) && IsUser(sptr) && !MyUser(sptr))
++    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s adding local JUPE for %s, expiring at %Tu: %s",
++      sptr, cli_name(sptr), server, expire + TSoffset, reason);
++
++  /* make the jupe */
++  ajupe = make_jupe(server, reason, expire, lastmod, flags);
++
++  propagate_jupe(cptr, sptr, ajupe);
++
++  return do_jupe(cptr, sptr, ajupe); /* remove server if necessary */
++}
++
++/** Activate a jupe, optionally changing its lastmod and flags.
++ * @param[in] cptr Local client that sent us the jupe.
++ * @param[in] sptr Originator of the jupe.
++ * @param[in] jupe Jupe to activate.
++ * @param[in] lastmod New timestamp for last modification of the jupe.
++ * @param[in] flags Flags to set on the jupe.
++ * @return Zero, unless the jupe causes \a cptr to be SQUIT, in which
++ * case CPTR_KILLED.
++ */
++int
++jupe_activate(struct Client *cptr, struct Client *sptr, struct Jupe *jupe,
++            time_t lastmod, unsigned int flags)
++{
++  unsigned int saveflags = 0;
++
++  assert(0 != jupe);
++
++  saveflags = jupe->ju_flags;
++
++  if (flags & JUPE_LOCAL)
++    jupe->ju_flags &= ~JUPE_LDEACT;
++  else {
++    jupe->ju_flags |= JUPE_ACTIVE;
++
++    if (jupe->ju_lastmod >= lastmod) /* force lastmod to increase */
++      jupe->ju_lastmod++;
++    else
++      jupe->ju_lastmod = lastmod;
++  }
++
++  if ((saveflags & JUPE_ACTMASK) == JUPE_ACTIVE)
++    return 0; /* was active to begin with */
++
++  /* Inform ops and log it */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s activating JUPE for %s, expiring "
++                     "at %Tu: %s",
++                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++                         get_client_name_and_opername(sptr) :
++                         cli_name((cli_user(sptr))->server),
++                     jupe->ju_server, jupe->ju_expire + TSoffset,
++                     jupe->ju_reason);
++
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++          "%#C activating JUPE for %s, expiring at %Tu: %s",sptr,
++          jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason);
++
++  if (!(flags & JUPE_LOCAL)) /* don't propagate local changes */
++    propagate_jupe(cptr, sptr, jupe);
++
++  return do_jupe(cptr, sptr, jupe);
++}
++
++/** Deactivate a jupe.
++ * @param[in] cptr Local client that sent us the jupe.
++ * @param[in] sptr Originator of the jupe.
++ * @param[in] jupe Jupe to deactivate.
++ * @param[in] lastmod New timestamp for last modification of the jupe.
++ * @param[in] flags Flags to set on the jupe.
++ * @return Zero.
++ */
++int
++jupe_deactivate(struct Client *cptr, struct Client *sptr, struct Jupe *jupe,
++              time_t lastmod, unsigned int flags)
++{
++  unsigned int saveflags = 0;
++
++  assert(0 != jupe);
++
++  saveflags = jupe->ju_flags;
++
++  if (!JupeIsLocal(jupe)) {
++    if (flags & JUPE_LOCAL)
++      jupe->ju_flags |= JUPE_LDEACT;
++    else {
++      jupe->ju_flags &= ~JUPE_ACTIVE;
++
++      if (jupe->ju_lastmod >= lastmod) /* force lastmod to increase */
++      jupe->ju_lastmod++;
++      else
++      jupe->ju_lastmod = lastmod;
++    }
++
++    if ((saveflags & JUPE_ACTMASK) != JUPE_ACTIVE)
++      return 0; /* was inactive to begin with */
++  }
++
++  /* Inform ops and log it */
++  sendto_opmask_butone(0, SNO_NETWORK, "%s %s JUPE for %s, expiring at %Tu: "
++                     "%s",
++                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
++                         get_client_name_and_opername(sptr) :
++                         cli_name((cli_user(sptr))->server),
++                     JupeIsLocal(jupe) ? "removing local" : "deactivating",
++                     jupe->ju_server, jupe->ju_expire + TSoffset,
++                     jupe->ju_reason);
++
++  log_write(LS_JUPE, L_INFO, LOG_NOSNOTICE,
++          "%#C %s JUPE for %s, expiring at %Tu: %s", sptr,
++          JupeIsLocal(jupe) ? "removing local" : "deactivating",
++          jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason);
++
++  /* local jupe removed by remote user, inform oper of success */
++  if ((flags & JUPE_LOCAL) && IsUser(sptr) && !MyUser(sptr))
++    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s removing local JUPE for %s, expiring at %Tu: %s",
++      sptr, cli_name(sptr), jupe->ju_server, jupe->ju_expire + TSoffset, jupe->ju_reason);
++
++  if (JupeIsLocal(jupe))
++    jupe_free(jupe);
++  else if (!(flags & JUPE_LOCAL)) /* don't propagate local changes */
++    propagate_jupe(cptr, sptr, jupe);
++
++  return 0;
++}
++
++/** Find a jupe by name.
++ * @param[in] server %Jupe name to search for.
++ * @return Matching jupe (or NULL if none match).
++ */
++struct Jupe *
++jupe_find(char *server)
++{
++  struct Jupe* jupe;
++  struct Jupe* sjupe;
++
++  for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
++    sjupe = jupe->ju_next;
++
++    if (jupe->ju_expire <= CurrentTime) /* expire any that need expiring */
++      jupe_free(jupe);
++    else if (0 == ircd_strcmp(server, jupe->ju_server)) /* found it yet? */
++      return jupe;
++  }
++
++  return 0;
++}
++
++/** Unlink and free an unused jupe.
++ * @param[in] jupe Server jupe to free.
++ */
++void
++jupe_free(struct Jupe* jupe)
++{
++  assert(0 != jupe);
++
++  *jupe->ju_prev_p = jupe->ju_next; /* squeeze this jupe out */
++  if (jupe->ju_next)
++    jupe->ju_next->ju_prev_p = jupe->ju_prev_p;
++
++  MyFree(jupe->ju_server);  /* and free up the memory */
++  MyFree(jupe->ju_reason);
++  MyFree(jupe);
++}
++
++/** Send the full list of active global jupes to \a cptr.
++ * @param[in] cptr Local server to send jupes to.
++ */
++void
++jupe_burst(struct Client *cptr)
++{
++  struct Jupe *jupe;
++  struct Jupe *sjupe;
++
++  for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
++    sjupe = jupe->ju_next;
++
++    if (jupe->ju_expire <= CurrentTime) /* expire any that need expiring */
++      jupe_free(jupe);
++    else if (!JupeIsLocal(jupe)) /* forward global jupes */
++      sendcmdto_one(&me, CMD_JUPE, cptr, "* %c%s %Tu %Tu :%s",
++                  JupeIsRemActive(jupe) ? '+' : '-', jupe->ju_server,
++                  jupe->ju_expire - CurrentTime, jupe->ju_lastmod,
++                  jupe->ju_reason);
++  }
++}
++
++/** Forward a jupe to another server.
++ * @param[in] cptr %Server to send jupe to.
++ * @param[in] jupe Jupe to forward.
++ */
++int
++jupe_resend(struct Client *cptr, struct Jupe *jupe)
++{
++  if (JupeIsLocal(jupe)) /* don't propagate local jupes */
++    return 0;
++
++  sendcmdto_one(&me, CMD_JUPE, cptr, "* %c%s %Tu %Tu :%s",
++              JupeIsRemActive(jupe) ? '+' : '-', jupe->ju_server,
++              jupe->ju_expire - CurrentTime, jupe->ju_lastmod,
++              jupe->ju_reason);
++
++  return 0;
++}
++
++/** Send a jupe (or a list of jupes) to a server.
++ * @param[in] sptr Client searching for jupes.
++ * @param[in] server Name of jupe to search for (if NULL, list all).
++ * @return Zero.
++ */
++int
++jupe_list(struct Client *sptr, char *server)
++{
++  struct Jupe *jupe;
++  struct Jupe *sjupe;
++
++  if (server) {
++    if (!(jupe = jupe_find(server))) /* no such jupe */
++      return send_reply(sptr, ERR_NOSUCHJUPE, server);
++
++    /* send jupe information along */
++    send_reply(sptr, RPL_JUPELIST, jupe->ju_server, jupe->ju_expire + TSoffset,
++             JupeIsLocal(jupe) ? cli_name(&me) : "*",
++             JupeIsActive(jupe) ? '+' : '-', jupe->ju_reason);
++  } else {
++    for (jupe = GlobalJupeList; jupe; jupe = sjupe) { /* go through jupes */
++      sjupe = jupe->ju_next;
++
++      if (jupe->ju_expire <= CurrentTime) /* expire any that need expiring */
++      jupe_free(jupe);
++      else /* send jupe information along */
++      send_reply(sptr, RPL_JUPELIST, jupe->ju_server,
++                 jupe->ju_expire + TSoffset,
++                 JupeIsLocal(jupe) ? cli_name(&me) : "*",
++                 JupeIsActive(jupe) ? '+' : '-', jupe->ju_reason);
++    }
++  }
++
++  /* end of jupe information */
++  return send_reply(sptr, RPL_ENDOFJUPELIST);
++}
++
++/** Count welcome and memory used by it.
++ * @param[out] we_size Receives total number of bytes allocated for welcome.
++ * @return Number of welcomes currently allocated.
++ */
++int
++welcome_memory_count(size_t *we_size)
++{
++  struct Jupe *jupe;
++  unsigned int ju = 0;
++
++  for (jupe = GlobalJupeList; jupe; jupe = jupe->ju_next)
++  {
++    ju++;
++    *ju_size += sizeof(struct Jupe);
++    *ju_size += jupe->ju_server ? (strlen(jupe->ju_server) + 1) : 0;
++    *ju_size += jupe->ju_reason ? (strlen(jupe->ju_reason) + 1) : 0;
++  }
++  return ju;
++}
+\ No newline at end of file