Author: Carlo Wood <run@alinoe.com> (Via Isomer <isomer@undernet.org>)
authorPerry Lorier <isomer@undernet.org>
Wed, 6 Mar 2002 09:31:28 +0000 (09:31 +0000)
committerPerry Lorier <isomer@undernet.org>
Wed, 6 Mar 2002 09:31:28 +0000 (09:31 +0000)
Log message:

Run's Patches:
 apass1.diff
 apass1-2.diff
 map_update.patch
 apass2-3.diff

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

13 files changed:
ChangeLog
include/channel.h
include/numeric.h
ircd/Makefile.in
ircd/channel.c
ircd/ircd.c
ircd/m_burst.c
ircd/m_endburst.c
ircd/m_join.c
ircd/m_mode.c
ircd/m_opmode.c
ircd/s_err.c
ircd/s_misc.c

index 6a372ee453ebad63ef3425be8e7fac52bfabfa6a..bd548ba126a558668ca72dd9b83dad772639cdf5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,85 @@
+
+2002-03-01  Carlo Wood  <run@alinoe.com>
+
+       * include/channel.h: struct Channel: new attribute destruct_event.
+       Prototype for destruct_channel().
+
+       * include/destruct_event.h: new header file for destruct_event.c.
+
+       * ircd/Makefile.in: New source file: destruct_event.c.
+
+       * ircd/channel.c: sub1_from_channel: Don't destruct channel
+       immedeately but instead schedule it for destruction after
+       some time when a channel becomes empty (and clear invite
+       only and limit in that case).
+       destruct_channel: new function, was previously the destructing
+       part of sub1_from_channel.
+       add_user_to_channel: remove destruction request if any.
+
+       * ircd/destruct_event.c: New file.  Implementation of functions
+       schedule_destruct_event_1m, schedule_destruct_event_48h,
+       remove_destruct_event and exec_expired_destruct_events.
+
+       * ircd/ircd.c: destruct_event_timer: new timer.
+       main: use destruct_event_timer to call exec_expired_destruct_events
+       once per minute.
+
+       * ircd/m_endburst.c: ms_end_of_burst: Don't complain about empty
+       channels.  Schedule new empty channels for destruction.
+
+       * ircd/m_join.c: m_join: Destruct just-created channel immedeately.
+
+2002-03-01  Carlo Wood  <run@alinoe.com>
+       * ircd/s_misc.c: exit_client: Only call map_update()
+       for servers.
+
+2002-02-28  Carlo Wood  <run@alinoe.com>
+       * include/channel.h: New attribute 'oplevel' in struct Membership.
+       Added defines MAXOPLEVELDIGITS and MAXOPLEVEL.
+       New macros:  OpLevel(member): returns op-level of member and
+       SetOpLevel(member, value): sets op-level of member.
+       Prototype of add_user_to_channel: add oplevel to parameters.
+       Prototype of mode_parse: add member to to parameters.
+
+       * include/numeric.h: added ERR_NOTLOWEROPLEVEL.
+
+       * ircd/s_err.c: idem.
+
+       * ircd/channel.c: Removed unmatched '{' braces from comments
+       (confuses vi).  add_user_to_channel: oplevel is passed to function
+       and added in the created MemberShip structure.  send_channel_modes:
+       Generate the nick:mode list of the BURST msg in the new style (with
+       op-levels).  DONE_UPASS/DONE_APASS: fixed typo in comment.  struct
+       ParseState: New attribute: member.  mode_process_clients: Disallow
+       deopping someone with an equal or higher op-level, take care of
+       inheritance of op-level.  mode_parse: member is passed to function      
+       and added in the created ParseState structure.  joinbuf_join: pass 0
+       as oplevel to add_user_to_channel as needed initialization of oplevel
+       in struct MemberShip.
+
+       * ircd/m_burst.c: ms_burst: Implementation of op-levels in the
+       decoding of a BURST message and passing on a BURST message.
+       Renamed default_mode to current_mode.
+
+       * ircd/m_mode.c: m_mode/ms_mode: pass on `member' to mode_parse.
+
+       * ircd/m_opmode.c: ms_opmode/mo_opmode: pass on NULL as member
+       to mode_parse (causes opped member to get op-level 0).                  
+
+2002-02-25  Carlo Wood  <run@alinoe.com>
+       * include/channel.h: Added two new strings to struct Mode,
+       upass and apass, both with maximum length PASSLEN (a new
+       define in this file).  Two new mode defines MODE_UPASS and
+       MODE_APASS.
+
+       * ircd/channel.c: is_level0_op: Added as dummy function.
+       channel_modes/modebuf_flush_int/modebuf_extract/mode_parse:
+       Added support for MODE_APASS (+A) and MODE_UPASS (+u).
+       mode_parse_upass: New function to parse mode +u.
+       mode_parse_apass: New function to parse mode +A.
+
+       * ircd/s_err.c: Added 'A' and 'u' to mode list (RPL_MYINFO).            
+
 2002-02-25  Carlo Wood  <carlo@alinoe.com>
 
        * ircd/m_server.c: remove unused variables
index 66ba3d5412c0f249edb518953da41974acaad350..2825da70a64866754189aa7055f7cd9e85a29fe6 100644 (file)
@@ -40,6 +40,7 @@ struct Client;
 #define MODEBUFLEN      200
 
 #define KEYLEN          23
+#define PASSLEN         23
 #define CHANNELLEN      200
 
 #define MAXJOINARGS    15 /* number of slots for join buffer */
@@ -89,10 +90,12 @@ struct Client;
 #define MODE_SAVE      0x20000 /* save this mode-with-arg 'til later */
 #define MODE_FREE      0x40000 /* string needs to be passed to MyFree() */
 #define MODE_BURSTADDED        0x80000 /* channel was created by a BURST */
+#define MODE_UPASS     0x100000
+#define MODE_APASS     0x200000
 /*
  * mode flags which take another parameter (With PARAmeterS)
  */
-#define MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT)
+#define MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS)
 
 #define HoldChannel(x)          (!(x))
 /* name invisible */
@@ -165,13 +168,18 @@ struct Membership {
   struct Membership* next_channel;
   struct Membership* prev_channel;
   unsigned int       status;
+  unsigned short     oplevel;
 };
 
+#define MAXOPLEVELDIGITS    3
+#define MAXOPLEVEL          999
+
 #define IsZombie(x)         ((x)->status & CHFL_ZOMBIE)
 #define IsDeopped(x)        ((x)->status & CHFL_DEOPPED)
 #define IsBanned(x)         ((x)->status & CHFL_BANNED)
 #define IsBanValid(x)       ((x)->status & CHFL_BANVALID)
 #define IsChanOp(x)         ((x)->status & CHFL_CHANOP)
+#define OpLevel(x)          ((x)->oplevel)
 #define HasVoice(x)         ((x)->status & CHFL_VOICE)
 #define IsServOpOk(x)       ((x)->status & CHFL_SERVOPOK)
 #define IsBurstJoined(x)    ((x)->status & CHFL_BURST_JOINED)
@@ -183,6 +191,7 @@ struct Membership {
 #define SetServOpOk(x)      ((x)->status |= CHFL_SERVOPOK)
 #define SetBurstJoined(x)   ((x)->status |= CHFL_BURST_JOINED)
 #define SetZombie(x)        ((x)->status |= CHFL_ZOMBIE)
+#define SetOpLevel(x, v)    (void)((x)->oplevel = (v))
 
 #define ClearBanned(x)      ((x)->status &= ~CHFL_BANNED)
 #define ClearBanValid(x)    ((x)->status &= ~CHFL_BANVALID)
@@ -195,12 +204,15 @@ struct Mode {
   unsigned int mode;
   unsigned int limit;
   char key[KEYLEN + 1];
+  char upass[PASSLEN + 1];
+  char apass[PASSLEN + 1];
 };
 
 struct Channel {
   struct Channel*    next;
   struct Channel*    prev;
   struct Channel*    hnext;
+  struct DestructEvent* destruct_event;
   time_t             creationtime;
   time_t             topic_time;
   unsigned int       users;
@@ -296,9 +308,10 @@ extern struct Channel *get_channel(struct Client *cptr,
 extern struct Membership* find_member_link(struct Channel * chptr,
                                            const struct Client* cptr);
 extern int sub1_from_channel(struct Channel* chptr);
+extern int destruct_channel(struct Channel* chptr);
 extern int can_join(struct Client *sptr, struct Channel *chptr, char *key);
 extern void add_user_to_channel(struct Channel* chptr, struct Client* who,
-                                unsigned int flags);
+                                unsigned int flags, int oplevel);
 extern void cancel_mode(struct Client *sptr, struct Channel *chptr, char m,
                         const char *param, int *count);
 extern void add_token_to_sendbuf(char *token, size_t *sblenp, int *firstp,
@@ -351,7 +364,8 @@ extern void mode_invite_clear(struct Channel *chan);
 
 extern int mode_parse(struct ModeBuf *mbuf, struct Client *cptr,
                      struct Client *sptr, struct Channel *chptr,
-                     int parc, char *parv[], unsigned int flags);
+                     int parc, char *parv[], unsigned int flags,
+                     struct Membership* member);
 
 #define MODE_PARSE_SET         0x01    /* actually set channel modes */
 #define MODE_PARSE_STRICT      0x02    /* +m +n +t style not supported */
index b16d2849d9822c9a92089423eacf8e04418af2d5..40343af5ae92ace8a57e3adba084ad064ca34e85 100644 (file)
@@ -439,12 +439,14 @@ extern const struct Numeric* get_error_numeric(int err);
 #define ERR_TOOMANYUSERS     519       /* Undernet extension -Kev */
 /*     ERR_OPERONLY         520        unreal */
 #define ERR_MASKTOOWIDE             520        /* Undernet extension -Kev */
-/*     ERR_WHOTRUNC         520        austnet */
-#define ERR_LASTERROR        521
-/*     ERR_LISTSYNTAX       521        dalnet
+/*     ERR_WHOTRUNC         520        austnet
+       ERR_LISTSYNTAX       521        dalnet
        ERR_WHOSYNTAX        522        dalnet
        ERR_WHOLIMEXCEED     523        dalnet */
 
+#define ERR_NOTLOWEROPLEVEL  550
+#define ERR_LASTERROR        551
+
 /*     RPL_LOGON            600        dalnet,unreal
        RPL_LOGOFF           601        dalnet,unreal
        RPL_WATCHOFF         602        dalnet,unreal
index 93c36d7eb484b508bfa85e4a1f4ab6b570a4bbd0..7a553cd245ae8cca181d9803a90f779f7999b2c7 100644 (file)
@@ -83,6 +83,7 @@ IRCD_SRC = \
        client.c \
        crule.c \
        dbuf.c \
+       destruct_event.c \
        fda.c \
        fileio.c \
        gline.c \
index 1db947500ddb790b26822f16fbd03b30f650a73a..1c3d491549ff26c32f8a589c74ed680a7cc9be8b 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "channel.h"
 #include "client.h"
+#include "destruct_event.h"
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
@@ -186,14 +187,11 @@ static char *make_nick_user_ip(char *ipbuf, char *nick, char *name,
 /*
  * Subtract one user from channel i (and free channel
  * block, if channel became empty).
- * Returns: true  (1) if channel still exists
- *          false (0) if the channel was destroyed
+ * Returns: true  (1) if channel still has members.
+ *          false (0) if the channel is now empty.
  */
 int sub1_from_channel(struct Channel* chptr)
 {
-  struct SLink *tmp;
-  struct SLink *obtmp;
-
   if (chptr->users > 1)         /* Can be 0, called for an empty channel too */
   {
     assert(0 != chptr->members);
@@ -201,6 +199,39 @@ int sub1_from_channel(struct Channel* chptr)
     return 1;
   }
 
+  chptr->users = 0;
+
+  /*
+   * Also channels without Apass set need to be kept alive,
+   * otherwise Bad Guys(tm) would be able to takeover
+   * existing channels too easily, and then set an Apass!
+   * However, if a channel without Apass becomes empty
+   * then we try to be kind to them and remove possible
+   * limiting modes.
+   */
+  chptr->mode.mode &= ~MODE_INVITEONLY;
+  chptr->mode.limit = 0;
+  /*
+   * We do NOT reset a possible key or bans because when
+   * the 'channel owners' can't get in because of a key
+   * or ban then apparently there was a fight/takeover
+   * on the channel and we want them to contact IRC opers
+   * who then will educate them on the use of Apass/upass.
+   */
+
+  if (TStime() - chptr->creationtime < 172800) /* Channel younger than 48 hours? */
+    schedule_destruct_event_1m(chptr);         /* Get rid of it in approximately 4-5 minutes */
+  else
+    schedule_destruct_event_48h(chptr);                /* Get rid of it in approximately 48 hours */
+
+  return 0;
+}
+
+int destruct_channel(struct Channel* chptr)
+{
+  struct SLink *tmp;
+  struct SLink *obtmp;
+
   assert(0 == chptr->members);
 
   /* Channel became (or was) empty: Remove channel */
@@ -465,7 +496,7 @@ static int is_banned(struct Client *cptr, struct Channel *chptr,
  * chain.
  */
 void add_user_to_channel(struct Channel* chptr, struct Client* who,
-                                unsigned int flags)
+                                unsigned int flags, int oplevel)
 {
   assert(0 != chptr);
   assert(0 != who);
@@ -484,6 +515,7 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who,
     member->user         = who;
     member->channel      = chptr;
     member->status       = flags;
+    member->oplevel      = oplevel;
 
     member->next_member  = chptr->members;
     if (member->next_member)
@@ -497,6 +529,8 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who,
     member->prev_channel = 0;
     (cli_user(who))->channel = member;
 
+    if (chptr->destruct_event)
+      remove_destruct_event(chptr);
     ++chptr->users;
     ++((cli_user(who))->joined);
   }
@@ -587,6 +621,11 @@ int is_chan_op(struct Client *cptr, struct Channel *chptr)
   return 0;
 }
 
+int is_level0_op(struct Client *cptr, struct Channel *chptr)
+{
+  return 0;
+}
+
 int is_zombie(struct Client *cptr, struct Channel *chptr)
 {
   struct Membership* member;
@@ -684,6 +723,8 @@ const char* find_no_nickchange_channel(struct Client* cptr)
 void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
                           struct Channel *chptr)
 {
+  int previous_parameter = 0;
+
   assert(0 != mbuf);
   assert(0 != pbuf);
   assert(0 != chptr);
@@ -706,27 +747,58 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
   if (chptr->mode.limit) {
     *mbuf++ = 'l';
     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
+    previous_parameter = 1;
   }
 
   if (*chptr->mode.key) {
     *mbuf++ = 'k';
-    if (chptr->mode.limit)
+    if (previous_parameter)
       strcat(pbuf, " ");
     if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
       strcat(pbuf, chptr->mode.key);
     } else
       strcat(pbuf, "*");
+    previous_parameter = 1;
+  }
+  if (*chptr->mode.apass) {
+    *mbuf++ = 'A';
+    if (previous_parameter)
+      strcat(pbuf, " ");
+    if (IsServer(cptr)) {
+      strcat(pbuf, chptr->mode.apass);
+    } else
+      strcat(pbuf, "*");
+    previous_parameter = 1;
+  }
+  if (*chptr->mode.upass) {
+    *mbuf++ = 'u';
+    if (previous_parameter)
+      strcat(pbuf, " ");
+    if (is_level0_op(cptr, chptr) || IsServer(cptr)) {
+      strcat(pbuf, chptr->mode.upass);
+    } else
+      strcat(pbuf, "*");
   }
   *mbuf = '\0';
 }
 
+int compare_member_oplevel(const void *mp1, const void *mp2)
+{
+  struct Membership const* member1 = *(struct Membership const**)mp1;
+  struct Membership const* member2 = *(struct Membership const**)mp2;
+  if (member1->oplevel == member2->oplevel)
+    return 0;
+  return (member1->oplevel < member2->oplevel) ? -1 : 1;
+}
+
 /*
  * send "cptr" a full list of the modes for channel chptr.
  */
 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
 {
+  /* The order in which modes are generated is now mandatory */
   static unsigned int current_flags[4] =
-      { 0, CHFL_CHANOP | CHFL_VOICE, CHFL_VOICE, CHFL_CHANOP };
+      { 0, CHFL_VOICE, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE };
   int                first = 1;
   int                full  = 1;
   int                flag_cnt = 0;
@@ -737,6 +809,10 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
   char modebuf[MODEBUFLEN];
   char parabuf[MODEBUFLEN];
   struct MsgBuf *mb;
+  int                 number_of_ops = 0;
+  int                 opped_members_index = 0;
+  struct Membership** opped_members = NULL;
+  int                 last_oplevel = 0;
 
   assert(0 != cptr);
   assert(0 != chptr); 
@@ -760,7 +836,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
     mb = msgq_make(&me, "%C " TOK_BURST " %H %Tu", &me, chptr,
                   chptr->creationtime);
 
-    if (first && modebuf[1])    /* Add simple modes (iklmnpst)
+    if (first && modebuf[1])    /* Add simple modes (Aiklmnpstu)
                                  if first message */
     {
       /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
@@ -773,52 +849,119 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
     /*
      * Attach nicks, comma seperated " nick[:modes],nick[:modes],..."
      *
-     * Run 4 times over all members, to group the members with the
-     * same mode together
+     * First find all opless members.
+     * Run 2 times over all members, to group the members with
+     * and without voice together.
+     * Then run 2 times over all opped members (which are ordered
+     * by op-level) to also group voice and non-voice together.
      */
-    for (first = 1; flag_cnt < 4;
-         member = chptr->members, new_mode = 1, flag_cnt++)
+    for (first = 1; flag_cnt < 4; new_mode = 1, ++flag_cnt)
     {
-      for (; member; member = member->next_member)
+      while (member)
       {
-        if ((member->status & CHFL_VOICED_OR_OPPED) !=
-            current_flags[flag_cnt])
-          continue;             /* Skip members with different flags */
-       if (msgq_bufleft(mb) < NUMNICKLEN + 4)
-          /* The 4 is a possible ",:ov" */
-        {
-          full = 1;           /* Make sure we continue after
-                                 sending it so far */
-          new_mode = 1;       /* Ensure the new BURST line contains the current
-                                 mode. --Gte */
-          break;              /* Do not add this member to this message */
-        }
-       msgq_append(&me, mb, "%c%C", first ? ' ' : ',', member->user);
-        first = 0;              /* From now on, us comma's to add new nicks */
-
-        /*
-         * Do we have a nick with a new mode ?
-         * Or are we starting a new BURST line?
-         */
-        if (new_mode)
-        {
-          new_mode = 0;
-          if (IsVoicedOrOpped(member)) {
-           char tbuf[4] = ":";
+       if (flag_cnt < 2 && IsChanOp(member))
+       {
+         /*
+          * The first loop (to find all non-voice/op), we count the ops.
+          * The second loop (to find all voiced non-ops), store the ops
+          * in a dynamic array.
+          */
+         if (flag_cnt == 0)
+           ++number_of_ops;
+         else
+           opped_members[opped_members_index++] = member;
+       }
+       /* Only handle the members with the flags that we are interested in. */
+        if ((member->status & CHFL_VOICED_OR_OPPED) == current_flags[flag_cnt])
+       {
+         if (msgq_bufleft(mb) < NUMNICKLEN + 3 + MAXOPLEVELDIGITS)
+           /* The 3 + MAXOPLEVELDIGITS is a possible ",:v999". */
+         {
+           full = 1;           /* Make sure we continue after
+                                  sending it so far */
+           /* Ensure the new BURST line contains the current
+            * ":mode", except when there is no mode yet. */
+           new_mode = (flag_cnt > 0) ? 1 : 0;
+           break;              /* Do not add this member to this message */
+         }
+         msgq_append(&me, mb, "%c%C", first ? ' ' : ',', member->user);
+         first = 0;              /* From now on, use commas to add new nicks */
+
+         /*
+          * Do we have a nick with a new mode ?
+          * Or are we starting a new BURST line?
+          */
+         if (new_mode)
+         {
+           /*
+            * This means we are at the _first_ member that has only
+            * voice, or the first member that has only ops, or the
+            * first member that has voice and ops (so we get here
+            * at most three times, plus once for every start of
+            * a continued BURST line where only these modes is current.
+            * In the two cases where the current mode includes ops,
+            * we need to add the _absolute_ value of the oplevel to the mode.
+            */
+           char tbuf[3 + MAXOPLEVELDIGITS] = ":";
            int loc = 1;
 
-            if (IsChanOp(member))
-             tbuf[loc++] = 'o';
-            if (HasVoice(member))
+           if (HasVoice(member))       /* flag_cnt == 1 or 3 */
              tbuf[loc++] = 'v';
+           if (IsChanOp(member))       /* flag_cnt == 2 or 3 */
+           {
+             /* append the absolute value of the oplevel */
+             loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", member->oplevel);
+             last_oplevel = member->oplevel;
+           }
            tbuf[loc] = '\0';
            msgq_append(&me, mb, tbuf);
-          }
-        }
+           new_mode = 0;
+         }
+         else if (flag_cnt > 1 && last_oplevel != member->oplevel)
+         {
+           /*
+            * This can't be the first member of a (continued) BURST
+            * message because then either flag_cnt == 0 or new_mode == 1
+            * Now we need to append the incremental value of the oplevel.
+            */
+            char tbuf[2 + MAXOPLEVELDIGITS];
+           ircd_snprintf(0, tbuf, sizeof(tbuf), ":%u", member->oplevel - last_oplevel);
+           last_oplevel = member->oplevel;
+           msgq_append(&me, mb, tbuf);
+         }
+       }
+       /* Go to the next `member'. */
+       if (flag_cnt < 2)
+         member = member->next_member;
+       else
+         member = opped_members[++opped_members_index];
       }
       if (full)
-        break;
-    }
+       break;
+
+      /* Point `member' at the start of the list again. */
+      if (flag_cnt == 0)
+      {
+       member = chptr->members;
+       /* Now, after one loop, we know the number of ops and can
+        * allocate the dynamic array with pointer to the ops. */
+       opped_members = (struct Membership**)
+         MyMalloc((number_of_ops + 1) * sizeof(struct Membership*));
+       opped_members[number_of_ops] = NULL;    /* Needed for loop termination */
+      }
+      else
+      {
+       /* At the end of the second loop, sort the opped members with
+        * increasing op-level, so that we will output them in the
+        * correct order (and all op-level increments stay positive) */
+       if (flag_cnt == 1)
+         qsort(opped_members, number_of_ops,
+               sizeof(struct Membership*), compare_member_oplevel);
+       /* The third and fourth loop run only over the opped members. */
+       member = opped_members[(opped_members_index = 0)];
+      }
+
+    } /* loop over 0,+v,+o,+ov */
 
     if (!full)
     {
@@ -844,6 +987,8 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
     msgq_clean(mb);
   }                             /* Continue when there was something
                                  that didn't fit (full==1) */
+  if (opped_members)
+    MyFree(opped_members);
 }
 
 /*
@@ -1342,6 +1487,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
 /*  MODE_KEY,          'k', */
 /*  MODE_BAN,          'b', */
 /*  MODE_LIMIT,                'l', */
+/*  MODE_APASS,                'A', */
+/*  MODE_UPASS,                'u', */
     0x0, 0x0
   };
   int i;
@@ -1416,13 +1563,29 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
        totalbuflen -= IRCD_MAX(5, tmp) + 1;
       }
-    } else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN)) {
+    } else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS)) {
       tmp = strlen(MB_STRING(mbuf, i));
 
       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
        MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
       else {
-       bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_KEY ? 'k' : 'b';
+       char mode_char;
+       switch(MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
+       {
+         case MODE_APASS:
+           mode_char = 'A';
+           break;
+         case MODE_UPASS:
+           mode_char = 'u';
+           break;
+         case MODE_KEY:
+           mode_char = 'k';
+           break;
+         default:
+           mode_char = 'b';
+           break;
+       }
+       bufptr[(*bufptr_i)++] = mode_char;
        totalbuflen -= tmp + 1;
       }
     } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
@@ -1474,6 +1637,10 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
 
+      /* deal with invisible passwords */
+      else if (MB_TYPE(mbuf, i) & (MODE_APASS | MODE_UPASS))
+       build_string(strptr, strptr_i, "*", 0, ' ');
+
       /*
        * deal with limit; note we cannot include the limit parameter if we're
        * removing it
@@ -1566,7 +1733,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
 
       /* deal with modes that take strings */
-      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
+      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
 
       /*
@@ -1794,6 +1961,8 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
     MODE_INVITEONLY,   'i',
     MODE_NOPRIVMSGS,   'n',
     MODE_KEY,          'k',
+    MODE_APASS,                'A',
+    MODE_UPASS,                'u',
 /*  MODE_BAN,          'b', */
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
@@ -1803,6 +1972,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
   int i, bufpos = 0, len;
   int *flag_p;
   char *key = 0, limitbuf[20];
+  char *apass, *upass;
 
   assert(0 != mbuf);
   assert(0 != buf);
@@ -1813,12 +1983,16 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
 
   for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
     if (MB_TYPE(mbuf, i) & MODE_ADD) {
-      add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT);
+      add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS);
 
       if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
        key = MB_STRING(mbuf, i);
       else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
        ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
+      else if (MB_TYPE(mbuf, i) & MODE_UPASS)
+       upass = MB_STRING(mbuf, i);
+      else if (MB_TYPE(mbuf, i) & MODE_APASS)
+       apass = MB_STRING(mbuf, i);
     }
   }
 
@@ -1836,6 +2010,10 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
       build_string(buf, &bufpos, key, 0, ' ');
     else if (buf[i] == 'l')
       build_string(buf, &bufpos, limitbuf, 0, ' ');
+    else if (buf[i] == 'u')
+      build_string(buf, &bufpos, upass, 0, ' ');
+    else if (buf[i] == 'A')
+      build_string(buf, &bufpos, apass, 0, ' ');
   }
 
   buf[bufpos] = '\0';
@@ -1871,12 +2049,15 @@ mode_invite_clear(struct Channel *chan)
 #define DONE_BANLIST   0x04    /* We've sent the ban list */
 #define DONE_NOTOPER   0x08    /* We've sent a "Not oper" error */
 #define DONE_BANCLEAN  0x10    /* We've cleaned bans... */
+#define DONE_UPASS     0x20    /* We've set user pass */
+#define DONE_APASS     0x40    /* We've set admin pass */
 
 struct ParseState {
   struct ModeBuf *mbuf;
   struct Client *cptr;
   struct Client *sptr;
   struct Channel *chptr;
+  struct Membership *member;
   int parc;
   char **parv;
   unsigned int flags;
@@ -2045,6 +2226,168 @@ mode_parse_key(struct ParseState *state, int *flag_p)
   }
 }
 
+/*
+ * Helper function to convert user passes
+ */
+static void
+mode_parse_upass(struct ParseState *state, int *flag_p)
+{
+  char *t_str, *s;
+  int t_len;
+
+  if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
+    return;
+
+  if (state->parc <= 0) { /* warn if not enough args */
+    if (MyUser(state->sptr))
+      need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
+                      "MODE -u");
+    return;
+  }
+
+  t_str = state->parv[state->args_used++]; /* grab arg */
+  state->parc--;
+  state->max_args--;
+
+  /* If they're not an oper, they can't change modes */
+  if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
+    send_notoper(state);
+    return;
+  }
+
+  if (state->done & DONE_UPASS) /* allow upass to be set only once */
+    return;
+  state->done |= DONE_UPASS;
+
+  t_len = PASSLEN + 1;
+
+  /* clean up the upass string */
+  s = t_str;
+  while (*++s > ' ' && *s != ':' && --t_len)
+    ;
+  *s = '\0';
+
+  if (!*t_str) { /* warn if empty */
+    if (MyUser(state->sptr))
+      need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
+                      "MODE -u");
+    return;
+  }
+
+  if (!state->mbuf)
+    return;
+
+  /* can't add a upass if one is set, nor can one remove the wrong upass */
+  if (!(state->flags & MODE_PARSE_FORCE))
+    if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
+       (state->dir == MODE_DEL &&
+        ircd_strcmp(state->chptr->mode.upass, t_str))) {
+      send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
+      return;
+    }
+
+  if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
+      !ircd_strcmp(state->chptr->mode.upass, t_str))
+    return; /* no upass change */
+
+  if (state->flags & MODE_PARSE_BOUNCE) {
+    if (*state->chptr->mode.upass) /* reset old upass */
+      modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
+                         state->chptr->mode.upass, 0);
+    else /* remove new bogus upass */
+      modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
+  } else /* send new upass */
+    modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
+
+  if (state->flags & MODE_PARSE_SET) {
+    if (state->dir == MODE_ADD) /* set the new upass */
+      ircd_strncpy(state->chptr->mode.upass, t_str, PASSLEN);
+    else /* remove the old upass */
+      *state->chptr->mode.upass = '\0';
+  }
+}
+
+/*
+ * Helper function to convert admin passes
+ */
+static void
+mode_parse_apass(struct ParseState *state, int *flag_p)
+{
+  char *t_str, *s;
+  int t_len;
+
+  if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
+    return;
+
+  if (state->parc <= 0) { /* warn if not enough args */
+    if (MyUser(state->sptr))
+      need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
+                      "MODE -A");
+    return;
+  }
+
+  t_str = state->parv[state->args_used++]; /* grab arg */
+  state->parc--;
+  state->max_args--;
+
+  /* If they're not an oper, they can't change modes */
+  if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
+    send_notoper(state);
+    return;
+  }
+
+  if (state->done & DONE_APASS) /* allow apass to be set only once */
+    return;
+  state->done |= DONE_APASS;
+
+  t_len = PASSLEN + 1;
+
+  /* clean up the apass string */
+  s = t_str;
+  while (*++s > ' ' && *s != ':' && --t_len)
+    ;
+  *s = '\0';
+
+  if (!*t_str) { /* warn if empty */
+    if (MyUser(state->sptr))
+      need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
+                      "MODE -A");
+    return;
+  }
+
+  if (!state->mbuf)
+    return;
+
+  /* can't add a apass if one is set, nor can one remove the wrong apass */
+  if (!(state->flags & MODE_PARSE_FORCE))
+    if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
+       (state->dir == MODE_DEL &&
+        ircd_strcmp(state->chptr->mode.apass, t_str))) {
+      send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
+      return;
+    }
+
+  if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
+      !ircd_strcmp(state->chptr->mode.apass, t_str))
+    return; /* no apass change */
+
+  if (state->flags & MODE_PARSE_BOUNCE) {
+    if (*state->chptr->mode.apass) /* reset old apass */
+      modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
+                         state->chptr->mode.apass, 0);
+    else /* remove new bogus apass */
+      modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
+  } else /* send new apass */
+    modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
+
+  if (state->flags & MODE_PARSE_SET) {
+    if (state->dir == MODE_ADD) /* set the new apass */
+      ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
+    else /* remove the old apass */
+      *state->chptr->mode.apass = '\0';
+  }
+}
+
 /*
  * Helper function to convert bans
  */
@@ -2356,17 +2699,41 @@ mode_process_clients(struct ParseState *state)
        }
       }
 
-      /* don't allow local opers to be deopped on local channels */
-      if (MyUser(state->sptr) && state->cli_change[i].client != state->sptr &&
-         IsLocalChannel(state->chptr->chname) &&
-         HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
-       send_reply(state->sptr, ERR_ISOPERLCHAN,
-                  cli_name(state->cli_change[i].client),
-                  state->chptr->chname);
-       continue;
+      /* check deop for local user */
+      if (MyUser(state->sptr)) {
+
+       /* don't allow local opers to be deopped on local channels */
+       if (state->cli_change[i].client != state->sptr &&
+           IsLocalChannel(state->chptr->chname) &&
+           HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
+         send_reply(state->sptr, ERR_ISOPERLCHAN,
+                    cli_name(state->cli_change[i].client),
+                    state->chptr->chname);
+         continue;
+        }
+
+       /* don't allow to deop members with an op level that is <= our own level */
+       if (state->sptr != state->cli_change[i].client          /* but allow to deop oneself */
+               && state->member
+               && OpLevel(member) <= OpLevel(state->member)) {
+           int equal = (OpLevel(member) == OpLevel(state->member));
+           send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
+                      cli_name(state->cli_change[i].client),
+                      state->chptr->chname,
+                      OpLevel(state->member), OpLevel(member),
+                      "deop", equal ? "the same" : "a higher");
+         continue;
+       }
       }
     }
 
+    /* set op-level of member being opped */
+    if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
+       (MODE_ADD | MODE_CHANOP)) {
+      int old_level = (state->member == NULL) ? -1 : OpLevel(state->member);
+      SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + 1));
+    }
+
     /* accumulate the change */
     modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
                        state->cli_change[i].client);
@@ -2382,7 +2749,7 @@ mode_process_clients(struct ParseState *state)
        member->status &= ~(state->cli_change[i].flag &
                            (MODE_CHANOP | MODE_VOICE));
     }
-  } /* for (i = 0; state->cli_change[i].flags; i++) */
+  } /* for (i = 0; state->cli_change[i].flags; i++) */
 }
 
 /*
@@ -2428,7 +2795,8 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
  */
 int
 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
-          struct Channel *chptr, int parc, char *parv[], unsigned int flags)
+          struct Channel *chptr, int parc, char *parv[], unsigned int flags,
+          struct Membership* member)
 {
   static int chan_flags[] = {
     MODE_CHANOP,       'o',
@@ -2440,6 +2808,8 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     MODE_INVITEONLY,   'i',
     MODE_NOPRIVMSGS,   'n',
     MODE_KEY,          'k',
+    MODE_APASS,                'A',
+    MODE_UPASS,                'u',
     MODE_BAN,          'b',
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
@@ -2463,6 +2833,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
   state.cptr = cptr;
   state.sptr = sptr;
   state.chptr = chptr;
+  state.member = member;
   state.parc = parc;
   state.parv = parv;
   state.flags = flags;
@@ -2513,6 +2884,14 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
        mode_parse_key(&state, flag_p);
        break;
 
+      case 'A': /* deal with Admin passes */
+       mode_parse_apass(&state, flag_p);
+       break;
+
+      case 'u': /* deal with user passes */
+       mode_parse_upass(&state, flag_p);
+       break;
+
       case 'b': /* deal with bans */
        mode_parse_ban(&state, flag_p);
        break;
@@ -2525,8 +2904,8 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
       default: /* deal with other modes */
        mode_parse_mode(&state, flag_p);
        break;
-      } /* switch (*modestr) */
-    } /* for (; *modestr; modestr++) */
+      } /* switch (*modestr) */
+    } /* for (; *modestr; modestr++) */
 
     if (state.flags & MODE_PARSE_BURST)
       break; /* don't interpret any more arguments */
@@ -2555,7 +2934,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
        break; /* break out of while loop */
       }
     }
-  } /* while (*modestr) */
+  } /* while (*modestr) */
 
   /*
    * the rest of the function finishes building resultant MODEs; if the
@@ -2592,6 +2971,12 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     if (*state.chptr->mode.key && !(state.done & DONE_KEY))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
                          state.chptr->mode.key, 0);
+    if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
+      modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
+                         state.chptr->mode.upass, 0);
+    if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
+      modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
+                         state.chptr->mode.apass, 0);
   }
 
   if (state.done & DONE_BANCLEAN) /* process bans */
@@ -2674,7 +3059,7 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
       remove_user_from_channel(jbuf->jb_source, chan);
   } else {
     /* Add user to channel */
-    add_user_to_channel(chan, jbuf->jb_source, flags);
+    add_user_to_channel(chan, jbuf->jb_source, flags, 0);
 
     /* send notification to all servers */
     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !IsLocalChannel(chan->chname))
index f4666e4eb9c3b4682f167318896bd55c71c04b66..1300dec900675d5e4df8adef3314c1534c6f4449 100644 (file)
@@ -26,6 +26,7 @@
 #include "class.h"
 #include "client.h"
 #include "crule.h"
+#include "destruct_event.h"
 #include "hash.h"
 #include "ircd_alloc.h"
 #include "ircd_events.h"
@@ -103,6 +104,7 @@ static char   *dpath             = DPATH;
 
 static struct Timer connect_timer; /* timer structure for try_connections() */
 static struct Timer ping_timer; /* timer structure for check_pings() */
+static struct Timer destruct_event_timer; /* timer structure for exec_expired_destruct_events() */
 
 static struct Daemon thisServer  = { 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0 };
 
@@ -617,6 +619,7 @@ int main(int argc, char **argv) {
   IPcheck_init();
   timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1);
   timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1);
+  timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60);
 
   CurrentTime = time(NULL);
 
index 8b84667083f49c3d8f07201c0bbfa39bab518c04..0f1e8bfcdad2e172e01138c7a514744ea4575ac9 100644 (file)
 #include "send.h"
 #include "struct.h"
 #include "support.h"
+#include "ircd_snprintf.h"
 
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 
 /*
  * ms_burst - server message handler
  *   parv[n] = %<ban> <ban> <ban> ...
  * If parv[n] starts with another character:
  *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
- *   where <mode> is the channel mode (ov) of nick and all following nicks.
+ *   where <mode> defines the mode and op-level
+ *   for nick and all following nicks until the
+ *   next <mode> field.
+ *   Digits in the <mode> field have of two meanings:
+ *   1) if it is the first field in this BURST message
+ *      that contains digits, and/or when a 'v' is
+ *      present in the <mode>:
+ *      The absolute value of the op-level.
+ *   2) if there are only digits in this field and
+ *      it is not the first field with digits:
+ *      An op-level increment relative to the previous
+ *      op-level.
+ *   First all modeless nicks must be emmitted,
+ *   then all combinations of modes without ops
+ *   (currently that is only 'v') followed by the same
+ *   series but then with ops (currently 'o','ov').
  *
  * Example:
- * "S BURST #channel 87654321 +ntkl key 123 AAA,AAB:o,BAA,BAB:ov :%ban1 ban2"
+ * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
+ *
+ * <mode> list example:
+ *
+ * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
+ *
+ * means
+ *
+ *  xxx                // first modeless nicks
+ *  sss +v     // then opless nicks
+ *  ttt +v     // no ":<mode>": everything stays the same
+ *  aaa -123   // first field with digit: absolute value
+ *  bbb -123
+ *  ccc -125   // only digits, not first field: increment
+ *  ddd -125
+ *  kkk -2 +v  // field with a 'v': absolute value
+ *  lll -4 +v  // only digits: increment
+ *  mmm -4 +v
  *
  * Anti net.ride code.
  *
- * When the channel already exist, and its TS is larger then
+ * When the channel already exist, and its TS is larger than
  * the TS in the BURST message, then we cancel all existing modes.
  * If its is smaller then the received BURST message is ignored.
  * If it's equal, then the received modes are just added.
@@ -190,7 +224,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     switch (*parv[param]) {
     case '+': /* parameter introduces a mode string */
       param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
-                         parv + param, parse_flags);
+                         parv + param, parse_flags, NULL);
       break;
 
     case '%': /* parameter contains bans */
@@ -266,8 +300,10 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       {
        struct Client *acptr;
        char *nicklist = parv[param], *p = 0, *nick, *ptr;
-       int default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+       int current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
        int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+       int oplevel = -1;       /* Mark first field with digits: means the same as 'o' (but with level). */
+       int last_oplevel = 0;
 
        for (nick = ircd_strtok(&p, nicklist, ","); nick;
             nick = ircd_strtok(&p, 0, ",")) {
@@ -276,12 +312,44 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
            *ptr++ = '\0';
 
            if (parse_flags & MODE_PARSE_SET) {
-             for (default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; *ptr;
-                  ptr++) {
-               if (*ptr == 'o') /* has oper status */
-                 default_mode = (default_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
-               else if (*ptr == 'v') /* has voice status */
-                 default_mode |= CHFL_VOICE;
+             int current_mode_needs_reset;
+             for (current_mode_needs_reset = 1; *ptr; ptr++) {
+               if (*ptr == 'o') { /* has oper status */
+                 /*
+                  * An 'o' is pre-oplevel protocol, so this is only for
+                  * backwards compatibility.  Give them an op-level of
+                  * MAXOPLEVEL so everyone can deop them.
+                  */
+                 oplevel = MAXOPLEVEL;
+                 if (current_mode_needs_reset) {
+                   current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                   current_mode_needs_reset = 0;
+                 }
+                 current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
+               }
+               else if (*ptr == 'v') { /* has voice status */
+                 if (current_mode_needs_reset) {
+                   current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                   current_mode_needs_reset = 0;
+                 }
+                 current_mode |= CHFL_VOICE;
+                 oplevel = -1; /* subsequential digits are an absolute op-level value. */
+                }
+               else if (isdigit(*ptr)) {
+                 int level_increment = 0;
+                 if (oplevel == -1) { /* op-level is absolute value? */
+                   if (current_mode_needs_reset) {
+                     current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
+                     current_mode_needs_reset = 0;
+                   }
+                   oplevel = 0;
+                 }
+                 current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
+                 do {
+                   level_increment = 10 * level_increment + *ptr++ - '0';
+                 } while(isdigit(*ptr));
+                 oplevel += level_increment;
+               }
                else /* I don't recognize that flag */
                  break; /* so stop processing */
              }
@@ -298,17 +366,22 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
          for (ptr = nick; *ptr; ptr++) /* store nick */
            nickstr[nickpos++] = *ptr;
 
-         if (default_mode != last_mode) { /* if mode changed... */
-           last_mode = default_mode;
+         if (current_mode != last_mode) { /* if mode changed... */
+           last_mode = current_mode;
+           last_oplevel = oplevel;
 
            nickstr[nickpos++] = ':'; /* add a specifier */
-           if (default_mode & CHFL_CHANOP)
-             nickstr[nickpos++] = 'o';
-           if (default_mode & CHFL_VOICE)
+           if (current_mode & CHFL_VOICE)
              nickstr[nickpos++] = 'v';
+           if (current_mode & CHFL_CHANOP)
+             nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
+         } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
+           nickstr[nickpos++] = ':'; /* add a specifier */
+           nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
+            last_oplevel = oplevel;
          }
 
-         add_user_to_channel(chptr, acptr, default_mode);
+         add_user_to_channel(chptr, acptr, current_mode, oplevel);
          sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr);
        }
       }
index d13be04c5cc1c050524cb9e2a439f25f0a262960..68ac9e11490161c7f0eb0b235b9579e03c8d2e74 100644 (file)
@@ -127,11 +127,8 @@ int ms_end_of_burst(struct Client* cptr, struct Client* sptr, int parc, char* pa
     next_chan = chan->next;
 
     if (!chan->members) { /* empty channel */
-      if (!(chan->mode.mode & MODE_BURSTADDED))
-       sendto_opmask_butone(0, SNO_OLDSNO, "Empty channel %H not added by "
-                            "BURST!", chan);
-
-      sub1_from_channel(chan); /* ok, nuke channel now */
+      if ((chan->mode.mode & MODE_BURSTADDED))
+       sub1_from_channel(chan); /* New empty channel, schedule it for removal. */
     }
 
     chan->mode.mode &= ~MODE_BURSTADDED;
index 9381d2dc898e63d41968beb1d3c209c335dffc55..e8f9bebe7172bdc1aecaa038752e28b0a2969f9e 100644 (file)
@@ -248,14 +248,15 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
          send_reply(sptr, i, chptr->chname);
          continue;
        }
-      } /* else if ((i = can_join(sptr, chptr, keys))) */
+      } /* else if ((i = can_join(sptr, chptr, keys))) */
 
       joinbuf_join(&join, chptr, flags);
     } else if (!(chptr = get_channel(sptr, name, CGT_CREATE)))
       continue; /* couldn't get channel */
     else if (check_target_limit(sptr, chptr, chptr->chname, 1)) {
       /* Note: check_target_limit will only ever return 0 here */
-      sub1_from_channel(chptr); /* created it... */
+      chptr->members = 0;
+      destruct_channel(chptr); /* created it... */
       continue;
     } else
       joinbuf_join(&create, chptr, flags);
index f32ea255a328c27b72d799563a9590ff59c5caa9..fc77ac7da64c6e1dd22f1df6d7232c4d66afcbab 100644 (file)
@@ -137,18 +137,19 @@ m_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
                    MODEBUF_DEST_HACK4));  /* Send HACK(4) notice */
       mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
                 (MODE_PARSE_SET |    /* Set the mode */
-                 MODE_PARSE_FORCE)); /* Force it to take */
+                 MODE_PARSE_FORCE),  /* Force it to take */
+                 member);
       return modebuf_flush(&mbuf);
     } else
       mode_parse(0, cptr, sptr, chptr, parc - 2, parv + 2,
-                (member ? MODE_PARSE_NOTOPER : MODE_PARSE_NOTMEMBER));
+                (member ? MODE_PARSE_NOTOPER : MODE_PARSE_NOTMEMBER), member);
     return 0;
   }
 
   modebuf_init(&mbuf, sptr, cptr, chptr,
               (MODEBUF_DEST_CHANNEL | /* Send mode to channel */
                MODEBUF_DEST_SERVER)); /* Send mode to servers */
-  mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, MODE_PARSE_SET);
+  mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, MODE_PARSE_SET, member);
   return modebuf_flush(&mbuf);
 }
 
@@ -185,7 +186,8 @@ ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
               (MODE_PARSE_SET    | /* Set the mode */
                MODE_PARSE_STRICT | /* Interpret it strictly */
-               MODE_PARSE_FORCE)); /* And force it to be accepted */
+               MODE_PARSE_FORCE),  /* And force it to be accepted */
+               NULL);
   } else {
     if (!(member = find_member_link(chptr, sptr)) || !IsChanOp(member)) {
       modebuf_init(&mbuf, sptr, cptr, chptr,
@@ -195,7 +197,8 @@ ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
                    MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */
       mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
                 (MODE_PARSE_STRICT |  /* Interpret it strictly */
-                 MODE_PARSE_BOUNCE)); /* And bounce the MODE */
+                 MODE_PARSE_BOUNCE),  /* And bounce the MODE */
+                 member);
     } else {
       modebuf_init(&mbuf, sptr, cptr, chptr,
                   (MODEBUF_DEST_CHANNEL | /* Send mode to clients */
@@ -203,7 +206,8 @@ ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
                 (MODE_PARSE_SET    | /* Set the mode */
                  MODE_PARSE_STRICT | /* Interpret it strictly */
-                 MODE_PARSE_FORCE)); /* And force it to be accepted */
+                 MODE_PARSE_FORCE),  /* And force it to be accepted */
+                 member);
     }
   }
 
index 01db05a54e496cd0017867a930e60a6d325ad433..ddfd734955d1fa648ce9216af3021b6d97daf775 100644 (file)
@@ -123,7 +123,8 @@ int ms_opmode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
             (MODE_PARSE_SET    | /* Set the modes on the channel */
              MODE_PARSE_STRICT | /* Be strict about it */
-             MODE_PARSE_FORCE)); /* And force them to be accepted */
+             MODE_PARSE_FORCE),  /* And force them to be accepted */
+             NULL);
 
   modebuf_flush(&mbuf); /* flush the modes */
 
@@ -166,7 +167,8 @@ int mo_opmode(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 
   mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
             (MODE_PARSE_SET |    /* set the modes on the channel */
-             MODE_PARSE_FORCE)); /* And force them to be accepted */
+             MODE_PARSE_FORCE),  /* And force them to be accepted */
+             NULL);
 
   modebuf_flush(&mbuf); /* flush the modes */
 
index 0fdcc6cbe9da25bd1d1ab7c36008e83e002055ef..5ad955c4ed01317c12735e49505cda3d0f91c60b 100644 (file)
@@ -36,7 +36,7 @@ static Numeric replyTable[] = {
 /* 003 */
   { RPL_CREATED, ":This server was created %s", "003" },
 /* 004 */
-  { RPL_MYINFO, "%s %s dioswkgx biklmnopstvr bklov", "004" },
+  { RPL_MYINFO, "%s %s dioswkgx Abiklmnopstuvr Abklouv", "004" },
 /* 005 */
   { RPL_ISUPPORT, "%s :are supported by this server", "005" },
 /* 006 */
@@ -1132,7 +1132,7 @@ static Numeric replyTable[] = {
 /* 549 */
   { 0 },
 /* 550 */
-  { 0 },
+  { ERR_NOTLOWEROPLEVEL, "%s %s %hu %hu :Cannot %s someone with %s op-level", "550" },
 /* 551 */
   { 0 },
 /* 552 */
index 62a3372acd22ddbb858e8da41ed288840ebc05b0..1344f867220281693ddc3eaadce43a3763244e76 100644 (file)
@@ -476,11 +476,11 @@ int exit_client(struct Client *cptr,    /* Connection being handled by
                           get_client_name(killer, HIDE_IP));
     sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
                         cli_serv(victim)->up, victim, comment);
-  }
 
 #ifdef HEAD_IN_SAND_MAP    
-  map_update(victim);
+    map_update(victim);
 #endif
+  }
 
   /*
    * First generate the needed protocol for the other server links