chanserv.c     |   10 ++--
 common.h       |    1
 hash.c         |   42 +++++++++++++++++++-
 hash.h         |   13 +++++-
 modules-list.h |    2
 opserv.c       |   24 ++++++++---
 proto-common.c |   31 ++++++++++++++
 proto-p10.c    |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 tools.c        |   23 +++++++++++
 9 files changed, 242 insertions(+), 22 deletions(-)

Index: src/chanserv.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/chanserv.c,v
retrieving revision 1.6
diff -u -r1.6 chanserv.c
--- src/chanserv.c	28 Jun 2004 22:45:20 -0000	1.6
+++ src/chanserv.c	11 Aug 2004 17:01:52 -0000
@@ -1740,7 +1740,7 @@
     }
 
     if(new_channel)
-        channel = AddChannel(argv[1], now, NULL, NULL);
+        channel = AddChannel(argv[1], now, NULL, NULL, NULL);
 
     cData = register_channel(channel, user->handle_info->handle);
     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
@@ -1878,7 +1878,7 @@
 
     if(!(target = GetChannel(argv[1])))
     {
-        target = AddChannel(argv[1], now, NULL, NULL);
+        target = AddChannel(argv[1], now, NULL, NULL, NULL);
         if(!IsSuspended(channel->channel_info))
             AddChannelUser(chanserv, target);
     }
@@ -6337,7 +6337,7 @@
             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
             if(!str2)
                 str2 = "+nt";
-            chan = AddChannel(strlist->list[ii], now, str2, NULL);
+            chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
             LockChannel(chan);
             channelList_append(&chanserv_conf.support_channels, chan);
         }
@@ -6348,7 +6348,7 @@
         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
         if(!str2)
             str2 = "+nt";
-        chan = AddChannel(str, now, str2, NULL);
+        chan = AddChannel(str, now, str2, NULL, NULL);
         LockChannel(chan);
         channelList_append(&chanserv_conf.support_channels, chan);
     }
@@ -6623,7 +6623,7 @@
     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
     if(!str)
         str = "<unknown>";
-    cNode = AddChannel(key, now, NULL, NULL);
+    cNode = AddChannel(key, now, NULL, NULL, NULL);
     if(!cNode)
     {
         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
Index: src/common.h
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/common.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 common.h
--- src/common.h	3 Jun 2004 17:13:59 -0000	1.1.1.1
+++ src/common.h	11 Aug 2004 17:01:52 -0000
@@ -96,6 +96,7 @@
 void reg_exit_func(exit_func_t handler);
 void call_exit_funcs(void);
 
+char *mysep(char **sepstr, char *delim);
 const char *inttobase64(char *buf, unsigned int v, unsigned int count);
 unsigned long base64toint(const char *s, int count);
 int split_line(char *line, int irc_colon, int argv_size, char *argv[]);
Index: src/hash.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/hash.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 hash.c
--- src/hash.c	3 Jun 2004 17:13:59 -0000	1.1.1.1
+++ src/hash.c	11 Aug 2004 17:01:53 -0000
@@ -309,6 +309,11 @@
         free(cNode->banlist.list[nn]);
     cNode->banlist.used = 0;
 
+    /* remove our old exe,[t list, replace it with the new one */
+    for (nn=0; nn<cNode->exemptlist.used; nn++)
+        free(cNode->exemptlist.list[nn]);
+    cNode->exemptlist.used = 0;
+
     /* deop anybody in the channel now, but count services to reop */
     for (nn=argc=0; nn<cNode->members.used; nn++) {
         struct modeNode *mn = cNode->members.list[nn];
@@ -339,7 +344,7 @@
 }
 
 struct chanNode *
-AddChannel(const char *name, time_t time_, const char *modes, char *banlist)
+AddChannel(const char *name, time_t time_, const char *modes, char *banlist, char *exemptlist)
 {
     struct chanNode *cNode;
     char new_modes[MAXLEN], *argv[MAXNUMPARAMS];
@@ -358,6 +363,7 @@
         cNode = calloc(1, sizeof(*cNode) + strlen(name));
         strcpy(cNode->name, name);
         banList_init(&cNode->banlist);
+        exemptList_init(&cNode->exemptlist);
         modeList_init(&cNode->members);
         mod_chanmode(NULL, cNode, argv, nn, 0);
         dict_insert(channels, cNode->name, cNode);
@@ -399,6 +405,23 @@
         }
     }
 
+    /* go through list of exempts and add each one */
+    if (exemptlist && (rel_age >= 0)) {
+        for (nn=0; exemptlist[nn];) {
+            char *exempt = exemptlist + nn;
+            struct exemptNode *en;
+            while (exemptlist[nn] != ' ' && exemptlist[nn])
+                nn++;
+            while (exemptlist[nn] == ' ')
+                exemptlist[nn++] = 0;
+            en = calloc(1, sizeof(*en));
+            safestrncpy(en->exempt, exempt, sizeof(en->exempt));
+            safestrncpy(en->who, "<unknown>", sizeof(en->who));
+            en->set = now;
+            exemptList_append(&cNode->exemptlist, en);
+        }
+    }
+
     return cNode;
 }
 
@@ -440,11 +463,17 @@
         free(channel->banlist.list[--n]);
     channel->banlist.used = 0;
 
+    /* delete all channel exempts */
+    for (n=channel->exemptlist.used; n>0; )
+        free(channel->exemptlist.list[--n]);
+    channel->exemptlist.used = 0;
+
     for (n=0; n<dcf_used; n++)
         dcf_list[n](channel);
 
     modeList_clean(&channel->members);
     banList_clean(&channel->banlist);
+    exemptList_clean(&channel->exemptlist);
     free(channel);
 }
 
@@ -632,6 +661,16 @@
     return 0;
 }
 
+int ChannelExemptExists(struct chanNode *channel, const char *exempt)
+{
+    unsigned int n;
+
+    for (n = 0; n < channel->exemptlist.used; n++)
+        if (match_ircglobs(channel->exemptlist.list[n]->exempt, exempt))
+            return 1;
+    return 0;
+}
+
 static topic_func_t *tf_list;
 static unsigned int tf_size = 0, tf_used = 0;
 
@@ -712,6 +751,7 @@
 DEFINE_LIST(userList, struct userNode*)
 DEFINE_LIST(modeList, struct modeNode*)
 DEFINE_LIST(banList, struct banNode*)
+DEFINE_LIST(exemptList, struct exemptNode*)
 DEFINE_LIST(channelList, struct chanNode*)
 DEFINE_LIST(serverList, struct server*)
 
Index: src/hash.h
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/hash.h,v
retrieving revision 1.3
diff -u -r1.3 hash.h
--- src/hash.h	14 Jun 2004 07:32:05 -0000	1.3
+++ src/hash.h	11 Aug 2004 17:01:53 -0000
@@ -48,6 +48,7 @@
 #define MODE_NOQUITMSGS         0x00100000 /* +Q suppress messages from quit notices */
 #define MODE_NOAMSG             0x00200000 /* +T no multi-target messages */
 #define MODE_SSLONLY            0x00400000 /* +z ssl only */
+#define MODE_EXEMPT		0x00800000 /* +e EXEMPT */
 #define MODE_REMOVE             0x80000000
 
 #define FLAGS_OPER		0x0001 /* Operator +O */
@@ -102,6 +103,7 @@
 
 #define MAXMODEPARAMS	6
 #define MAXBANS		45
+#define MAXEXEMPTS	45
 
 /* IDLEN is 6 because it takes 5.33 Base64 digits to store 32 bytes. */
 #define IDLEN           6
@@ -109,6 +111,7 @@
 DECLARE_LIST(userList, struct userNode*);
 DECLARE_LIST(modeList, struct modeNode*);
 DECLARE_LIST(banList, struct banNode*);
+DECLARE_LIST(exemptList, struct exemptNode*);
 DECLARE_LIST(channelList, struct chanNode*);
 DECLARE_LIST(serverList, struct server*);
 
@@ -151,6 +154,7 @@
 
     struct modeList members;
     struct banList banlist;
+    struct exemptList exemptlist;
     struct policer join_policer;
     unsigned int join_flooded : 1;
     unsigned int bad_channel : 1;
@@ -166,6 +170,12 @@
     time_t set; /* time ban was set */
 };
 
+struct exemptNode {
+    char exempt[NICKLEN + USERLEN + HOSTLEN + 3]; /* 1 for '\0', 1 for ! and 1 for @ = 3 */
+    char who[NICKLEN + 1]; /* who set exempt */
+    time_t set; /* time exempt was set */
+};
+
 struct modeNode {
     struct chanNode *channel;
     struct userNode *user;
@@ -235,7 +245,7 @@
 typedef void (*del_channel_func_t) (struct chanNode *chan);
 void reg_del_channel_func(del_channel_func_t handler);
 
-struct chanNode* AddChannel(const char *name, time_t time_, const char *modes, char *banlist);
+struct chanNode* AddChannel(const char *name, time_t time_, const char *modes, char *banlist, char *exemptlist);
 void LockChannel(struct chanNode *channel);
 void UnlockChannel(struct chanNode *channel);
 
@@ -252,6 +262,7 @@
 void ChannelUserKicked(struct userNode* kicker, struct userNode* victim, struct chanNode* channel);
 
 int ChannelBanExists(struct chanNode *channel, const char *ban);
+int ChannelExemptExists(struct chanNode *channel, const char *exempt);
 
 typedef int (*topic_func_t)(struct userNode *who, struct chanNode *chan, const char *old_topic);
 void reg_topic_func(topic_func_t handler);
Index: src/modules-list.h
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/modules-list.h,v
retrieving revision 1.4
diff -u -r1.4 modules-list.h
--- src/modules-list.h	23 Jul 2004 18:56:59 -0000	1.4
+++ src/modules-list.h	11 Aug 2004 17:01:53 -0000
@@ -1,3 +1 @@
 
-WITH_MODULE(snoop)
-WITH_MODULE(memoserv)
Index: src/opserv.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/opserv.c,v
retrieving revision 1.2
diff -u -r1.2 opserv.c
--- src/opserv.c	10 Jun 2004 20:14:33 -0000	1.2
+++ src/opserv.c	11 Aug 2004 17:01:56 -0000
@@ -244,6 +244,8 @@
     { "OSMSG_CHANINFO_TOPIC_UNKNOWN", "Topic: (none / not gathered)" },
     { "OSMSG_CHANINFO_BAN_COUNT", "Bans (%d):" },
     { "OSMSG_CHANINFO_BAN", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
+    { "OSMSG_CHANINFO_EXEMPT_COUNT", "Exempts (%d):" },
+    { "OSMSG_CHANINFO_EXEMPT", "%%s by %%s (%a %b %d %H:%M:%S %Y)" },
     { "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" },
     { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" },
     { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" },
@@ -420,6 +422,7 @@
     char buffer[MAXLEN];
     const char *fmt;
     struct banNode *ban;
+    struct exemptNode *exempt;
     struct modeNode *moden;
     unsigned int n;
 
@@ -449,6 +452,15 @@
 	    send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who);
 	}
     }
+    if (channel->exemptlist.used) {
+        reply("OSMSG_CHANINFO_EXEMPT_COUNT", channel->exemptlist.used);
+        fmt = user_find_message(user, "OSMSG_CHANINFO_EXEMPT");
+        for (n = 0; n < channel->exemptlist.used; n++) {
+            exempt = channel->exemptlist.list[n];
+            strftime(buffer, sizeof(buffer), fmt, localtime(&exempt->set));
+            send_message_type(4, user, cmd->parent->bot, buffer, exempt->exempt, exempt->who);
+        }
+    }
     if ((argc < 2) && (channel->members.used >= 50)) {
         /* early out unless they ask for users */
         reply("OSMSG_CHANINFO_MANY_USERS", channel->members.used, argv[0], channel->name);
@@ -913,7 +925,7 @@
         reply("MSG_NOT_CHANNEL_NAME");
         return 0;
     } else if (!(channel = GetChannel(argv[1]))) {
-        channel = AddChannel(argv[1], now, NULL, NULL);
+        channel = AddChannel(argv[1], now, NULL, NULL, NULL);
         AddChannelUser(bot, channel)->modes |= MODE_CHANOP;
     } else if (GetUserMode(channel, bot)) {
         reply("OSMSG_ALREADY_JOINED", channel->name);
@@ -2259,7 +2271,7 @@
     channel = GetChannel(argv[3]);
     if (!irccasecmp(argv[1], "JOIN")) {
 	if (!channel
-	    && !(channel = AddChannel(argv[3], now, NULL, NULL))) {
+	    && !(channel = AddChannel(argv[3], now, NULL, NULL, NULL))) {
 	    reply("MSG_CHANNEL_UNKNOWN", argv[3]);
 	    return 0;
 	}
@@ -2981,7 +2993,7 @@
                     send_message(user, opserv, "MSG_CHANNEL_UNKNOWN", argv[i]);
                     goto fail;
                 } else {
-                    discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL);
+                    discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL, NULL);
                 }
 	    }
             LockChannel(discrim->channel);
@@ -3907,7 +3919,7 @@
         str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING);
         if (!str2)
             str2 = "+tinms";
-	opserv_conf.debug_channel = AddChannel(str, now, str2, NULL);
+	opserv_conf.debug_channel = AddChannel(str, now, str2, NULL, NULL);
         AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP;
     } else {
 	opserv_conf.debug_channel = NULL;
@@ -3917,7 +3929,7 @@
         str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING);
         if (!str2)
             str2 = "+tns";
-	opserv_conf.alert_channel = AddChannel(str, now, str2, NULL);
+	opserv_conf.alert_channel = AddChannel(str, now, str2, NULL, NULL);
         AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP;
     } else {
 	opserv_conf.alert_channel = NULL;
@@ -3927,7 +3939,7 @@
         str2 = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL_MODES, RECDB_QSTRING);
         if (!str2)
             str2 = "+timns";
-        opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL);
+        opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL, NULL);
         AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP;
     } else {
         opserv_conf.staff_auth_channel = NULL;
Index: src/proto-common.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/proto-common.c,v
retrieving revision 1.2
diff -u -r1.2 proto-common.c
--- src/proto-common.c	10 Jun 2004 20:14:33 -0000	1.2
+++ src/proto-common.c	11 Aug 2004 17:01:57 -0000
@@ -552,6 +552,7 @@
 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
 {
     struct banNode *bn;
+    struct exemptNode *en;
     unsigned int ii, jj;
 
     assert(change->argc <= change->alloc_argc);
@@ -592,6 +593,36 @@
                 break;
             }
             break;
+        case MODE_EXEMPT:
+            /* If any existing exempt is a subset of the new exempt,
+             * silently remove it.  The new exempt is not allowed
+             * to be more specific than an existing exempt.
+             */
+            for (jj=0; jj<channel->exemptlist.used; ++jj) {
+                if (match_ircglobs(change->args[ii].hostmask, channel->exemptlist.list[jj]->exempt)) {
+                    exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
+                    free(channel->exemptlist.list[jj]);
+                    jj--;
+                }
+            }
+            en = calloc(1, sizeof(*en));
+            safestrncpy(en->exempt, change->args[ii].hostmask, sizeof(en->exempt));
+            if (who)
+                safestrncpy(en->who, who->nick, sizeof(en->who));
+            else
+                safestrncpy(en->who, "<unknown>", sizeof(en->who));
+            en->set = now;
+            exemptList_append(&channel->exemptlist, en);
+            break;
+        case MODE_REMOVE|MODE_EXEMPT:
+            for (jj=0; jj<channel->exemptlist.used; ++jj) {
+                if (strcmp(channel->exemptlist.list[jj]->exempt, change->args[ii].hostmask))
+                    continue;
+                free(channel->exemptlist.list[jj]);
+                exemptList_remove(&channel->exemptlist, channel->exemptlist.list[jj]);
+                break;
+            }
+            break;
         case MODE_CHANOP:
         case MODE_VOICE:
         case MODE_VOICE|MODE_CHANOP:
Index: src/proto-p10.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/proto-p10.c,v
retrieving revision 1.3
diff -u -r1.3 proto-p10.c
--- src/proto-p10.c	14 Jun 2004 07:32:06 -0000	1.3
+++ src/proto-p10.c	11 Aug 2004 17:01:59 -0000
@@ -574,6 +574,7 @@
     int pos, base_len, len;
     struct modeNode *mn;
     struct banNode *bn;
+    struct exemptNode *en;
     long last_mode=-1;
     unsigned int n;
 
@@ -632,6 +633,33 @@
             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);
@@ -937,7 +965,7 @@
         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)
@@ -1034,18 +1062,26 @@
 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;
+    char* parm = NULL;
 
     if (argc < 3)
         return 0;
     modes[0] = 0;
+
+    exemptlist[0] = 0;
+    banlist[0] = 0;
+
     while (next < argc) {
         switch (argv[next][0]) {
         case '+': {
@@ -1058,9 +1094,52 @@
             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]);
@@ -1069,9 +1148,9 @@
         dict_remove(unbursted_channels, cNode->name);
         irc_burst(cNode);
     }
-    cNode = AddChannel(argv[1], in_timestamp, modes, banlist);
 
-    /* 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;
@@ -2159,6 +2238,16 @@
                 change->args[ch_arg].mode |= MODE_REMOVE;
             change->args[ch_arg++].hostmask = modes[in_arg++];
             break;
+        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 'v':
         {
             struct userNode *victim;
@@ -2286,6 +2375,9 @@
         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);
@@ -2336,6 +2428,9 @@
         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);
@@ -2446,6 +2541,7 @@
             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;
@@ -2473,6 +2569,14 @@
         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. */
     if ((remove & (MODE_CHANOP | MODE_VOICE)) && channel->members.used) {
         int mask = ~(remove & (MODE_CHANOP | MODE_VOICE));
Index: src/tools.c
===================================================================
RCS file: /cvsroot/x2serv/srvx2/src/tools.c,v
retrieving revision 1.2
diff -u -r1.2 tools.c
--- src/tools.c	10 Jun 2004 20:14:33 -0000	1.2
+++ src/tools.c	11 Aug 2004 17:02:00 -0000
@@ -854,3 +854,26 @@
         free(str_tab.list[ii]);
     free(str_tab.list);
 }
+
+/* mysep() is my answer to the strtok/strsep
+ * issue. strsep is nice but doesn't skip
+ * multiple dilimiters, which can really
+ * offset tokens and cause huge corruption
+ * so this function will use strsep but
+ * act like strtok in that sence.
+ */
+char *mysep(char **sepstr, char *delim)
+{
+  static char *retstr;
+
+  if(!*sepstr || !**sepstr)
+    return(NULL);
+
+  do
+  {
+      retstr = strsep(sepstr, delim);
+  }while (retstr && !(*retstr));
+
+  return(retstr);
+}
+


syntax highlighted by Code2HTML, v. 0.9.1