Send oplevels correctly during a burst.
authorMichael Poole <mdpoole@troilus.org>
Thu, 7 Jan 2010 02:33:21 +0000 (21:33 -0500)
committerMichael Poole <mdpoole@troilus.org>
Thu, 7 Jan 2010 02:33:21 +0000 (21:33 -0500)
src/hash.c (AddChannelUser): Initialize oplevel to the maximum, not -1.
src/proto-p10.c (parse_oplevel): Move up for easier inlining.
  (modeNode_sort): New function.
  (irc_burst): Use it to sort the membership list for channels so the
    members' oplevels can be sent correctly.
  (cmd_burst): Simplify the handling of oplevels.
  (mod_chanmode_parse): Only change the member's oplevel when opping or
    deopping -- otherwise +v would set it to MAXOPLEVEL!

src/hash.c
src/proto-p10.c

index a4dd6fb6fbc0b7ddfffbae5af600231cf0bd2f9b..75769fa09706c6e4ebdf5962af50b6f0bb431c8c 100644 (file)
@@ -485,7 +485,7 @@ AddChannelUser(struct userNode *user, struct chanNode* channel)
         mNode->channel = channel;
         mNode->user = user;
         mNode->modes = 0;
-        mNode->oplevel = -1;
+        mNode->oplevel = MAXOPLEVEL;
         mNode->idle_since = now;
 
         /* Add modeNode to channel and to user.
index bd8688509674e592601eeb7a4cf3708f7d1e81ab..86cd33574f645488914ec4dc9c998b1448043b6d 100644 (file)
@@ -299,7 +299,17 @@ static struct userNode *AddUser(struct server* uplink, const char *nick, const c
 
 extern int off_channel;
 
-static int parse_oplevel(char *str);
+/*
+ * Oplevel parsing
+ */
+static int
+parse_oplevel(char *str)
+{
+    int oplevel = 0;
+    while (isdigit(*str))
+        oplevel = oplevel * 10 + *str++ - '0';
+    return oplevel;
+}
 
 /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the
  * server and Y's indentifying the client on that server. */
@@ -666,6 +676,32 @@ irc_ungline(const char *mask)
     putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
 }
 
+/* Return negative if *(struct modeNode**)pa is "less than" pb,
+ * positive if pa is "larger than" pb.  Comparison is based on sorting
+ * so that non-voiced/non-opped users are first, voiced-only users are
+ * next, and the "strongest" oplevels are before "weaker" oplevels.
+ * Within those sets, ordering is arbitrary.
+ */
+static int
+modeNode_sort(const void *pa, const void *pb)
+{
+        struct modeNode *a = *(struct modeNode**)pa;
+        struct modeNode *b = *(struct modeNode**)pb;
+
+        if (a->modes & MODE_CHANOP) {
+            if (!(b->modes & MODE_CHANOP))
+                return 1;
+            else if ((a->modes & MODE_VOICE) != (b->modes & MODE_VOICE))
+                return (a->modes & MODE_VOICE) - (b->modes & MODE_VOICE);
+            else if (a->oplevel != b->oplevel)
+                return a->oplevel - b->oplevel;
+        } else if (b->modes & MODE_CHANOP)
+            return -1;
+        else if ((a->modes & MODE_VOICE) != (b->modes & MODE_VOICE))
+            return (a->modes & MODE_VOICE) - (b->modes & MODE_VOICE);
+        return (a < b) ? -1 : 1;
+}
+
 static void
 irc_burst(struct chanNode *chan)
 {
@@ -673,7 +709,9 @@ irc_burst(struct chanNode *chan)
     int pos, base_len, len;
     struct modeNode *mn;
     struct banNode *bn;
-    long last_mode=-1;
+    int last_oplevel = 0;
+    int last_mode = 0;
+    int new_modes;
     unsigned int first_ban;
     unsigned int n;
 
@@ -685,6 +723,9 @@ irc_burst(struct chanNode *chan)
     if (len > 0 && chan->members.used > 0)
         burst_line[pos++] = ' ';
 
+    /* sort the users for oplevel-sending purposes */
+    qsort(chan->members.list, chan->members.used, sizeof(chan->members.list[0]), modeNode_sort);
+
     /* dump the users */
     for (n=0; n<chan->members.used; n++) {
         mn = chan->members.list[n];
@@ -692,17 +733,32 @@ irc_burst(struct chanNode *chan)
             burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
             putsock("%s", burst_line);
             pos = base_len;
-            last_mode = -1;
+            last_mode = 0;
+            last_oplevel = 0;
         }
         memcpy(burst_line+pos, mn->user->numeric, strlen(mn->user->numeric));
         pos += strlen(mn->user->numeric);
-        if (mn->modes && (mn->modes != last_mode)) {
-            last_mode = mn->modes;
+        new_modes = mn->modes & (MODE_CHANOP | MODE_VOICE);
+        if (new_modes != last_mode) {
+            last_mode = new_modes;
             burst_line[pos++] = ':';
-            if (last_mode & MODE_CHANOP)
-                burst_line[pos++] = 'o';
-            if (last_mode & MODE_VOICE)
+            if (new_modes & MODE_VOICE)
                 burst_line[pos++] = 'v';
+            /* Note: :vNNN (oplevel NNN with voice) resets the
+             * implicit oplevel back to zero, so we always use the raw
+             * oplevel value here.  Read ircu's m_burst.c for more
+             * examples.
+             */
+            if (new_modes & MODE_CHANOP) {
+                last_oplevel = mn->oplevel;
+                if (mn->oplevel < MAXOPLEVEL)
+                    pos += sprintf(burst_line + pos, "%u", mn->oplevel);
+                else
+                    burst_line[pos++] = 'o';
+            }
+        } else if ((last_mode & MODE_CHANOP) && (mn->oplevel != last_oplevel)) {
+            pos += sprintf(burst_line + pos, ":%u", mn->oplevel - last_oplevel);
+            last_oplevel = mn->oplevel;
         }
         if ((n+1)<chan->members.used)
             burst_line[pos++] = ',';
@@ -1204,7 +1260,7 @@ static CMD_FUNC(cmd_burst)
     struct userNode *un;
     struct modeNode *mNode;
     long mode;
-    int oplevel = -1;
+    int oplevel = 0;
     char *user, *end, sep;
     unsigned long in_timestamp;
 
@@ -1248,16 +1304,13 @@ static CMD_FUNC(cmd_burst)
             while ((sep = *end++)) {
                 if (sep == 'o') {
                     mode |= MODE_CHANOP;
-                    oplevel = -1;
+                    oplevel = MAXOPLEVEL;
                 } else if (sep == 'v') {
                     mode |= MODE_VOICE;
-                    oplevel = -1;
+                    oplevel = 0;
                 } else if (isdigit(sep)) {
                     mode |= MODE_CHANOP;
-                    if (oplevel >= 0)
-                        oplevel += parse_oplevel(end);
-                    else
-                        oplevel = parse_oplevel(end);
+                    oplevel += parse_oplevel(end - 1);
                     while (isdigit(*end)) end++;
                 } else
                     break;
@@ -2409,7 +2462,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             else if (channel->modes & MODE_UPASS)
                 oplevel = base_oplevel + 1;
             else
-                oplevel = -1;
+                oplevel = MAXOPLEVEL;
 
             /* Check that oplevel is within bounds. */
             if (oplevel > MAXOPLEVEL)
@@ -2430,8 +2483,9 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 continue;
             if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
             {
-                /* Apply the oplevel change */
-                change->args[ch_arg].u.member->oplevel = oplevel;
+                /* Apply the oplevel change if the user is being (de-)opped */
+                if (modes[0][ii] == 'o')
+                    change->args[ch_arg].u.member->oplevel = oplevel;
                 ch_arg++;
             }
             break;
@@ -2848,15 +2902,3 @@ send_burst(void)
     for (it = dict_first(channels); it; it = iter_next(it))
         dict_insert(unbursted_channels, iter_key(it), iter_data(it));
 }
-
-/*
- * Oplevel parsing
- */
-static int
-parse_oplevel(char *str)
-{
-    int oplevel = 0;
-    while (isdigit(*str))
-        oplevel = oplevel * 10 + *str++ - '0';
-    return oplevel;
-}