]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
Stage 2, exempt support
[irc/evilnet/x3.git] / src / proto-p10.c
index 0138662aab17d39e6120c5d62ff4448dbf0b4e5e..5173c04e01d9674be0a9ab5ec2b241176fe21323 100644 (file)
 #define CMD_VERSION             "VERSION"
 #define CMD_WALLCHOPS           "WALLCHOPS"
 #define CMD_WALLOPS             "WALLOPS"
 #define CMD_VERSION             "VERSION"
 #define CMD_WALLCHOPS           "WALLCHOPS"
 #define CMD_WALLOPS             "WALLOPS"
+#define CMD_WALLHOPS            "WALLHOPS"
 #define CMD_WALLUSERS           "WALLUSERS"
 #define CMD_WALLVOICES          "WALLVOICES"
 #define CMD_WALLUSERS           "WALLUSERS"
 #define CMD_WALLVOICES          "WALLVOICES"
+#define CMD_WALLHOPS            "WALLHOPS"
 #define CMD_WHO                 "WHO"
 #define CMD_WHOIS               "WHOIS"
 #define CMD_WHOWAS              "WHOWAS"
 #define CMD_WHO                 "WHO"
 #define CMD_WHOIS               "WHOIS"
 #define CMD_WHOWAS              "WHOWAS"
 #define TOK_VERSION             "V"
 #define TOK_WALLCHOPS           "WC"
 #define TOK_WALLOPS             "WA"
 #define TOK_VERSION             "V"
 #define TOK_WALLCHOPS           "WC"
 #define TOK_WALLOPS             "WA"
+#define TOK_WALLHOPS            "WH"
 #define TOK_WALLUSERS           "WU"
 #define TOK_WALLVOICES          "WV"
 #define TOK_WALLUSERS           "WU"
 #define TOK_WALLVOICES          "WV"
+#define TOK_WALLHOPS            "WH"
 #define TOK_WHO                 "H"
 #define TOK_WHOIS               "W"
 #define TOK_WHOWAS              "X"
 #define TOK_WHO                 "H"
 #define TOK_WHOIS               "W"
 #define TOK_WHOWAS              "X"
 #define P10_VERSION             TYPE(VERSION)
 #define P10_WALLCHOPS           TYPE(WALLCHOPS)
 #define P10_WALLOPS             TYPE(WALLOPS)
 #define P10_VERSION             TYPE(VERSION)
 #define P10_WALLCHOPS           TYPE(WALLCHOPS)
 #define P10_WALLOPS             TYPE(WALLOPS)
+#define P10_WALLHOPS            TYPE(WALLHOPS)
 #define P10_WALLUSERS           TYPE(WALLUSERS)
 #define P10_WALLVOICES          TYPE(WALLVOICES)
 #define P10_WHO                 TYPE(WHO)
 #define P10_WALLUSERS           TYPE(WALLUSERS)
 #define P10_WALLVOICES          TYPE(WALLVOICES)
 #define P10_WHO                 TYPE(WHO)
@@ -548,8 +553,8 @@ irc_introduce(const char *passwd)
 void
 irc_gline(struct server *srv, struct gline *gline)
 {
 void
 irc_gline(struct server *srv, struct gline *gline)
 {
-    putsock("%s " P10_GLINE " %s +%s %ld :%s",
-            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->reason);
+    putsock("%s " P10_GLINE " %s +%s %ld :<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->issuer, gline->reason);
 }
 
 void
 }
 
 void
@@ -574,6 +579,7 @@ irc_burst(struct chanNode *chan)
     int pos, base_len, len;
     struct modeNode *mn;
     struct banNode *bn;
     int pos, base_len, len;
     struct modeNode *mn;
     struct banNode *bn;
+    struct exemptNode *en;
     long last_mode=-1;
     unsigned int n;
 
     long last_mode=-1;
     unsigned int n;
 
@@ -600,6 +606,8 @@ irc_burst(struct chanNode *chan)
             burst_line[pos++] = ':';
             if (last_mode & MODE_CHANOP)
                 burst_line[pos++] = 'o';
             burst_line[pos++] = ':';
             if (last_mode & MODE_CHANOP)
                 burst_line[pos++] = 'o';
+            if (last_mode & MODE_HALFOP)
+                burst_line[pos++] = 'h';
             if (last_mode & MODE_VOICE)
                 burst_line[pos++] = 'v';
         }
             if (last_mode & MODE_VOICE)
                 burst_line[pos++] = 'v';
         }
@@ -632,6 +640,33 @@ irc_burst(struct chanNode *chan)
             burst_line[pos++] = ' ';
         }
     }
             burst_line[pos++] = ' ';
         }
     }
+    if (chan->exemptlist.used) {
+        /* dump the exempt */
+        if (pos+2+strlen(chan->exemptlist.list[0]->exempt) > 505) {
+            burst_line[pos-1] = 0;
+            putsock("%s", burst_line);
+            pos = base_len;
+        } else {
+            burst_line[pos++] = ' ';
+        }
+
+        burst_line[pos++] = ' ';
+        burst_line[pos++] = '~';
+        burst_line[pos++] = ' ';
+        base_len = pos;
+        for (n=0; n<chan->exemptlist.used; n++) {
+            en = chan->exemptlist.list[n];
+            len = strlen(en->exempt);
+            if (pos+len+1 > 510) {
+                burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
+                putsock("%s", burst_line);
+                pos = base_len;
+            }
+            memcpy(burst_line+pos, en->exempt, len);
+            pos += len;
+            burst_line[pos++] = ' ';
+        }
+    }
     /* print the last line */
     burst_line[pos] = 0;
     putsock("%s", burst_line);
     /* print the last line */
     burst_line[pos] = 0;
     putsock("%s", burst_line);
@@ -937,7 +972,7 @@ create_helper(char *name, void *data)
         return;
     }
 
         return;
     }
 
-    AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL));
+    AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL, NULL));
 }
 
 static CMD_FUNC(cmd_create)
 }
 
 static CMD_FUNC(cmd_create)
@@ -1034,44 +1069,98 @@ static CMD_FUNC(cmd_fakehost)
 static CMD_FUNC(cmd_burst)
 {
     extern int rel_age;
 static CMD_FUNC(cmd_burst)
 {
     extern int rel_age;
-    char modes[MAXLEN], *members = "", *banlist = NULL;
-    unsigned int next = 3, res = 1;
+    char modes[MAXLEN], *members = "";
+    static char exemptlist[MAXLEN], banlist[MAXLEN];
+    unsigned int next = 3;
+    unsigned int res = 1;
+    int ctype = 0, echeck = 0, bcheck = 0;
     struct chanNode *cNode;
     struct userNode *un;
     struct modeNode *mNode;
     long mode;
     char *user, *end, sep;
     time_t in_timestamp;
     struct chanNode *cNode;
     struct userNode *un;
     struct modeNode *mNode;
     long mode;
     char *user, *end, sep;
     time_t in_timestamp;
+    char* parm = NULL;
 
     if (argc < 3)
         return 0;
     modes[0] = 0;
 
     if (argc < 3)
         return 0;
     modes[0] = 0;
+
+    exemptlist[0] = 0;
+    banlist[0] = 0;
+
     while (next < argc) {
         switch (argv[next][0]) {
         case '+': {
             const char *pos;
             int n_modes;
     while (next < argc) {
         switch (argv[next][0]) {
         case '+': {
             const char *pos;
             int n_modes;
-            for (pos=argv[next], n_modes = 1; *pos; pos++)
+            for (pos=argv[next], n_modes = 1; *pos; pos++) {
                 if ((*pos == 'k') || (*pos == 'l'))
                     n_modes++;
                 if ((*pos == 'k') || (*pos == 'l'))
                     n_modes++;
+            }
             unsplit_string(argv+next, n_modes, modes);
             next += n_modes;
             break;
         }
             unsplit_string(argv+next, n_modes, modes);
             next += n_modes;
             break;
         }
-        case '%': banlist = argv[next++]+1; break;
-        default: members = argv[next++]; break;
+        case '%': {
+            for(parm = mysep(&argv[next], " "); /* parm = first param */
+                  parm;   /* While param is not null */
+                  parm = mysep(&argv[next], " ")  /* parm = next param */
+               )
+            {
+              switch (parm[0]) {
+                case '%': {
+                  ctype = 1;
+                  break;
+                }
+                case '~': {
+                  ctype = 2;
+                  break;
+                }
+                default: {
+                  break;
+                }
+              }
+              if (ctype == 1) {
+                if (bcheck == 0) {
+                  /* strip % char off start of very first ban */
+                  strncat(banlist, strtok(parm, "%"), sizeof(banlist) - 1 - strlen(banlist));
+                  strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
+                  bcheck = 1;
+                } else {
+                  strncat(banlist, parm, sizeof(banlist) - 1 - strlen(banlist));
+                  strncat(banlist, " ", sizeof(banlist) - 1 - strlen(banlist));
+                }
+              } else if (ctype == 2) {
+                if (echeck == 0) {
+                   echeck = 1;
+                } else {
+                  strncat(exemptlist, parm, sizeof(exemptlist) - 1 - strlen(exemptlist));
+                  strncat(exemptlist, " ", sizeof(exemptlist) - 1 - strlen(exemptlist));
+                }
+              }
+            }
+            next++;
+            break;
+        }
+        default: {
+            members = argv[next++];
+            break;
         }
         }
+      }
     }
 
     }
 
+
     in_timestamp = atoi(argv[2]);
     if ((cNode = dict_find(unbursted_channels, argv[1], NULL))) {
         cNode->timestamp = in_timestamp;
         dict_remove(unbursted_channels, cNode->name);
         irc_burst(cNode);
     }
     in_timestamp = atoi(argv[2]);
     if ((cNode = dict_find(unbursted_channels, argv[1], NULL))) {
         cNode->timestamp = in_timestamp;
         dict_remove(unbursted_channels, cNode->name);
         irc_burst(cNode);
     }
-    cNode = AddChannel(argv[1], in_timestamp, modes, banlist);
 
     /* Burst channel members in now. */
 
     /* Burst channel members in now. */
+    cNode = AddChannel(argv[1], in_timestamp, modes, banlist, exemptlist);
+
     for (user = members, sep = *members, mode = 0; sep; user = end) {
         for (end = user + 3; isalnum(*end) || *end == '[' || *end == ']'; end++) ;
         sep = *end++; end[-1] = 0;
     for (user = members, sep = *members, mode = 0; sep; user = end) {
         for (end = user + 3; isalnum(*end) || *end == '[' || *end == ']'; end++) ;
         sep = *end++; end[-1] = 0;
@@ -1080,6 +1169,8 @@ static CMD_FUNC(cmd_burst)
             while ((sep = *end++)) {
                 if (sep == 'o')
                     mode |= MODE_CHANOP;
             while ((sep = *end++)) {
                 if (sep == 'o')
                     mode |= MODE_CHANOP;
+                else if (sep == 'h')
+                    mode |= MODE_HALFOP;
                 else if (sep == 'v')
                     mode |= MODE_VOICE;
                 else
                 else if (sep == 'v')
                     mode |= MODE_VOICE;
                 else
@@ -1576,9 +1667,13 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_WALLCHOPS, cmd_dummy);
     dict_insert(irc_func_dict, CMD_WALLVOICES, cmd_dummy);
     dict_insert(irc_func_dict, TOK_WALLVOICES, cmd_dummy);
     dict_insert(irc_func_dict, TOK_WALLCHOPS, cmd_dummy);
     dict_insert(irc_func_dict, CMD_WALLVOICES, cmd_dummy);
     dict_insert(irc_func_dict, TOK_WALLVOICES, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_WALLHOPS, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_WALLHOPS, cmd_dummy);
     /* Ignore opers being silly. */
     dict_insert(irc_func_dict, CMD_WALLOPS, cmd_dummy);
     dict_insert(irc_func_dict, TOK_WALLOPS, cmd_dummy);
     /* Ignore opers being silly. */
     dict_insert(irc_func_dict, CMD_WALLOPS, cmd_dummy);
     dict_insert(irc_func_dict, TOK_WALLOPS, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_WALLHOPS, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_WALLHOPS, cmd_dummy);
     /* We have reliable clock!  Always!  Wraaa! */
     dict_insert(irc_func_dict, CMD_SETTIME, cmd_dummy);
     dict_insert(irc_func_dict, TOK_SETTIME, cmd_dummy);
     /* We have reliable clock!  Always!  Wraaa! */
     dict_insert(irc_func_dict, CMD_SETTIME, cmd_dummy);
     dict_insert(irc_func_dict, TOK_SETTIME, cmd_dummy);
@@ -2114,6 +2209,14 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'r': do_chan_mode(MODE_REGONLY); break;
         case 's': do_chan_mode(MODE_SECRET); break;
         case 't': do_chan_mode(MODE_TOPICLIMIT); break;
         case 'r': do_chan_mode(MODE_REGONLY); break;
         case 's': do_chan_mode(MODE_SECRET); break;
         case 't': do_chan_mode(MODE_TOPICLIMIT); break;
+        case 'S': do_chan_mode(MODE_STRIPCOLOR); break;
+        case 'M': do_chan_mode(MODE_MODUNREG); break;
+        case 'N': do_chan_mode(MODE_NONOTICE); break;
+        case 'Q': do_chan_mode(MODE_NOQUITMSGS); break;
+        case 'T': do_chan_mode(MODE_NOAMSG); break;
+        case 'O': do_chan_mode(MODE_OPERSONLY); break;
+//   uncomment this when ssl is enabled on the network.
+//        case 'z': do_chan_mode(MODE_SSLONLY); break;
 #undef do_chan_mode
         case 'l':
             if (add) {
 #undef do_chan_mode
         case 'l':
             if (add) {
@@ -2151,14 +2254,31 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 change->args[ch_arg].mode |= MODE_REMOVE;
             change->args[ch_arg++].hostmask = modes[in_arg++];
             break;
                 change->args[ch_arg].mode |= MODE_REMOVE;
             change->args[ch_arg++].hostmask = modes[in_arg++];
             break;
-        case 'o': case 'v':
+        case 'e':
+            if (!(flags & MCP_ALLOW_OVB))
+                goto error;
+            if (in_arg >= argc)
+                goto error;
+            change->args[ch_arg].mode = MODE_EXEMPT;
+            if (!add)
+                change->args[ch_arg].mode |= MODE_REMOVE;
+            change->args[ch_arg++].hostmask = modes[in_arg++];
+            break;
+        case 'o': case 'h': case 'v':
         {
             struct userNode *victim;
             if (!(flags & MCP_ALLOW_OVB))
                 goto error;
             if (in_arg >= argc)
                 goto error;
         {
             struct userNode *victim;
             if (!(flags & MCP_ALLOW_OVB))
                 goto error;
             if (in_arg >= argc)
                 goto error;
-            change->args[ch_arg].mode = (modes[0][ii] == 'o') ? MODE_CHANOP : MODE_VOICE;
+
+            if (modes[0][ii] == 'o')
+                change->args[ch_arg].mode = MODE_CHANOP;
+            else if (modes[0][ii] == 'h')
+                change->args[ch_arg].mode = MODE_HALFOP;
+            else if (modes[0][ii] == 'v')
+                change->args[ch_arg].mode = MODE_VOICE;
+
             if (!add)
                 change->args[ch_arg].mode |= MODE_REMOVE;
             if (flags & MCP_FROM_SERVER)
             if (!add)
                 change->args[ch_arg].mode |= MODE_REMOVE;
             if (flags & MCP_FROM_SERVER)
@@ -2257,6 +2377,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(STRIPCOLOR, 'S');
+        DO_MODE_CHAR(MODUNREG, 'M');
+        DO_MODE_CHAR(NONOTICE, 'N');
+        DO_MODE_CHAR(NOQUITMSGS, 'Q');
+        DO_MODE_CHAR(NOAMSG, 'T');
+        DO_MODE_CHAR(OPERSONLY, 'O');
+        // uncomment this for ssl support
+        //DO_MODE_CHAR(SSLONLY, 'z');
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', channel->key);
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', channel->key);
@@ -2270,9 +2398,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         case MODE_BAN:
             mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
             break;
         case MODE_BAN:
             mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
             break;
+        case MODE_EXEMPT:
+            mod_chanmode_append(&chbuf, 'e', change->args[arg].hostmask);
+            break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
                 mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
         default:
             if (change->args[arg].mode & MODE_CHANOP)
                 mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+            if (change->args[arg].mode & MODE_HALFOP)
+                mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
                 mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
             break;
             if (change->args[arg].mode & MODE_VOICE)
                 mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
             break;
@@ -2295,6 +2428,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(STRIPCOLOR, 'S');
+        DO_MODE_CHAR(MODUNREG, 'M');
+        DO_MODE_CHAR(NONOTICE, 'N');
+        DO_MODE_CHAR(NOQUITMSGS, 'Q');
+        DO_MODE_CHAR(NOAMSG, 'T');
+        DO_MODE_CHAR(OPERSONLY, 'O');
+        // uncomment this for ssl support
+        //DO_MODE_CHAR(SSLONLY, 'z');
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', change->new_key);
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', change->new_key);
@@ -2312,9 +2453,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         case MODE_BAN:
             mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
             break;
         case MODE_BAN:
             mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
             break;
+        case MODE_EXEMPT:
+            mod_chanmode_append(&chbuf, 'e', change->args[arg].hostmask);
+            break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
                 mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
         default:
             if (change->args[arg].mode & MODE_CHANOP)
                 mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+            if (change->args[arg].mode & MODE_HALFOP)
+                mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
                 mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
             break;
             if (change->args[arg].mode & MODE_VOICE)
                 mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
             break;
@@ -2350,6 +2496,14 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(STRIPCOLOR, 'S');
+        DO_MODE_CHAR(MODUNREG, 'M');
+        DO_MODE_CHAR(NONOTICE, 'N');
+        DO_MODE_CHAR(NOQUITMSGS, 'Q');
+        DO_MODE_CHAR(NOAMSG, 'T');
+        DO_MODE_CHAR(OPERSONLY, 'O');
+        // uncomment this for ssl support
+        //DO_MODE_CHAR(SSLONLY, 'z');
 #undef DO_MODE_CHAR
     }
     if (change->modes_set) {
 #undef DO_MODE_CHAR
     }
     if (change->modes_set) {
@@ -2365,6 +2519,14 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(STRIPCOLOR, 'S');
+        DO_MODE_CHAR(MODUNREG, 'M');
+        DO_MODE_CHAR(NONOTICE, 'N');
+        DO_MODE_CHAR(NOQUITMSGS, 'Q');
+        DO_MODE_CHAR(NOAMSG, 'T');
+        DO_MODE_CHAR(OPERSONLY, 'O');
+        // uncomment this for ssl support
+        //DO_MODE_CHAR(SSLONLY, 'z');
 #undef DO_MODE_CHAR
         switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
         case MODE_KEY|MODE_LIMIT:
 #undef DO_MODE_CHAR
         switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
         case MODE_KEY|MODE_LIMIT:
@@ -2390,6 +2552,7 @@ clear_chanmode(struct chanNode *channel, const char *modes)
     for (remove = 0; *modes; modes++) {
         switch (*modes) {
         case 'o': remove |= MODE_CHANOP; break;
     for (remove = 0; *modes; modes++) {
         switch (*modes) {
         case 'o': remove |= MODE_CHANOP; break;
+        case 'h': remove |= MODE_HALFOP; break;
         case 'v': remove |= MODE_VOICE; break;
         case 'p': remove |= MODE_PRIVATE; break;
         case 's': remove |= MODE_SECRET; break;
         case 'v': remove |= MODE_VOICE; break;
         case 'p': remove |= MODE_PRIVATE; break;
         case 's': remove |= MODE_SECRET; break;
@@ -2406,10 +2569,17 @@ clear_chanmode(struct chanNode *channel, const char *modes)
             channel->limit = 0;
             break;
         case 'b': remove |= MODE_BAN; break;
             channel->limit = 0;
             break;
         case 'b': remove |= MODE_BAN; break;
+        case 'e': remove |= MODE_EXEMPT; break;
         case 'D': remove |= MODE_DELAYJOINS; break;
         case 'r': remove |= MODE_REGONLY; break;
         case 'c': remove |= MODE_NOCOLORS;
         case 'C': remove |= MODE_NOCTCPS; break;
         case 'D': remove |= MODE_DELAYJOINS; break;
         case 'r': remove |= MODE_REGONLY; break;
         case 'c': remove |= MODE_NOCOLORS;
         case 'C': remove |= MODE_NOCTCPS; break;
+        case 'S': remove |= MODE_STRIPCOLOR; break;
+        case 'M': remove |= MODE_MODUNREG; break;
+        case 'N': remove |= MODE_NONOTICE; break;
+        case 'Q': remove |= MODE_NOQUITMSGS; break;
+        case 'T': remove |= MODE_NOAMSG; break;
+        case 'O': remove |= MODE_OPERSONLY; break;
         }
     }
 
         }
     }
 
@@ -2427,9 +2597,17 @@ clear_chanmode(struct chanNode *channel, const char *modes)
         channel->banlist.used = 0;
     }
 
         channel->banlist.used = 0;
     }
 
+    /* If removing exempts, kill 'em all. */
+    if ((remove & MODE_EXEMPT) && channel->exemptlist.used) {
+        unsigned int i;
+        for (i=0; i<channel->exemptlist.used; i++)
+            free(channel->exemptlist.list[i]);
+        channel->exemptlist.used = 0;
+    }
+
     /* Remove member modes. */
     /* Remove member modes. */
-    if ((remove & (MODE_CHANOP | MODE_VOICE)) && channel->members.used) {
-        int mask = ~(remove & (MODE_CHANOP | MODE_VOICE));
+    if ((remove & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) && channel->members.used) {
+        int mask = ~(remove & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));
         unsigned int i;
 
         for (i = 0; i < channel->members.used; i++)
         unsigned int i;
 
         for (i = 0; i < channel->members.used; i++)