Author: Kev <klmitch@mit.edu>
authorKevin L. Mitchell <klmitch@mit.edu>
Wed, 3 Jan 2001 03:03:04 +0000 (03:03 +0000)
committerKevin L. Mitchell <klmitch@mit.edu>
Wed, 3 Jan 2001 03:03:04 +0000 (03:03 +0000)
Log message:

Add notify functionality to features interface; featurize
DEFAULT_LIST_PARAM, rewriting much of m_list in the process.

Testing:

It compiles and runs and appears to function properly; please brute-force.

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

15 files changed:
ChangeLog
config/config-sh.in
doc/api/features.txt
include/channel.h
include/client.h
include/ircd_features.h
include/ircd_log.h
ircd/class.c
ircd/client.c
ircd/ircd.c
ircd/ircd_features.c
ircd/ircd_log.c
ircd/m_list.c
ircd/m_privs.c
ircd/motd.c

index 2429d94b47605f4476a35eb1c3d1c1d467692a7b..f12448864251faeedf1b1fd73fa31d7da010d03f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,55 @@
 2001-01-02  Kevin L. Mitchell  <klmitch@mit.edu>
 
+       * config/config-sh.in: get rid of DEFAULT_LIST_PARAMETER
+       compile-time option--now in features subsystem
+
+       * ircd/motd.c (motd_init): rework motd_init() to be called as the
+       notify function for MPATH and RPATH features (should probably
+       split it up a bit, though...)
+
+       * ircd/m_privs.c (mo_privs): if called with no parameters, return
+       privs of the caller, rather than an error
+
+       * ircd/m_list.c: pull usage message into its own function; pull
+       list parameter processing into its own function that does not
+       modify the contents of the parameter; add list_set_default() to
+       set the default list parameter (uses the notify hook); rework
+       m_list() to make use of these functions; removed dead code
+
+       * ircd/ircd_log.c (log_feature_mark): make sure to return 0, since
+       we have no notify handler
+
+       * ircd/ircd_features.c: add notify callback for notification of
+       value changes; give mark callback an int return value to indicate
+       whether or not to call the notify callback; fix up feature macros
+       for new notify callback; add DEFAULT_LIST_PARAM feature; rewrite
+       string handling in feature_set() to deal with def_str being a null
+       pointer; wrote feature_init() to set up all defaults appropriately
+
+       * ircd/ircd.c (main): call feature_init() instead of
+       feature_mark(), to avoid calling notify functions while setting up
+       defaults
+
+       * ircd/client.c: updated to deal with new privileges structure
+
+       * ircd/class.c: updated so init_class() can be called should one
+       of PINGFREQUENCY, CONNECTFREQUENCY, MAXIMUM_LINKS, or
+       DEFAULTMAXSENDQLENGTH be changed
+
+       * include/ircd_log.h: log_feature_mark() updated to fit with new
+       API changes
+
+       * include/ircd_features.h: added DEFAULT_LIST_PARAM feature and
+       feature_init() function (found necessary since adding the notify
+       stuff and notifying motd.c during start-up...before we defined
+       RPATH!)
+
+       * include/client.h: move privs around to enable addition of more
+       bits if necessary; based on the FD_* macros
+
+       * include/channel.h: declare list_set_default (actually located in
+       m_list.c *blanche*)
+
        * ircd/s_user.c: retrieve MAXSILES and MAXSILELENGTH (now
        AVBANLEN*MAXSILES) from features subsystem
 
index 405741a71964c18394721937573c522ec61b6225..71a7226e58c748bf143625373c938b4c7ab4720a 100644 (file)
@@ -220,15 +220,3 @@ comment 'Configuration'
   int 'Maximum number of network connections (23 - (FD_SETSIZE-4))' MAXCONNECTIONS 252
   int 'Nickname history length' NICKNAMEHISTORYLENGTH 800
 endmenu
-
-mainmenu_option next_comment
-comment 'Server characteristics'
-  bool 'Do you want to have a default LIST parameter' CONFIG_LIST y
-  if [ "$CONFIG_LIST" = "y" ]; then
-    string 'Give default LIST parameter' DEFAULT_LIST 'T<10'
-    define_string DEFAULT_LIST_PARAM "$DEFAULT_LIST"
-  else
-    define_string DEFAULT_LIST "$DEFAULT_LIST"
-    define_bool DEFAULT_LIST_PARAM n
-  fi
-endmenu
index d388540d45e4714b51e1a5f2d2e585d93fc2009e..a66e14409bfaff84465dd132a4fc8ab7f06a6c08 100644 (file)
@@ -8,8 +8,8 @@ In the ircd_features.h header file is an enum Feature that lists all
 the features known to the features subsystem.  The order of entries in
 this list must match precisely the order of features as listed in the
 features[] table in ircd_features.c.  There are four kinds of
-features, five different flags that can be set for features, and six
-different call-backs for more complex features.
+features, seven different flags that can be set for features, and
+seven different call-backs for more complex features.
 
 Types of Features
 
@@ -35,18 +35,26 @@ functions, described below.
 
 Feature Flags
 
-There are five feature flags, one of which is used internally by the
-feature subsystem.  Two of these flags, FEAT_OPER and FEAT_MYOPER, are
-used to select who can see the settings of those features; FEAT_OPER
-permits any operator anywhere on the network to examine the settings
-of a particular feature, whereas FEAT_MYOPER only permits operators
-local to a server to examine feature values.  If neither of these two
-flags is specified, then any user may examine that feature's value.
-
-The other two flags only have any meaning for string values; they are
+There are seven feature flags, one of which is used internally by the
+feature subsystem.  Three of these flags, FEAT_OPER, FEAT_MYOPER, and
+FEAT_NODISP, are used to select who can see the settings of those
+features; FEAT_OPER permits any operator anywhere on the network to
+examine the settings of a particular feature, whereas FEAT_MYOPER only
+permits operators local to a server to examine feature values, and
+FEAT_NODISP prohibits display of the feature value altogether.  If
+none of these three flags are specified, then any user may examine
+that feature's value.
+
+Two other flags only have any meaning for string values; they are
 FEAT_NULL, which is used to specify that a feature of type "STR" may
 have a NULL value, and FEAT_CASE, which specifies that the feature is
-case sensitive--this may be used on file names, for example.
+case sensitive--this may be used on file names, for example.  Note
+that if you give "0" as the default value for a feature, you must also
+set the FEAT_NULL flag.
+
+The remaining non-internal flag is FEAT_READ, which simply sets the
+feature to be read-only; a feature so marked may only be changed
+through the configuration file.
 
 Marking Features
 
@@ -128,6 +136,13 @@ unchanged feature settings to their defaults.  See the subsection on
 "Marking Features."
 </function>
 
+<function>
+void feature_init(void);
+
+This function initializes the feature interface by setting the default
+values for all features correctly.
+</function>
+
 <function>
 void feature_report(struct Client* to);
 
@@ -158,7 +173,7 @@ Use this function to retrieve strings values for features of type
 </function>
 
 <macro>
-#define F_N(type, flags, set, reset, get, unmark, mark, report)
+#define F_N(type, flags, set, reset, get, notify, unmark, mark, report)
 
 This macro is used in the features[] table to simplify defining a
 feature of type "NONE."  The _type_ parameter is the name of the
@@ -170,28 +185,33 @@ functions implementing the named call-back.
 </macro>
 
 <macro>
-#define F_I(type, flags, v_int)
+#define F_I(type, flags, v_int, notify)
 
 To define integer features, use the F_I() macro.  The _type_ and
 _flags_ parameters are as for F_N(), and the _v_int_ macro specifies
-the default value of the feature.
+the default value of the feature.  The _notify_ parameter, if
+non-zero, will be called whenever the value of the feature changes.
 </macro>
 
 <macro>
-#define F_B(type, flags, v_int)
+#define F_B(type, flags, v_int, notify)
 
 This macro is used for defining features of type "BOOL"; it is very
 similar to F_I(), but _v_int_ should either 0 (for a "FALSE" value) or
-1 (for a "TRUE" value).
+1 (for a "TRUE" value).  The _notify_ parameter, if non-zero, will be
+called whenever the value of the feature changes.
 </macro>
 
 <macro>
-#define F_S(type, flags, v_str)
+#define F_S(type, flags, v_str, notify)
 
 Also similar to F_I(), F_S() defines features of type "STR."  The
 _flags_ argument may be the bitwise OR of one of FEAT_OPER or
 FEAT_MYOPER with the special string flags FEAT_NULL and FEAT_CASE,
-which are described above in the section "Feature Flags."
+which are described above in the section "Feature Flags."  The
+_notify_ parameter, if non-zero, will be called whenever the value of
+the feature changes.  Note that FEAT_NULL *must* be set if the default
+string _v_str_ is set to NULL.
 </macro>
 
 <authors>
@@ -199,5 +219,8 @@ Kev <klmitch@mit.edu>
 </authors>
 
 <changelog>
+[2001-01-02 Kev] Add documentation for new flags and for the notify
+mechanism
+
 [2000-12-18 Kev] Document the features API
 </changelog>
index 87c51e95269349e54b9db1ee36af5cc560b6d7c9..a8c4c3c4fb4401b9c10ab17709ae2744f88152ef 100644 (file)
@@ -335,6 +335,7 @@ extern void send_channel_modes(struct Client *cptr, struct Channel *chptr);
 extern char *pretty_mask(char *mask);
 extern void del_invite(struct Client *cptr, struct Channel *chptr);
 extern void list_next_channels(struct Client *cptr, int nr);
+extern void list_set_default(void); /* this belongs elsewhere! */
 
 extern void modebuf_init(struct ModeBuf *mbuf, struct Client *source,
                         struct Client *connect, struct Channel *chan,
index ba6217d676b23e7e137a59bfe3e2a517b4bbc0e2..d6b4f493a19568c35b3c437e5240b512a24f9155 100644 (file)
@@ -55,6 +55,7 @@ struct User;
 struct Whowas;
 struct DNSReply;
 struct hostent;
+struct Privs;
 
 /*
  * Structures
@@ -64,6 +65,46 @@ struct hostent;
  * source file, or in the source file itself (when only used in that file).
  */
 
+#define PRIV_CHAN_LIMIT                 1 /* no channel limit on oper */
+#define PRIV_MODE_LCHAN                 2 /* oper can mode local chans */
+#define PRIV_WALK_LCHAN                 3 /* oper can walk thru local modes */
+#define PRIV_DEOP_LCHAN                 4 /* no deop oper on local chans */
+#define PRIV_SHOW_INVIS                 5 /* show local invisible users */
+#define PRIV_SHOW_ALL_INVIS     6 /* show all invisible users */
+#define PRIV_UNLIMIT_QUERY      7 /* unlimit who queries */
+
+#define PRIV_KILL               8 /* oper can KILL */
+#define PRIV_LOCAL_KILL                 9 /* oper can local KILL */
+#define PRIV_REHASH            10 /* oper can REHASH */
+#define PRIV_RESTART           11 /* oper can RESTART */
+#define PRIV_DIE               12 /* oper can DIE */
+#define PRIV_GLINE             13 /* oper can GLINE */
+#define PRIV_LOCAL_GLINE       14 /* oper can local GLINE */
+#define PRIV_JUPE              15 /* oper can JUPE */
+#define PRIV_LOCAL_JUPE                16 /* oper can local JUPE */
+#define PRIV_OPMODE            17 /* oper can OP/CLEARMODE */
+#define PRIV_LOCAL_OPMODE      18 /* oper can local OP/CLEARMODE */
+#define PRIV_SET               19 /* oper can SET */
+#define PRIV_WHOX              20 /* oper can use /who x */
+#define PRIV_BADCHAN           21 /* oper can BADCHAN */
+#define PRIV_LOCAL_BADCHAN     22 /* oper can local BADCHAN */
+#define PRIV_SEE_CHAN          23 /* oper can see in secret chans */
+
+#define PRIV_PROPAGATE         24 /* propagate oper status */
+#define PRIV_DISPLAY           25 /* "Is an oper" displayed */
+#define PRIV_SEE_OPERS         26 /* display hidden opers */
+
+#define PRIV_LAST_PRIV         26 /* must be the same as the last priv */
+
+#define _PRIV_NBITS            (8 * sizeof(unsigned long))
+
+#define _PRIV_IDX(priv)                ((priv) / _PRIV_NBITS)
+#define _PRIV_BIT(priv)                (1 << ((priv) % _PRIV_NBITS))
+
+struct Privs {
+  unsigned long priv_mask[(PRIV_LAST_PRIV / _PRIV_NBITS) + 1];
+};
+
 struct Connection {
   /*
    *  The following fields are allocated only for local clients
@@ -138,7 +179,7 @@ struct Client {
   struct in_addr cli_ip;        /* Real ip# NOT defined for remote servers! */
   short          cli_status;    /* Client type */
   unsigned char  cli_local;     /* local or remote client */
-  unsigned int   cli_privs;     /* Oper privileges */
+  struct Privs   cli_privs;     /* Oper privileges */
   char cli_name[HOSTLEN + 1];   /* Unique name of the client, nick or host */
   char cli_username[USERLEN + 1]; /* username here now for auth stuff */
   char cli_info[REALLEN + 1];   /* Free form additional client information */
@@ -405,36 +446,16 @@ struct Client {
 #define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP)
 #define SNO_NOISY (SNO_SERVKILL|SNO_UNAUTH)
 
-#define PRIV_CHAN_LIMIT                0x00000001 /* no channel limit on oper */
-#define PRIV_MODE_LCHAN                0x00000002 /* oper can mode local chans */
-#define PRIV_WALK_LCHAN                0x00000004 /* oper can walk thru local modes */
-#define PRIV_DEOP_LCHAN                0x00000008 /* no deop oper on local chans */
-#define PRIV_SHOW_INVIS                0x00000010 /* show local invisible users */
-#define PRIV_SHOW_ALL_INVIS    0x00000020 /* show all invisible users */
-#define PRIV_UNLIMIT_QUERY     0x00000040 /* unlimit who queries */
-
-#define PRIV_KILL              0x00000080 /* oper can KILL */
-#define PRIV_LOCAL_KILL                0x00000100 /* oper can local KILL */
-#define PRIV_REHASH            0x00000200 /* oper can REHASH */
-#define PRIV_RESTART           0x00000400 /* oper can RESTART */
-#define PRIV_DIE               0x00000800 /* oper can DIE */
-#define PRIV_GLINE             0x00001000 /* oper can GLINE */
-#define PRIV_LOCAL_GLINE       0x00002000 /* oper can local GLINE */
-#define PRIV_JUPE              0x00004000 /* oper can JUPE */
-#define PRIV_LOCAL_JUPE                0x00008000 /* oper can local JUPE */
-#define PRIV_OPMODE            0x00010000 /* oper can OP/CLEARMODE */
-#define PRIV_LOCAL_OPMODE      0x00020000 /* oper can local OP/CLEARMODE */
-#define PRIV_SET               0x00040000 /* oper can SET */
-#define PRIV_WHOX              0x00080000 /* oper can use /who x */
-#define PRIV_BADCHAN           0x00100000 /* oper can BADCHAN */
-#define PRIV_LOCAL_BADCHAN     0x00200000 /* oper can local BADCHAN */
-#define PRIV_SEE_CHAN          0x00400000 /* oper can see in secret chans */
-
-#define PRIV_PROPAGATE         0x00800000 /* propagate oper status */
-#define PRIV_DISPLAY           0x01000000 /* "Is an oper" displayed */
-#define PRIV_SEE_OPERS         0x02000000 /* display hidden opers */
-
-#define HasPriv(cli, priv)     (cli_privs(cli) & (priv))
+#define PrivSet(pset, priv)    ((pset)->priv_mask[_PRIV_IDX(priv)] |= \
+                                _PRIV_BIT(priv))
+#define PrivClr(pset, priv)    ((pset)->priv_mask[_PRIV_IDX(priv)] &= \
+                                ~(_PRIV_BIT(priv)))
+#define PrivHas(pset, priv)    ((pset)->priv_mask[_PRIV_IDX(priv)] & \
+                                _PRIV_BIT(priv))
+
+#define GrantPriv(cli, priv)   (PrivSet(&(cli_privs(cli)), priv))
+#define RevokePriv(cli, priv)  (PrivClr(&(cli_privs(cli)), priv))
+#define HasPriv(cli, priv)     (PrivHas(&(cli_privs(cli)), priv))
 
 typedef enum ShowIPType {
   HIDE_IP,
index 04f03afec87e2cb474206b85bb7b1bcb4ffa8627..6c019bfb018f662d4b78312fd9f83321bf6e38ed 100644 (file)
@@ -39,6 +39,7 @@ enum Feature {
   FEAT_WALLOPS_OPER_ONLY,
   FEAT_NODNS,
   FEAT_RANDOM_SEED,
+  FEAT_DEFAULT_LIST_PARAM,
 
   /* features that probably should not be touched */
   FEAT_KILLCHASETIMELIMIT,
@@ -108,6 +109,8 @@ enum Feature {
   FEAT_LAST_F
 };
 
+extern void feature_init(void);
+
 extern int feature_set(struct Client* from, const char* const* fields,
                       int count);
 extern int feature_reset(struct Client* from, const char* const* fields,
index 9c6b3b24ccc5a885c9164513980571c5ebbf4fc1..871534c839bb1adf4feb36404d5bf139edda1d36 100644 (file)
@@ -89,7 +89,7 @@ extern int log_set_default(const char *facility);
 extern char *log_get_default(void);
 
 extern void log_feature_unmark(void);
-extern void log_feature_mark(int flag);
+extern int log_feature_mark(int flag);
 extern void log_feature_report(struct Client *to, int flag);
 
 #endif /* INCLUDED_ircd_log_h */
index feb5dd8e35b6985ce1f4f85d0efbd3871ee9256b..2a4d9cbde53f0f673b6153d965d434704418a4ae 100644 (file)
@@ -36,7 +36,7 @@
 #define BAD_PING                ((unsigned int)-2)
 #define BAD_CLIENT_CLASS        ((unsigned int)-3)
 
-static struct ConnectionClass* connClassList;
+static struct ConnectionClass* connClassList = 0;
 static unsigned int connClassAllocCount;
 
 const struct ConnectionClass* get_class_list(void)
@@ -68,7 +68,8 @@ void free_class(struct ConnectionClass* p)
  */
 void init_class(void)
 {
-  connClassList = (struct ConnectionClass*) make_class();
+  if (!connClassList)
+    connClassList = (struct ConnectionClass*) make_class();
 
   ConClass(connClassList) = 0;
   PingFreq(connClassList) = feature_int(FEAT_PINGFREQUENCY);
index 0c7446517a56c04aef34be2540a2eeea8771ed28..1ab61a42d65dc1e4e2660140a89dab69d78db60c 100644 (file)
@@ -152,15 +152,19 @@ static struct {
 void
 client_set_privs(struct Client* client)
 {
-  unsigned int privs = 0;
-  unsigned int antiprivs = 0;
+  struct Privs privs;
+  struct Privs antiprivs;
   int i;
 
-  if (!IsAnOper(client)) {
-    cli_privs(client) = 0; /* clear privilege mask */
+  memset(&privs, 0, sizeof(struct Privs));
+  memset(&privs, 0, sizeof(struct Privs));
+
+  if (!IsAnOper(client)) { /* clear privilege mask */
+    memset(&(cli_privs(client)), 0, sizeof(struct Privs));
     return;
   } else if (!MyConnect(client)) {
-    cli_privs(client) = ~(PRIV_SET); /* everything but set... */
+    memset(&(cli_privs(client)), 255, sizeof(struct Privs));
+    PrivClr(&(cli_privs(client)), PRIV_SET);
     return;
   }
 
@@ -169,26 +173,33 @@ client_set_privs(struct Client* client)
   for (i = 0; feattab[i].priv; i++) {
     if (feattab[i].flag == 0) {
       if (feature_bool(feattab[i].feat))
-       antiprivs |= feattab[i].priv;
+       PrivSet(&antiprivs, feattab[i].priv);
     } else if (feattab[i].flag == ~0) {
       if (!feature_bool(feattab[i].feat))
-       antiprivs |= feattab[i].priv;
+       PrivSet(&antiprivs, feattab[i].priv);
     } else if (cli_flags(client) & feattab[i].flag) {
       if (feattab[i].feat == FEAT_LAST_F ||
          feature_bool(feattab[i].feat))
-       privs |= feattab[i].priv;
+       PrivSet(&privs, feattab[i].priv);
     }
   }
 
   /* This is the end of the gross section */
 
-  if (privs & PRIV_PROPAGATE)
-    privs |= PRIV_DISPLAY; /* force propagating opers to display */
-  else /* if they don't propagate oper status, prevent desyncs */
-    antiprivs |= (PRIV_KILL | PRIV_GLINE | PRIV_JUPE | PRIV_OPMODE |
-                 PRIV_BADCHAN);
+  if (PrivHas(&privs, PRIV_PROPAGATE))
+    PrivSet(&privs, PRIV_DISPLAY); /* force propagating opers to display */
+  else { /* if they don't propagate oper status, prevent desyncs */
+    PrivSet(&antiprivs, PRIV_KILL);
+    PrivSet(&antiprivs, PRIV_GLINE);
+    PrivSet(&antiprivs, PRIV_JUPE);
+    PrivSet(&antiprivs, PRIV_OPMODE);
+    PrivSet(&antiprivs, PRIV_BADCHAN);
+  }
+
+  for (i = 0; i < _PRIV_IDX(PRIV_LAST_PRIV); i++)
+    privs.priv_mask[i] &= ~antiprivs.priv_mask[i];
 
-  cli_privs(client) = privs & ~antiprivs;
+  cli_privs(client) = privs;
 }
 
 static struct {
@@ -222,7 +233,7 @@ client_report_privs(struct Client *to, struct Client *client)
                 cli_name(client));
 
   for (i = 0; privtab[i].name; i++)
-    if (cli_privs(client) & privtab[i].priv)
+    if (HasPriv(client, privtab[i].priv))
       msgq_append(0, mb, "%s%s", found1++ ? " " : "", privtab[i].name);
 
   send_buffer(to, mb, 0); /* send response */
index d60cc0c241326d64ff8f288b316bb1a22485ca72..27f6c8e5d10909ac94ff0f0209db608026355ca1 100644 (file)
@@ -647,7 +647,7 @@ int main(int argc, char **argv) {
   daemon_init(thisServer.bootopt & BOOT_TTY);
 
   setup_signals();
-  feature_mark(); /* initialize features... */
+  feature_init(); /* initialize features... */
   log_init(*argv);
 
   set_nomem_handler(outofmemory);
index 9e6983b86dc9fdb124157e6dbdb85db2d88c7ace..30c13ea3ed95e9d1f570e56d4eb2af0aed811ed3 100644 (file)
@@ -20,6 +20,8 @@
  */
 #include "config.h"
 #include "ircd_features.h"
+#include "channel.h"   /* list_set_default */
+#include "class.h"
 #include "client.h"
 #include "hash.h"
 #include "ircd.h"
@@ -28,6 +30,7 @@
 #include "ircd_reply.h"
 #include "ircd_string.h"
 #include "match.h"
+#include "motd.h"
 #include "msg.h"
 #include "numeric.h"
 #include "numnicks.h"
@@ -171,10 +174,12 @@ feature_log_get(struct Client* from, const char* const* fields, int count)
 typedef int  (*feat_set_call)(struct Client*, const char* const*, int);
 /* gets the value of a feature */
 typedef void (*feat_get_call)(struct Client*, const char* const*, int);
+/* callback to notify of a feature's change */
+typedef void (*feat_notify_call)(void);
 /* unmarks all sub-feature values prior to reading .conf */
 typedef void (*feat_unmark_call)(void);
 /* resets to defaults all currently unmarked values */
-typedef void (*feat_mark_call)(int);
+typedef int  (*feat_mark_call)(int);
 /* reports features as a /stats f list */
 typedef void (*feat_report_call)(struct Client*, int);
 
@@ -205,110 +210,112 @@ static struct FeatureDesc {
   feat_set_call           set;     /* set feature values */
   feat_set_call           reset;   /* reset feature values to defaults */
   feat_get_call           get;     /* get feature values */
+  feat_notify_call notify;  /* notify of value change */
   feat_unmark_call unmark;  /* unmark all feature change values */
   feat_mark_call   mark;    /* reset to defaults all unchanged features */
   feat_report_call report;  /* report feature values */
 } features[] = {
-#define F_N(type, flags, set, reset, get, unmark, mark, report)                      \
+#define F_N(type, flags, set, reset, get, notify, unmark, mark, report)              \
   { FEAT_ ## type, #type, FEAT_NONE | (flags), 0, 0, 0, 0,                   \
-    (set), (reset), (get), (unmark), (mark), (report) }
-#define F_I(type, flags, v_int)                                                      \
+    (set), (reset), (get), (notify), (unmark), (mark), (report) }
+#define F_I(type, flags, v_int, notify)                                              \
   { FEAT_ ## type, #type, FEAT_INT | (flags), 0, (v_int), 0, 0,                      \
-    0, 0, 0, 0, 0, 0 }
-#define F_B(type, flags, v_int)                                                      \
+    0, 0, 0, (notify), 0, 0, 0 }
+#define F_B(type, flags, v_int, notify)                                              \
   { FEAT_ ## type, #type, FEAT_BOOL | (flags), 0, (v_int), 0, 0,             \
-    0, 0, 0, 0, 0, 0 }
-#define F_S(type, flags, v_str)                                                      \
+    0, 0, 0, (notify), 0, 0, 0 }
+#define F_S(type, flags, v_str, notify)                                              \
   { FEAT_ ## type, #type, FEAT_STR | (flags), 0, 0, 0, (v_str),                      \
-    0, 0, 0, 0, 0, 0 }
+    0, 0, 0, (notify), 0, 0, 0 }
 
   /* Misc. features */
   F_N(LOG, FEAT_MYOPER, feature_log_set, feature_log_reset, feature_log_get,
-      log_feature_unmark, log_feature_mark, log_feature_report),
-  F_S(DOMAINNAME, 0, DOMAINNAME),
-  F_B(RELIABLE_CLOCK, 0, 0),
-  F_I(BUFFERPOOL, 0, 27000000),
-  F_B(HAS_FERGUSON_FLUSHER, 0, 0),
-  F_I(CLIENT_FLOOD, 0, 1024),
-  F_I(SERVER_PORT, FEAT_OPER, 4400),
-  F_B(NODEFAULTMOTD, 0, 1),
-  F_B(KILL_IPMISMATCH, FEAT_OPER, 0),
-  F_B(IDLE_FROM_MSG, 0, 1),
-  F_B(HUB, 0, 0),
-  F_B(WALLOPS_OPER_ONLY, 0, 0),
-  F_B(NODNS, 0, 0),
-  F_N(RANDOM_SEED, FEAT_NODISP, random_seed_set, 0, 0, 0, 0, 0),
+      0, log_feature_unmark, log_feature_mark, log_feature_report),
+  F_S(DOMAINNAME, 0, DOMAINNAME, 0),
+  F_B(RELIABLE_CLOCK, 0, 0, 0),
+  F_I(BUFFERPOOL, 0, 27000000, 0),
+  F_B(HAS_FERGUSON_FLUSHER, 0, 0, 0),
+  F_I(CLIENT_FLOOD, 0, 1024, 0),
+  F_I(SERVER_PORT, FEAT_OPER, 4400, 0),
+  F_B(NODEFAULTMOTD, 0, 1, 0),
+  F_B(KILL_IPMISMATCH, FEAT_OPER, 0, 0),
+  F_B(IDLE_FROM_MSG, 0, 1, 0),
+  F_B(HUB, 0, 0, 0),
+  F_B(WALLOPS_OPER_ONLY, 0, 0, 0),
+  F_B(NODNS, 0, 0, 0),
+  F_N(RANDOM_SEED, FEAT_NODISP, random_seed_set, 0, 0, 0, 0, 0, 0),
+  F_S(DEFAULT_LIST_PARAM, FEAT_NULL, 0, list_set_default),
 
   /* features that probably should not be touched */
-  F_I(KILLCHASETIMELIMIT, 0, 30),
-  F_I(MAXCHANNELSPERUSER, 0, 10),
-  F_I(AVBANLEN, 0, 40),
-  F_I(MAXBANS, 0, 30),
-  F_I(MAXSILES, 0, 15),
-  F_I(HANGONGOODLINK, 0, 300),
-  F_I(HANGONRETRYDELAY, 0, 10),
-  F_I(CONNECTTIMEOUT, 0, 90),
-  F_I(TIMESEC, 0, 60),
-  F_I(MAXIMUM_LINKS, 0, 1),
-  F_I(PINGFREQUENCY, 0, 120),
-  F_I(CONNECTFREQUENCY, 0, 600),
-  F_I(DEFAULTMAXSENDQLENGTH, 0, 40000),
+  F_I(KILLCHASETIMELIMIT, 0, 30, 0),
+  F_I(MAXCHANNELSPERUSER, 0, 10, 0),
+  F_I(AVBANLEN, 0, 40, 0),
+  F_I(MAXBANS, 0, 30, 0),
+  F_I(MAXSILES, 0, 15, 0),
+  F_I(HANGONGOODLINK, 0, 300, 0),
+  F_I(HANGONRETRYDELAY, 0, 10, 0),
+  F_I(CONNECTTIMEOUT, 0, 90, 0),
+  F_I(TIMESEC, 0, 60, 0),
+  F_I(MAXIMUM_LINKS, 0, 1, init_class), /* reinit class 0 as needed */
+  F_I(PINGFREQUENCY, 0, 120, init_class),
+  F_I(CONNECTFREQUENCY, 0, 600, init_class),
+  F_I(DEFAULTMAXSENDQLENGTH, 0, 40000, init_class),
 
   /* Some misc. default paths */
-  F_S(MPATH, FEAT_CASE | FEAT_MYOPER, "ircd.motd"),
-  F_S(RPATH, FEAT_CASE | FEAT_MYOPER, "remote.motd"),
-  F_S(PPATH, FEAT_CASE | FEAT_MYOPER | FEAT_READ, "ircd.pid"),
+  F_S(MPATH, FEAT_CASE | FEAT_MYOPER, "ircd.motd", motd_init),
+  F_S(RPATH, FEAT_CASE | FEAT_MYOPER, "remote.motd", motd_init),
+  F_S(PPATH, FEAT_CASE | FEAT_MYOPER | FEAT_READ, "ircd.pid", 0),
 
   /* Networking features */
-  F_B(VIRTUAL_HOST, 0, 0),
-  F_I(TOS_SERVER, 0, 0x08),
-  F_I(TOS_CLIENT, 0, 0x08),
+  F_B(VIRTUAL_HOST, 0, 0, 0),
+  F_I(TOS_SERVER, 0, 0x08, 0),
+  F_I(TOS_CLIENT, 0, 0x08, 0),
 
   /* features that affect all operators */
-  F_B(CRYPT_OPER_PASSWORD, FEAT_MYOPER | FEAT_READ, 1),
-  F_B(OPER_NO_CHAN_LIMIT, 0, 1),
-  F_B(OPER_MODE_LCHAN, 0, 1),
-  F_B(OPER_WALK_THROUGH_LMODES, 0, 0),
-  F_B(NO_OPER_DEOP_LCHAN, 0, 0),
-  F_B(SHOW_INVISIBLE_USERS, 0, 1),
-  F_B(SHOW_ALL_INVISIBLE_USERS, 0, 1),
-  F_B(UNLIMIT_OPER_QUERY, 0, 0),
-  F_B(LOCAL_KILL_ONLY, 0, 0),
-  F_B(CONFIG_OPERCMDS, 0, 1), /* XXX change default before release */
+  F_B(CRYPT_OPER_PASSWORD, FEAT_MYOPER | FEAT_READ, 1, 0),
+  F_B(OPER_NO_CHAN_LIMIT, 0, 1, 0),
+  F_B(OPER_MODE_LCHAN, 0, 1, 0),
+  F_B(OPER_WALK_THROUGH_LMODES, 0, 0, 0),
+  F_B(NO_OPER_DEOP_LCHAN, 0, 0, 0),
+  F_B(SHOW_INVISIBLE_USERS, 0, 1, 0),
+  F_B(SHOW_ALL_INVISIBLE_USERS, 0, 1, 0),
+  F_B(UNLIMIT_OPER_QUERY, 0, 0, 0),
+  F_B(LOCAL_KILL_ONLY, 0, 0, 0),
+  F_B(CONFIG_OPERCMDS, 0, 1, 0), /* XXX change default before release */
 
   /* features that affect global opers on this server */
-  F_B(OPER_KILL, 0, 1),
-  F_B(OPER_REHASH, 0, 1),
-  F_B(OPER_RESTART, 0, 1),
-  F_B(OPER_DIE, 0, 1),
-  F_B(OPER_GLINE, 0, 1),
-  F_B(OPER_LGLINE, 0, 1),
-  F_B(OPER_JUPE, 0, 1),
-  F_B(OPER_LJUPE, 0, 1),
-  F_B(OPER_OPMODE, 0, 1),
-  F_B(OPER_LOPMODE, 0, 1),
-  F_B(OPER_BADCHAN, 0, 0),
-  F_B(OPER_LBADCHAN, 0, 0),
-  F_B(OPER_SET, 0, 1),
-  F_B(OPERS_SEE_IN_SECRET_CHANNELS, 0, 1),
+  F_B(OPER_KILL, 0, 1, 0),
+  F_B(OPER_REHASH, 0, 1, 0),
+  F_B(OPER_RESTART, 0, 1, 0),
+  F_B(OPER_DIE, 0, 1, 0),
+  F_B(OPER_GLINE, 0, 1, 0),
+  F_B(OPER_LGLINE, 0, 1, 0),
+  F_B(OPER_JUPE, 0, 1, 0),
+  F_B(OPER_LJUPE, 0, 1, 0),
+  F_B(OPER_OPMODE, 0, 1, 0),
+  F_B(OPER_LOPMODE, 0, 1, 0),
+  F_B(OPER_BADCHAN, 0, 0, 0),
+  F_B(OPER_LBADCHAN, 0, 0, 0),
+  F_B(OPER_SET, 0, 1, 0),
+  F_B(OPERS_SEE_IN_SECRET_CHANNELS, 0, 1, 0),
 
   /* features that affect local opers on this server */
-  F_B(LOCOP_KILL, 0, 0),
-  F_B(LOCOP_REHASH, 0, 1),
-  F_B(LOCOP_RESTART, 0, 0),
-  F_B(LOCOP_DIE, 0, 0),
-  F_B(LOCOP_LGLINE, 0, 1),
-  F_B(LOCOP_LJUPE, 0, 1),
-  F_B(LOCOP_LOPMODE, 0, 1),
-  F_B(LOCOP_LBADCHAN, 0, 0),
-  F_B(LOCOP_SET, 0, 0),
-  F_B(LOCOP_SEE_IN_SECRET_CHANNELS, 0, 0),
+  F_B(LOCOP_KILL, 0, 0, 0),
+  F_B(LOCOP_REHASH, 0, 1, 0),
+  F_B(LOCOP_RESTART, 0, 0, 0),
+  F_B(LOCOP_DIE, 0, 0, 0),
+  F_B(LOCOP_LGLINE, 0, 1, 0),
+  F_B(LOCOP_LJUPE, 0, 1, 0),
+  F_B(LOCOP_LOPMODE, 0, 1, 0),
+  F_B(LOCOP_LBADCHAN, 0, 0, 0),
+  F_B(LOCOP_SET, 0, 0, 0),
+  F_B(LOCOP_SEE_IN_SECRET_CHANNELS, 0, 0, 0),
 
 #undef F_S
 #undef F_B
 #undef F_I
 #undef F_N
-  { FEAT_LAST_F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+  { FEAT_LAST_F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 };
 
 /* Given a feature's identifier, look up the feature descriptor */
@@ -336,7 +343,8 @@ feature_desc(struct Client* from, const char *feature)
 int
 feature_set(struct Client* from, const char* const* fields, int count)
 {
-  int i;
+  int i, change = 0, tmp;
+  const char *t_str;
   struct FeatureDesc *feat;
 
   if (from && !HasPriv(from, PRIV_SET))
@@ -354,6 +362,8 @@ feature_set(struct Client* from, const char* const* fields, int count)
     switch (feat->flags & FEAT_MASK) {
     case FEAT_NONE:
       if (feat->set && (i = (*feat->set)(from, fields + 1, count - 1))) {
+       change++; /* feature handler wants a change recorded */
+
        if (i > 0) /* call the set callback and do marking */
          feat->flags |= FEAT_MARK;
        else /* i < 0 */
@@ -362,19 +372,26 @@ feature_set(struct Client* from, const char* const* fields, int count)
       }
 
     case FEAT_INT: /* an integer value */
+      tmp = feat->v_int; /* detect changes... */
+
       if (count < 2) { /* reset value */
        feat->v_int = feat->def_int;
        feat->flags &= ~FEAT_MARK;
       } else { /* ok, figure out the value and whether to mark it */
-       feat->v_int = atoi(fields[1]);
+       feat->v_int = strtoul(fields[1], 0, 0);
        if (feat->v_int == feat->def_int)
          feat->flags &= ~FEAT_MARK;
        else
          feat->flags |= FEAT_MARK;
       }
+
+      if (feat->v_int != tmp) /* check for change */
+       change++;
       break;
 
     case FEAT_BOOL: /* it's a boolean value--true or false */
+      tmp = feat->v_int; /* detect changes... */
+
       if (count < 2) { /* reset value */
        feat->v_int = feat->def_int;
        feat->flags &= ~FEAT_MARK;
@@ -402,42 +419,69 @@ feature_set(struct Client* from, const char* const* fields, int count)
        else
          feat->flags |= FEAT_MARK;
       }
+
+      if (feat->v_int != tmp) /* check for change */
+       change++;
       break;
 
     case FEAT_STR: /* it's a string value */
-      if (count < 2 ||
-         !(feat->flags & FEAT_CASE ? strcmp(fields[1], feat->def_str) :
-           ircd_strcmp(fields[1], feat->def_str))) { /* reset to default */
-       if (feat->v_str && feat->v_str != feat->def_str)
-         MyFree(feat->v_str); /* free old value */
+      if (count < 2)
+       t_str = feat->def_str; /* changing to default */
+      else
+       t_str = *fields[1] ? fields[1] : 0;
+
+      if (!t_str && !(feat->flags & FEAT_NULL)) { /* NULL value permitted? */
+       if (from)
+         return send_reply(from, ERR_BADFEATVALUE, "NULL", feat->type);
+       else {
+         log_write(LS_CONFIG, L_ERROR, 0, "Bad value \"NULL\" for feature %s",
+                   feat->type);
+         return 0;
+       }
+      }
+
+      if (t_str == feat->def_str ||
+         (t_str && feat->def_str &&
+          !(feat->flags & FEAT_CASE ? strcmp(t_str, feat->def_str) :
+            ircd_strcmp(t_str, feat->def_str)))) { /* resetting to default */
+       if (feat->v_str != feat->def_str) {
+         change++; /* change from previous value */
+
+         if (feat->v_str)
+           MyFree(feat->v_str); /* free old value */
+       }
+
        feat->v_str = feat->def_str; /* very special... */
 
-       feat->flags &= ~FEAT_MARK; /* unmark it */
-      } else {
-       if (!*fields[1]) { /* empty string translates to NULL */
-         if (feat->flags & FEAT_NULL) { /* permitted? */
-           if (feat->v_str && feat->v_str != feat->def_str)
-             MyFree(feat->v_str); /* free old value */
-           feat->v_str = 0; /* set it to NULL */
-         } else if (from) /* hmmm...not permitted; report error */
-           return send_reply(from, ERR_BADFEATVALUE, "NULL", feat->type);
-         else {
-           log_write(LS_CONFIG, L_ERROR, 0,
-                     "Bad value \"NULL\" for feature %s", feat->type);
-           return 0;
-         }
-       } else if ((feat->flags & FEAT_CASE ?
-                   strcmp(fields[1], feat->v_str) :
-                   ircd_strcmp(fields[1], feat->v_str))) { /* new value */
-         if (feat->v_str && feat->v_str != feat->def_str)
+       feat->flags &= ~FEAT_MARK;
+      } else if (!t_str) {
+       if (feat->v_str) {
+         change++; /* change from previous value */
+
+         if (feat->v_str != feat->def_str)
            MyFree(feat->v_str); /* free old value */
-         DupString(feat->v_str, fields[1]); /* store new value */
        }
 
-       feat->flags |= FEAT_MARK; /* mark it as having been touched */
-      }
+       feat->v_str = 0; /* set it to NULL */
+
+       feat->flags |= FEAT_MARK;
+      } else if (!feat->v_str ||
+                (feat->flags & FEAT_CASE ? strcmp(t_str, feat->v_str) :
+                 ircd_strcmp(t_str, feat->v_str))) { /* new value */
+       change++; /* change from previous value */
+
+       if (feat->v_str && feat->v_str != feat->def_str)
+         MyFree(feat->v_str); /* free old value */
+       DupString(feat->v_str, t_str); /* store new value */
+
+       feat->flags |= FEAT_MARK;
+      } else /* they match, but don't match the default */
+       feat->flags |= FEAT_MARK;
       break;
     }
+
+    if (change && feat->notify) /* call change notify function */
+      (*feat->notify)();
   }
 
   return 0;
@@ -447,7 +491,7 @@ feature_set(struct Client* from, const char* const* fields, int count)
 int
 feature_reset(struct Client* from, const char* const* fields, int count)
 {
-  int i;
+  int i, change = 0;
   struct FeatureDesc *feat;
 
   assert(0 != from);
@@ -464,6 +508,8 @@ feature_reset(struct Client* from, const char* const* fields, int count)
     switch (feat->flags & FEAT_MASK) {
     case FEAT_NONE: /* None... */
       if (feat->reset && (i = (*feat->reset)(from, fields + 1, count - 1))) {
+       change++; /* feature handler wants a change recorded */
+
        if (i > 0) /* call reset callback and parse mark return */
          feat->flags |= FEAT_MARK;
        else /* i < 0 */
@@ -473,17 +519,27 @@ feature_reset(struct Client* from, const char* const* fields, int count)
 
     case FEAT_INT:  /* Integer... */
     case FEAT_BOOL: /* Boolean... */
+      if (feat->v_int != feat->def_int)
+       change++; /* change will be made */
+
       feat->v_int = feat->def_int; /* set the default */
       feat->flags &= ~FEAT_MARK; /* unmark it */
       break;
 
     case FEAT_STR: /* string! */
-      if (feat->v_str && feat->v_str != feat->def_str)
-       MyFree(feat->v_str); /* free old value */
+      if (feat->v_str != feat->def_str) {
+       change++; /* change has been made */
+       if (feat->v_str)
+         MyFree(feat->v_str); /* free old value */
+      }
+
       feat->v_str = feat->def_str; /* set it to default */
       feat->flags &= ~FEAT_MARK; /* unmark it */
       break;
     }
+
+    if (change && feat->notify) /* call change notify function */
+      (*feat->notify)();
   }
 
   return 0;
@@ -553,29 +609,66 @@ feature_unmark(void)
 void
 feature_mark(void)
 {
-  int i;
+  int i, change;
+
+  for (i = 0; features[i].type; i++) {
+    change = 0;
 
-  for (i = 0; features[i].type; i++)
     switch (features[i].flags & FEAT_MASK) {
     case FEAT_NONE:
-      if (features[i].mark) /* call the mark callback if necessary */
-       (*features[i].mark)(features[i].flags & FEAT_MARK ? 1 : 0);
+      if (features[i].mark &&
+         (*features[i].mark)(features[i].flags & FEAT_MARK ? 1 : 0))
+       change++; /* feature handler wants a change recorded */
       break;
 
     case FEAT_INT:  /* Integers or Booleans... */
     case FEAT_BOOL:
-      if (!(features[i].flags & FEAT_MARK)) /* not changed? */
+      if (!(features[i].flags & FEAT_MARK)) { /* not changed? */
+       if (features[i].v_int != features[i].def_int)
+         change++; /* we're making a change */
        features[i].v_int = features[i].def_int;
+      }
       break;
 
     case FEAT_STR: /* strings... */
       if (!(features[i].flags & FEAT_MARK)) { /* not changed? */
-       if (features[i].v_str && features[i].v_str != features[i].def_str)
-         MyFree(features[i].v_str); /* free old value */
+       if (features[i].v_str != features[i].def_str) {
+         change++; /* we're making a change */
+         if (features[i].v_str)
+           MyFree(features[i].v_str); /* free old value */
+       }
        features[i].v_str = features[i].def_str;
       }
       break;
     }
+
+    if (change && features[i].notify)
+      (*features[i].notify)(); /* call change notify function */
+  }
+}
+
+/* used to initialize the features subsystem */
+void
+feature_init(void)
+{
+  int i;
+
+  for (i = 0; features[i].type; i++) {
+    switch (features[i].flags & FEAT_MASK) {
+    case FEAT_NONE: /* you're on your own */
+      break;
+
+    case FEAT_INT:  /* Integers or Booleans... */
+    case FEAT_BOOL:
+      features[i].v_int = features[i].def_int;
+      break;
+
+    case FEAT_STR:  /* Strings */
+      features[i].v_str = features[i].def_str;
+      assert(features[i].def_str || (features[i].flags & FEAT_NULL));
+      break;
+    }
+  }
 }
 
 /* report all F-lines */
index ab65f3b563e1c6d26a50609b9efb0ecddd18317a..53e8e00ff05b7242d0e71ee7c6d3b2daf30b74ad 100644 (file)
@@ -828,7 +828,7 @@ log_feature_unmark(void)
 }
 
 /* Reset unmarked fields to defaults... */
-void
+int
 log_feature_mark(int flag)
 {
   int i;
@@ -854,6 +854,8 @@ log_feature_mark(int flag)
     if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
       logDesc[i].level = L_DEFAULT;
   }
+
+  return 0; /* we don't have a notify handler */
 }
 
 /* Report feature settings */
index 1a7db906baeee95fc61688b02a8200fc4c87cfaf..6007f7e172d0b56e709ce0f0916d4ea63cb3d2ee 100644 (file)
@@ -93,6 +93,8 @@
 #include "ircd.h"
 #include "ircd_alloc.h"
 #include "ircd_chattr.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
 #include "msg.h"
 #include <stdlib.h>
 #include <string.h>
 
+#define LPARAM_ERROR   -1
+#define LPARAM_SUCCESS  0
+#define LPARAM_CHANNEL  1
+
+static struct ListingArgs la_init = {
+  2147483647,                 /* max_time */
+  0,                          /* min_time */
+  4294967295U,                /* max_users */
+  0,                          /* min_users */
+  0,                          /* topic_limits */
+  2147483647,                 /* max_topic_time */
+  0,                          /* min_topic_time */
+  0                           /* chptr */
+};
+
+static struct ListingArgs la_default = {
+  2147483647,                 /* max_time */
+  0,                          /* min_time */
+  4294967295U,                /* max_users */
+  0,                          /* min_users */
+  0,                          /* topic_limits */
+  2147483647,                 /* max_topic_time */
+  0,                          /* min_topic_time */
+  0                           /* chptr */
+};
+
+static int
+show_usage(struct Client *sptr)
+{
+  if (!sptr) { /* configuration file error... */
+    log_write(LS_CONFIG, L_ERROR, 0, "Invalid default list parameter");
+    return LPARAM_ERROR;
+  }
+
+  send_reply(sptr, RPL_LISTUSAGE,
+            "Usage: \002/QUOTE LIST\002 \037parameters\037");
+  send_reply(sptr, RPL_LISTUSAGE,
+            "Where \037parameters\037 is a space or comma seperated "
+            "list of one or more of:");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002<\002\037max_users\037    ; Show all channels with less "
+            "than \037max_users\037.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002>\002\037min_users\037    ; Show all channels with more "
+            "than \037min_users\037.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002C<\002\037max_minutes\037 ; Channels that exist less "
+            "than \037max_minutes\037.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002C>\002\037min_minutes\037 ; Channels that exist more "
+            "than \037min_minutes\037.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002T<\002\037max_minutes\037 ; Channels with a topic last "
+            "set less than \037max_minutes\037 ago.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            " \002T>\002\037min_minutes\037 ; Channels with a topic last "
+            "set more than \037min_minutes\037 ago.");
+  send_reply(sptr, RPL_LISTUSAGE,
+            "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 "
+            "min., topic set.");
+
+  return LPARAM_ERROR; /* return error condition */
+}
+
+static int
+param_parse(struct Client *sptr, const char *param, struct ListingArgs *args,
+           int permit_chan)
+{
+  int is_time = 0;
+  char dir;
+  unsigned int val;
+
+  assert(0 != args);
+
+  if (!param) /* NULL param == default--no list param */
+    return LPARAM_SUCCESS;
+
+  while (1) {
+    switch (*param) {
+    case 'T':
+    case 't':
+      is_time++;
+      args->topic_limits = 1;
+      /*FALLTHROUGH*/
+
+    case 'C':
+    case 'c':
+      is_time++;
+      param++;
+      if (*param != '<' && *param != '>')
+       return show_usage(sptr);
+      /*FALLTHROUGH*/
+
+    case '<':
+    case '>':
+      dir = *(param++);
+
+      if (!IsDigit(*param)) /* must start with a digit */
+       return show_usage(sptr);
+
+      val = strtol(param, (char **)&param, 10); /* convert it... */
+
+      if (*param != ',' && *param != ' ' && *param != '\0') /* check syntax */
+       return show_usage(sptr);
+
+      if (is_time && val < 80000000) /* Toggle UTC/offset */
+       val = TStime() - val * 60;
+      
+      switch (is_time) {
+      case 0: /* number of users on channel */
+       if (dir == '<')
+         args->max_users = val;
+       else
+         args->min_users = val;
+       break;
+
+      case 1: /* channel topic */
+       if (dir == '<')
+         args->min_topic_time = val;
+       else
+         args->max_topic_time = val;
+       break;
+
+      case 2: /* channel creation time */
+       if (dir == '<')
+         args->min_time = val;
+       else
+         args->max_time = val;
+       break;
+      }
+      break;
+
+    default: /* channel name? */
+      if (!permit_chan || !IsChannelName(param))
+       return show_usage(sptr);
+
+      return LPARAM_CHANNEL;
+      break;
+    }
+
+    if (!*param) /* hit end of string? */
+      break;
+
+    param++;
+  }
+
+  return LPARAM_SUCCESS;
+}
+
+void
+list_set_default(void)
+{
+  la_default = la_init; /* start off with a clean slate... */
+
+  if (param_parse(0, feature_str(FEAT_DEFAULT_LIST_PARAM), &la_default, 0) !=
+      LPARAM_SUCCESS)
+    la_default = la_init; /* recover from error by switching to default */
+}
+
 /*
  * m_list - generic message handler
  *
@@ -115,17 +276,8 @@ int m_list(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
   struct Channel *chptr;
   char *name, *p = 0;
-  int show_usage = 0, show_channels = 0, param;
-  struct ListingArgs args = {
-    2147483647,                 /* max_time */
-    0,                          /* min_time */
-    4294967295U,                /* max_users */
-    0,                          /* min_users */
-    0,                          /* topic_limits */
-    2147483647,                 /* max_topic_time */
-    0,                          /* min_topic_time */
-    0                        /* chptr */
-  };
+  int show_channels = 0, param;
+  struct ListingArgs args;
 
   if (cli_listing(sptr))            /* Already listing ? */
   {
@@ -138,156 +290,24 @@ int m_list(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   }
 
   if (parc < 2)                 /* No arguments given to /LIST ? */
-  {
-#ifdef DEFAULT_LIST_PARAM
-    static char *defparv[MAXPARA + 1];
-    static int defparc = 0;
-    static char lp[] = DEFAULT_LIST_PARAM;
-    int i;
-
-    /*
-     * XXX - strtok used
-     */
-    if (!defparc)
-    {
-      char *s = lp, *t;
-
-      defparc = 1;
-      defparv[defparc++] = t = strtok(s, " ");
-      while (t && defparc < MAXPARA)
-      {
-        if ((t = strtok(0, " ")))
-          defparv[defparc++] = t;
-      }
-    }
-    for (i = 1; i < defparc; i++)
-      parv[i] = defparv[i];
-    parv[i] = 0;
-    parc = defparc;
-#endif /* DEFAULT_LIST_PARAM */
-  }
+    args = la_default;
+  else {
+    args = la_init; /* initialize argument to blank slate */
 
-  /* Decode command */
-  for (param = 1; !show_usage && parv[param]; param++)
-  {
-    char *p = parv[param];
-    do
-    {
-      int is_time = 0;
-      switch (*p)
-      {
-        case 'T':
-        case 't':
-          is_time++;
-          args.topic_limits = 1;
-          /* Fall through */
-        case 'C':
-        case 'c':
-          is_time++;
-          p++;
-          if (*p != '<' && *p != '>')
-          {
-            show_usage = 1;
-            break;
-          }
-          /* Fall through */
-        case '<':
-        case '>':
-        {
-          p++;
-          if (!IsDigit(*p))
-            show_usage = 1;
-          else
-          {
-            if (is_time)
-            {
-              time_t val = atoi(p);
-              if (p[-1] == '<')
-              {
-                if (val < 80000000)     /* Toggle UTC/offset */
-                {
-                  /*
-                   * Demands that
-                   * 'TStime() - chptr->creationtime < val * 60'
-                   * Which equals
-                   * 'chptr->creationtime > TStime() - val * 60'
-                   */
-                  if (is_time == 1)
-                    args.min_time = TStime() - val * 60;
-                  else
-                    args.min_topic_time = TStime() - val * 60;
-                }
-                else if (is_time == 1)  /* Creation time in UTC was entered */
-                  args.max_time = val;
-                else            /* Topic time in UTC was entered */
-                  args.max_topic_time = val;
-              }
-              else if (val < 80000000)
-              {
-                if (is_time == 1)
-                  args.max_time = TStime() - val * 60;
-                else
-                  args.max_topic_time = TStime() - val * 60;
-              }
-              else if (is_time == 1)
-                args.min_time = val;
-              else
-                args.min_topic_time = val;
-            }
-            else if (p[-1] == '<')
-              args.max_users = atoi(p);
-            else
-              args.min_users = atoi(p);
-            if ((p = strchr(p, ',')))
-              p++;
-          }
-          break;
-        }
-        default:
-          if (!IsChannelName(p))
-          {
-            show_usage = 1;
-            break;
-          }
-          if (parc != 2)        /* Don't allow a mixture of channels with <,> */
-            show_usage = 1;
-          show_channels = 1;
-          p = 0;
-          break;
+    for (param = 1; parv[param]; param++) { /* process each parameter */
+      switch (param_parse(sptr, parv[param], &args, parc != 2)) {
+      case LPARAM_ERROR: /* error encountered, usage already sent, return */
+       return 0;
+       break;
+
+      case LPARAM_CHANNEL: /* show channel instead */
+       show_channels++;
+       break;
+
+      case LPARAM_SUCCESS: /* parse succeeded */
+       break;
       }
     }
-    while (!show_usage && p);   /* p points after comma, or is NULL */
-  }
-
-  if (show_usage)
-  {
-    send_reply(sptr, RPL_LISTUSAGE,
-              "Usage: \002/QUOTE LIST\002 \037parameters\037");
-    send_reply(sptr, RPL_LISTUSAGE,
-              "Where \037parameters\037 is a space or comma seperated "
-              "list of one or more of:");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002<\002\037max_users\037    ; Show all channels with less "
-              "than \037max_users\037.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002>\002\037min_users\037    ; Show all channels with more "
-              "than \037min_users\037.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002C<\002\037max_minutes\037 ; Channels that exist less "
-              "than \037max_minutes\037.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002C>\002\037min_minutes\037 ; Channels that exist more "
-              "than \037min_minutes\037.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002T<\002\037max_minutes\037 ; Channels with a topic last "
-              "set less than \037max_minutes\037 ago.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              " \002T>\002\037min_minutes\037 ; Channels with a topic last "
-              "set more than \037min_minutes\037 ago.");
-    send_reply(sptr, RPL_LISTUSAGE,
-              "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 "
-              "min., topic set.");
-    return 0;
   }
 
   send_reply(sptr, RPL_LISTSTART);
@@ -324,226 +344,3 @@ int m_list(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   send_reply(sptr, RPL_LISTEND);
   return 0;
 }
-
-
-#if 0
-/*
- * m_list
- *
- * parv[0] = sender prefix
- * parv[1] = channel list or user/time limit
- * parv[2...] = more user/time limits
- */
-int m_list(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
-{
-  struct Channel *chptr;
-  char *name, *p = 0;
-  int show_usage = 0, show_channels = 0, param;
-  struct ListingArgs args = {
-    2147483647,                 /* max_time */
-    0,                          /* min_time */
-    4294967295U,                /* max_users */
-    0,                          /* min_users */
-    0,                          /* topic_limits */
-    2147483647,                 /* max_topic_time */
-    0,                          /* min_topic_time */
-    0                        /* chptr */
-  };
-
-  if (sptr->listing)            /* Already listing ? */
-  {
-    sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
-    MyFree(sptr->listing);
-    sptr->listing = 0;
-    sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name); /* XXX DEAD */
-    if (parc < 2)
-      return 0;                 /* Let LIST abort a listing. */
-  }
-
-  if (parc < 2)                 /* No arguments given to /LIST ? */
-  {
-#ifdef DEFAULT_LIST_PARAM
-    static char *defparv[MAXPARA + 1];
-    static int defparc = 0;
-    static char lp[] = DEFAULT_LIST_PARAM;
-    int i;
-
-    if (!defparc)
-    {
-      char *s = lp, *t;
-
-      defparc = 1;
-      defparv[defparc++] = t = strtok(s, " ");
-      while (t && defparc < MAXPARA)
-      {
-        if ((t = strtok(0, " ")))
-          defparv[defparc++] = t;
-      }
-    }
-    for (i = 1; i < defparc; i++)
-      parv[i] = defparv[i];
-    parv[i] = 0;
-    parc = defparc;
-#endif /* DEFAULT_LIST_PARAM */
-  }
-
-  /* Decode command */
-  for (param = 1; !show_usage && parv[param]; param++)
-  {
-    char *p = parv[param];
-    do
-    {
-      int is_time = 0;
-      switch (*p)
-      {
-        case 'T':
-        case 't':
-          is_time++;
-          args.topic_limits = 1;
-          /* Fall through */
-        case 'C':
-        case 'c':
-          is_time++;
-          p++;
-          if (*p != '<' && *p != '>')
-          {
-            show_usage = 1;
-            break;
-          }
-          /* Fall through */
-        case '<':
-        case '>':
-        {
-          p++;
-          if (!IsDigit(*p))
-            show_usage = 1;
-          else
-          {
-            if (is_time)
-            {
-              time_t val = atoi(p);
-              if (p[-1] == '<')
-              {
-                if (val < 80000000)     /* Toggle UTC/offset */
-                {
-                  /*
-                   * Demands that
-                   * 'TStime() - chptr->creationtime < val * 60'
-                   * Which equals
-                   * 'chptr->creationtime > TStime() - val * 60'
-                   */
-                  if (is_time == 1)
-                    args.min_time = TStime() - val * 60;
-                  else
-                    args.min_topic_time = TStime() - val * 60;
-                }
-                else if (is_time == 1)  /* Creation time in UTC was entered */
-                  args.max_time = val;
-                else            /* Topic time in UTC was entered */
-                  args.max_topic_time = val;
-              }
-              else if (val < 80000000)
-              {
-                if (is_time == 1)
-                  args.max_time = TStime() - val * 60;
-                else
-                  args.max_topic_time = TStime() - val * 60;
-              }
-              else if (is_time == 1)
-                args.min_time = val;
-              else
-                args.min_topic_time = val;
-            }
-            else if (p[-1] == '<')
-              args.max_users = atoi(p);
-            else
-              args.min_users = atoi(p);
-            if ((p = strchr(p, ',')))
-              p++;
-          }
-          break;
-        }
-        default:
-          if (!IsChannelName(p))
-          {
-            show_usage = 1;
-            break;
-          }
-          if (parc != 2)        /* Don't allow a mixture of channels with <,> */
-            show_usage = 1;
-          show_channels = 1;
-          p = 0;
-          break;
-      }
-    }
-    while (!show_usage && p);   /* p points after comma, or is NULL */
-  }
-
-  if (show_usage)
-  {
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        "Usage: \002/QUOTE LIST\002 \037parameters\037");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        "Where \037parameters\037 is a space or comma seperated "
-        "list of one or more of:");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002<\002\037max_users\037    ; Show all channels with less "
-        "than \037max_users\037.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002>\002\037min_users\037    ; Show all channels with more "
-        "than \037min_users\037.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002C<\002\037max_minutes\037 ; Channels that exist less "
-        "than \037max_minutes\037.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002C>\002\037min_minutes\037 ; Channels that exist more "
-        "than \037min_minutes\037.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002T<\002\037max_minutes\037 ; Channels with a topic last "
-        "set less than \037max_minutes\037 ago.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        " \002T>\002\037min_minutes\037 ; Channels with a topic last "
-        "set more than \037min_minutes\037 ago.");
-    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
-        "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 min., "
-        "topic set.");
-    return 0;
-  }
-
-  sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]); /* XXX DEAD */
-
-  if (!show_channels)
-  {
-    if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
-        args.max_topic_time > args.min_topic_time)      /* Sanity check */
-    {
-      sptr->listing = (struct ListingArgs*) MyMalloc(sizeof(struct ListingArgs));
-      assert(0 != sptr->listing);
-      memcpy(sptr->listing, &args, sizeof(struct ListingArgs));
-      if ((sptr->listing->chptr = GlobalChannelList)) {
-        int m = GlobalChannelList->mode.mode & MODE_LISTED;
-        list_next_channels(sptr, 64);
-        GlobalChannelList->mode.mode |= m;
-        return 0;
-      }
-      MyFree(sptr->listing);
-      sptr->listing = 0;
-    }
-    sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); /* XXX DEAD */
-    return 0;
-  }
-
-  for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
-  {
-    chptr = FindChannel(name);
-    if (chptr && ShowChannel(sptr, chptr) && sptr->user)
-      sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0], /* XXX DEAD */
-          ShowChannel(sptr, chptr) ? chptr->chname : "*",
-          chptr->users - number_of_zombies(chptr), chptr->topic);
-  }
-
-  sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); /* XXX DEAD */
-  return 0;
-}
-#endif /* 0 */
-
index c2890a8213e574e6cdcad3364af1e03e44a28285..a5a307d2b3b94be1877fdaad8f438750e7a77758 100644 (file)
@@ -109,7 +109,7 @@ int mo_privs(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   int i;
 
   if (parc < 2)
-    return need_more_params(sptr, "PRIVS");
+    return client_report_privs(sptr, sptr);
 
   for (i = 1; i < parc; i++) {
     for (name = ircd_strtok(&p, parv[i], " "); name;
index f32bfb4638e36d360df9becf65426067ec5c1b3f..4617a3670ff09117ecc1f6acd72f8358ed71b5a6 100644 (file)
@@ -53,7 +53,7 @@ static struct {
   struct Motd*     other;
   struct Motd*     freelist;
   struct MotdCache* cachelist;
-} MotdList;
+} MotdList = { 0, 0, 0, 0, 0 };
 
 /* Create a struct Motd and initialize it */
 static struct Motd *
@@ -326,16 +326,17 @@ motd_recache(void)
 void
 motd_init(void)
 {
+  if (MotdList.local) /* destroy old local... */
+    motd_destroy(MotdList.local);
+
   MotdList.local = motd_create(0, feature_str(FEAT_MPATH), MOTD_MAXLINES);
   motd_cache(MotdList.local); /* init local and cache it */
 
+  if (MotdList.remote) /* destroy old remote... */
+    motd_destroy(MotdList.remote);
+
   MotdList.remote = motd_create(0, feature_str(FEAT_RPATH), MOTD_MAXREMOTE);
   motd_cache(MotdList.remote); /* init remote and cache it */
-
-  MotdList.other = 0; /* no T-lines processed yet */
-
-  MotdList.freelist = 0;
-  MotdList.cachelist = 0;
 }
 
 /* This routine adds a MOTD */