]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
This should fix the nickserv module complaining about not enough parameters when...
[irc/evilnet/x3.git] / src / proto-p10.c
index 7e2246d0da238ba648d142b433568da79a1ae007..580cbc5e1f1a526960791ece1675926d8fe7706a 100644 (file)
@@ -1,7 +1,7 @@
 /* proto-p10.c - IRC protocol output
  * Copyright 2000-2004 srvx Development Team
  *
- * This file is part of srvx.
+ * This file is part of x3.
  *
  * srvx is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,6 +18,8 @@
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  */
 
+#include "nickserv.h"
+#include "chanserv.h"
 #include "proto-common.c"
 
 /* Full commands. */
@@ -55,6 +57,7 @@
 #define CMD_LIST                "LIST"
 #define CMD_LUSERS              "LUSERS"
 #define CMD_MAP                 "MAP"
+#define CMD_MARK                "MARK"
 #define CMD_MODE                "MODE"
 #define CMD_MOTD                "MOTD"
 #define CMD_NAMES               "NAMES"
@@ -81,6 +84,7 @@
 #define CMD_SERVSET             "SERVSET"
 #define CMD_SET                        "SET"
 #define CMD_SETTIME             "SETTIME"
+#define CMD_SHUN               "SHUN"
 #define CMD_SILENCE             "SILENCE"
 #define CMD_SQUERY              "SQUERY"
 #define CMD_SQUIT               "SQUIT"
 #define TOK_EOB                 "EB"
 #define TOK_EOB_ACK             "EA"
 #define TOK_ERROR               "Y"
+#define TOK_EXEMPT             "EX"
 #define TOK_FAKEHOST            "FA"
 #define TOK_GET                        "GET"
 #define TOK_GLINE               "GL"
 #define TOK_LIST                "LIST"
 #define TOK_LUSERS              "LU"
 #define TOK_MAP                 "MAP"
+#define TOK_MARK                "MK"
 #define TOK_MODE                "M"
 #define TOK_MOTD                "MO"
 #define TOK_NAMES               "E"
 #define TOK_SERVSET             "SERVSET"
 #define TOK_SET                        "SET"
 #define TOK_SETTIME             "SE"
+#define TOK_SHUN               "SU"
 #define TOK_SILENCE             "U"
 #define TOK_SQUERY              "SQUERY"
 #define TOK_SQUIT               "SQ"
 #define P10_LIST                TYPE(LIST)
 #define P10_LUSERS              TYPE(LUSERS)
 #define P10_MAP                 TYPE(MAP)
+#define P10_MARK                TYPE(MARK)
 #define P10_MODE                TYPE(MODE)
 #define P10_MOTD                TYPE(MOTD)
 #define P10_NAMES               TYPE(NAMES)
 #define P10_SERVSET             TYPE(SERVSET)
 #define P10_SET                        TYPE(SET)
 #define P10_SETTIME             TYPE(SETTIME)
+#define P10_SHUN               TYPE(SHUN)
 #define P10_SILENCE             TYPE(SILENCE)
 #define P10_SQUERY              TYPE(SQUERY)
 #define P10_SQUIT               TYPE(SQUIT)
 #define P10_WHO                 TYPE(WHO)
 #define P10_WHOIS               TYPE(WHOIS)
 #define P10_WHOWAS              TYPE(WHOWAS)
+#define P10_EXEMPT             TYPE(EXEMPT)
 
 /* Servers claiming to have a boot or link time before PREHISTORY
  * trigger errors to the log.  We hope no server has been running
@@ -296,6 +306,7 @@ static unsigned int num_notice_funcs;
 static struct dict *unbursted_channels;
 static char *his_servername;
 static char *his_servercomment;
+static int extended_accounts;
 
 static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip);
 
@@ -425,9 +436,26 @@ irc_user(struct userNode *user)
 }
 
 void
-irc_account(struct userNode *user, const char *stamp)
+irc_rename(struct userNode *user, const char *new_handle)
 {
-    putsock("%s " P10_ACCOUNT " %s %s", self->numeric, user->numeric, stamp);
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s M %s", self->numeric, user->numeric, new_handle);
+}
+
+void
+irc_delete(struct userNode *user)
+{
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s U", self->numeric, user->numeric);
+}
+
+void
+irc_account(struct userNode *user, const char *stamp, time_t timestamp)
+{
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s R %s %lu", self->numeric, user->numeric, stamp, timestamp); 
+    else
+        putsock("%s " P10_ACCOUNT " %s %s %lu", self->numeric, user->numeric, stamp, timestamp);
 }
 
 void
@@ -551,10 +579,17 @@ irc_introduce(const char *passwd)
 }
 
 void
-irc_gline(struct server *srv, struct gline *gline)
+irc_gline(struct server *srv, struct gline *gline, int silent)
 {
-    putsock("%s " P10_GLINE " %s +%s %ld :<%s> %s",
-            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->issuer, gline->reason);
+    putsock("%s " P10_GLINE " %s +%s %ld :%s<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, silent ? "AUTO " : "", gline->issuer, gline->reason);
+}
+
+void
+irc_shun(struct server *srv, struct shun *shun)
+{
+    putsock("%s " P10_SHUN " %s +%s %ld :<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->issuer, shun->reason);
 }
 
 void
@@ -572,6 +607,12 @@ irc_ungline(const char *mask)
     putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
 }
 
+void
+irc_unshun(const char *mask)
+{
+    putsock("%s " P10_SHUN " * -%s", self->numeric, mask);
+}
+
 static void
 irc_burst(struct chanNode *chan)
 {
@@ -579,6 +620,7 @@ irc_burst(struct chanNode *chan)
     int pos, base_len, len;
     struct modeNode *mn;
     struct banNode *bn;
+    struct exemptNode *en;
     long last_mode=-1;
     unsigned int n;
 
@@ -639,6 +681,33 @@ irc_burst(struct chanNode *chan)
             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);
@@ -680,6 +749,16 @@ irc_mode(struct userNode *from, struct chanNode *target, const char *modes)
             target->name, modes, target->timestamp);
 }
 
+/* Added to allow services to mode users
+   2005 - 8 - 10 by Life4Christ
+*/
+void
+irc_umode(struct userNode *target, const char *modes)
+{
+    putsock("%s " P10_MODE " %s %s ",self->numeric,target->nick, modes);
+}
+
+
 void
 irc_invite(struct userNode *from, struct userNode *who, struct chanNode *to)
 {
@@ -730,8 +809,14 @@ irc_part(struct userNode *who, struct chanNode *what, const char *reason)
 }
 
 void
-irc_topic(struct userNode *who, struct chanNode *what, const char *topic)
+irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
 {
+/* UNCOMMENT FOR NEFARIOUS 0.5.0 TOPIC SUPPORT
+ *    putsock("%s " P10_TOPIC " %s %s " FMT_TIME_T " " FMT_TIME_T " :%s", service->numeric, what->name, who->nick, what->timestamp, now, topic);
+ * UNCOMMENT FOR NEFARIOUS 0.5.0 TOPIC SUPPORT */
+
+    who = service; /* REMOVE LINE FOR NEFARIOUS 0.5.0 */
+
     putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
 }
 
@@ -793,13 +878,11 @@ static CMD_FUNC(cmd_whois)
     }
     irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
     if (his_servername && his_servercomment)
-      irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
+        irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
     else
-    irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
-
-    if (IsOper(who)) {
+        irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
+    if (IsOper(who))
         irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
-    }
     irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
     return 1;
 }
@@ -811,7 +894,7 @@ static CMD_FUNC(cmd_server)
 
     if (argc < 8)
         return 0;
-    if (origin) {
+    if (self->uplink) {
         /* another server introduced us */
         srv = AddServer(GetServerH(origin), argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[6], argv[argc-1]);
         if (!srv)
@@ -944,7 +1027,7 @@ create_helper(char *name, void *data)
         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)
@@ -1016,13 +1099,39 @@ static CMD_FUNC(cmd_nick)
 static CMD_FUNC(cmd_account)
 {
     struct userNode *user;
+    struct server *server;
+    struct handle_info *hi;
 
-    if ((argc < 3) || !origin || !GetServerH(origin))
+    if ((argc < 3) || !origin || !(server = GetServerH(origin)))
         return 0; /* Origin must be server. */
+    
+    /* This next line appears to tremple origin.. why? */
     user = GetUserN(argv[1]);
     if (!user)
         return 1; /* A QUIT probably passed the ACCOUNT. */
-    call_account_func(user, argv[2]);
+
+    if(!extended_accounts) /* any need for this function without? */
+        return 1;
+    
+    if(!strcmp(argv[2],"C"))
+    {
+        if((hi = loc_auth(argv[4], argv[5])))
+        {
+            /* Return a AC A */
+            putsock("%s " P10_ACCOUNT " %s A %s %lu", self->numeric, server->numeric , argv[3], hi->registered);
+
+        }
+        else
+        {
+            /* Return a AC D */
+            putsock("%s " P10_ACCOUNT " %s D %s", self->numeric, server->numeric , argv[3]);
+        }
+        return 1;
+    }
+    else if(!strcmp(argv[2],"R"))
+       call_account_func(user, argv[3]);
+    else
+        call_account_func(user, argv[2]); /* For backward compatability */
     return 1;
 }
 
@@ -1041,44 +1150,91 @@ static CMD_FUNC(cmd_fakehost)
 static CMD_FUNC(cmd_burst)
 {
     extern int rel_age;
-    char modes[MAXLEN], *members = "", *banlist = NULL;
+    char modes[MAXLEN], *members = "";
+    static char exemptlist[MAXLEN], banlist[MAXLEN];
     unsigned int next = 3, 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 '+': {
             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++;
-            }
             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 */
+                  if (strlen(parm) > 1) {
+                    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);
     }
-    cNode = AddChannel(argv[1], in_timestamp, modes, banlist);
+    cNode = AddChannel(argv[1], in_timestamp, modes, banlist, exemptlist);
 
     /* Burst channel members in now. */
     for (user = members, sep = *members, mode = 0; sep; user = end) {
@@ -1093,7 +1249,10 @@ static CMD_FUNC(cmd_burst)
                     mode |= MODE_HALFOP;
                 else if (sep == 'v')
                     mode |= MODE_VOICE;
-                else
+                else if (isdigit(sep)) {
+                    mode |= MODE_CHANOP;
+                    while (isdigit(*end)) end++;
+                } else
                     break;
             }
             if (rel_age < 0)
@@ -1212,6 +1371,7 @@ static CMD_FUNC(cmd_topic)
 {
     struct chanNode *cn;
     time_t chan_ts, topic_ts;
+    struct userNode *user;
 
     if (argc < 3)
         return 0;
@@ -1219,15 +1379,23 @@ static CMD_FUNC(cmd_topic)
         log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose topic is being set", argv[1]);
         return 0;
     }
-    if (argc >= 5) {
-        /* Looks like an Asuka style topic burst. */
+
+
+    if (argc == 5) {              /* Asuka / Topic Bursting IRCu's */
+        user = GetUserH(origin);
         chan_ts = atoi(argv[2]);
         topic_ts = atoi(argv[3]);
-    } else {
+    } else if (argc >= 6) {       /* Nefarious 0.5.0 */
+        user = GetUserH(strtok(argv[2], "!"));
+        chan_ts = atoi(argv[3]);
+        topic_ts = atoi(argv[4]);
+    } else {                      /* Regular IRCu (No Topic Bursting)*/
+        user = GetUserH(origin);
         chan_ts = cn->timestamp;
         topic_ts = now;
     }
-    SetChannelTopic(cn, GetUserH(origin), argv[argc-1], 0);
+
+    SetChannelTopic(cn, user, user, argv[argc-1], 0);
     cn->topic_time = topic_ts;
     return 1;
 }
@@ -1272,7 +1440,15 @@ static CMD_FUNC(cmd_num_gline)
 {
     if (argc < 6)
         return 0;
-    gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
+    gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
+    return 1;
+}
+
+static CMD_FUNC(cmd_num_shun)
+{
+    if (argc < 6)
+        return 0;
+    shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
     return 1;
 }
 
@@ -1405,7 +1581,7 @@ static CMD_FUNC(cmd_gline)
     if (argv[2][0] == '+') {
         if (argc < 5)
             return 0;
-        gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
+        gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0, 0);
         return 1;
     } else if (argv[2][0] == '-') {
         gline_remove(argv[2]+1, 0);
@@ -1414,6 +1590,22 @@ static CMD_FUNC(cmd_gline)
         return 0;
 }
 
+static CMD_FUNC(cmd_shun)
+{
+    if (argc < 3)
+        return 0;
+    if (argv[2][0] == '+') {
+        if (argc < 5)
+            return 0;
+        shun_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
+        return 1;
+    } else if (argv[2][0] == '-') {
+        shun_remove(argv[2]+1, 0);
+        return 1;
+    } else
+        return 0;
+}
+
 static CMD_FUNC(cmd_svsnick)
 {
     struct userNode *target, *dest;
@@ -1469,6 +1661,8 @@ init_parse(void)
     char numer[COMBO_NUMERIC_LEN+1];
 
     /* read config items */
+    str = conf_get_data("server/extended_accounts", RECDB_QSTRING);
+    extended_accounts = str ? enabled_string(str) : 1;
     str = conf_get_data("server/ping_freq", RECDB_QSTRING);
     ping_freq = str ? ParseInterval(str) : 120;
     str = conf_get_data("server/ping_timeout", RECDB_QSTRING);
@@ -1561,6 +1755,8 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois);
     dict_insert(irc_func_dict, CMD_GLINE, cmd_gline);
     dict_insert(irc_func_dict, TOK_GLINE, cmd_gline);
+    dict_insert(irc_func_dict, CMD_SHUN, cmd_shun);
+    dict_insert(irc_func_dict, TOK_SHUN, cmd_shun);
     dict_insert(irc_func_dict, CMD_OPMODE, cmd_opmode);
     dict_insert(irc_func_dict, TOK_OPMODE, cmd_opmode);
     dict_insert(irc_func_dict, CMD_CLEARMODE, cmd_clearmode);
@@ -1594,6 +1790,14 @@ init_parse(void)
     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);
+    dict_insert(irc_func_dict, TOK_WALLUSERS, cmd_dummy);
+    /* Ignore dnsbl exemptions */
+    dict_insert(irc_func_dict, TOK_EXEMPT, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_MARK, cmd_dummy);
+    /* Ignore privs */
+    dict_insert(irc_func_dict, TOK_PRIVS, cmd_dummy);
+    /* Ignore remote luser */
+    dict_insert(irc_func_dict, TOK_LUSERS, 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);
@@ -1606,11 +1810,13 @@ init_parse(void)
     /* ban list resetting */
     /* "stats g" responses */
     dict_insert(irc_func_dict, "247", cmd_num_gline);
+    dict_insert(irc_func_dict, "542", cmd_num_shun);
     dict_insert(irc_func_dict, "219", cmd_dummy); /* "End of /STATS report" */
     /* other numeric responses we might get */
     dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
     dict_insert(irc_func_dict, "403", cmd_dummy); /* no such channel */
     dict_insert(irc_func_dict, "404", cmd_dummy); /* cannot send to channel */
+    dict_insert(irc_func_dict, "439", cmd_dummy); /* target change too fast */
     dict_insert(irc_func_dict, "441", cmd_dummy); /* target isn't on that channel */
     dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
     dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
@@ -1834,7 +2040,7 @@ void DelServer(struct server* serv, int announce, const char *message)
 }
 
 struct userNode *
-AddService(const char *nick, const char *desc, const char *hostname)
+AddService(const char *nick, const char *modes, const char *desc, const char *hostname)
 {
     char numeric[COMBO_NUMERIC_LEN+1];
     int local_num = get_local_numeric();
@@ -1853,7 +2059,8 @@ AddService(const char *nick, const char *desc, const char *hostname)
     if (!hostname)
         hostname = self->name;
     make_numeric(self, local_num, numeric);
-    return AddUser(self, nick, nick, hostname, "+oik", numeric, desc, now, "AAAAAA");
+    /* TODO: Make these modes part of the conf file */
+    return AddUser(self, nick, nick, hostname, modes ? modes : "+oik", numeric, desc, now, "AAAAAA");
 }
 
 struct userNode *
@@ -1879,12 +2086,13 @@ AddClone(const char *nick, const char *ident, const char *hostname, const char *
 
 int
 is_valid_nick(const char *nick) {
+    unsigned int ii;
     /* IRC has some of The Most Fucked-Up ideas about character sets
      * in the world.. */
     if (!isalpha(*nick) && !strchr("{|}~[\\]^_`", *nick))
         return 0;
-    for (++nick; *nick; ++nick)
-        if (!isalnum(*nick) && !strchr("{|}~[\\]^-_`", *nick))
+    for (ii = 0; nick[ii]; ++ii)
+        if (!isalnum(nick[ii]) && !strchr("{|}~[\\]^-_`", nick[ii]))
             return 0;
     if (strlen(nick) > nicklen)
         return 0;
@@ -1965,6 +2173,10 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     for (n=0; n<nuf_used; n++)
         if (nuf_list[n](uNode))
             break;
+
+    if ((uNode->loc == 1) && (uNode->handle_info))
+        send_func_list(uNode);
+
     return uNode;
 }
 
@@ -1974,9 +2186,14 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
 {
     unsigned int n;
 
+    verify(user);
+
     /* mark them as dead, in case anybody cares */
     user->dead = 1;
 
+    /* remove pending adduser commands */
+    wipe_adduser_pending(NULL, user);
+
     /* remove user from all channels */
     while (user->channels.used > 0)
         DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, false, 0);
@@ -2015,15 +2232,16 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
         free_user(user);
 }
 
+static void call_oper_funcs(struct userNode *user);
+
 void mod_usermode(struct userNode *user, const char *mode_change) {
-    static void call_oper_funcs(struct userNode *user);
     int add = 1;
     const char *word = mode_change;
 
     if (!user || !mode_change)
         return;
-    while (*word != ' ' && *word) word++;\
-    while (*word == ' ') word++; \
+    while (*word != ' ' && *word) word++;
+    while (*word == ' ') word++;
     while (1) {
 #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
        switch (*mode_change++) {
@@ -2031,13 +2249,15 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
        case '+': add = 1; break;
        case '-': add = 0; break;
        case 'o':
-           do_user_mode(FLAGS_OPER);
            if (add) {
-               userList_append(&curr_opers, user);
-               call_oper_funcs(user);
+                if(!IsOper(user)) { /* Dont re-oper an oper */
+                    userList_append(&curr_opers, user);
+                    call_oper_funcs(user);
+                }
            } else {
                userList_remove(&curr_opers, user);
            }
+           do_user_mode(FLAGS_OPER);
            break;
        case 'O': do_user_mode(FLAGS_LOCOP); break;
        case 'i': do_user_mode(FLAGS_INVISIBLE);
@@ -2135,8 +2355,16 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         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;
+        case 'Z': do_chan_mode(MODE_SSLONLY); break;
+       case 'L': do_chan_mode(MODE_HIDEMODE); break;
+       case 'z':
+         if (!(flags & MCP_REGISTERED)) {
+          do_chan_mode(MODE_REGISTERED);
+         } else {
+          mod_chanmode_free(change);
+          return NULL;
+         }
+         break;
 #undef do_chan_mode
         case 'l':
             if (add) {
@@ -2172,7 +2400,17 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             change->args[ch_arg].mode = MODE_BAN;
             if (!add)
                 change->args[ch_arg].mode |= MODE_REMOVE;
-            change->args[ch_arg++].hostmask = modes[in_arg++];
+            change->args[ch_arg++].u.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++].u.hostmask = modes[in_arg++];
             break;
         case 'o': case 'h': case 'v':
         {
@@ -2197,7 +2435,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 victim = GetUserH(modes[in_arg++]);
             if (!victim)
                 continue;
-            if ((change->args[ch_arg].member = GetUserMode(channel, victim)))
+            if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
                 ch_arg++;
             break;
         }
@@ -2293,8 +2531,9 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         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');
+        DO_MODE_CHAR(REGISTERED, 'z');
+        DO_MODE_CHAR(SSLONLY, 'Z');
+       DO_MODE_CHAR(HIDEMODE, 'L');
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', channel->key);
@@ -2306,15 +2545,18 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             chbuf.modes[chbuf.modes_used++] = mode = '-';
         switch (change->args[arg].mode & ~MODE_REMOVE) {
         case MODE_BAN:
-            mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
+            mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
+            break;
+        case MODE_EXEMPT:
+            mod_chanmode_append(&chbuf, 'e', change->args[arg].u.hostmask);
             break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
-                mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_HALFOP)
-                mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'h', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
-                mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
             break;
         }
     }
@@ -2341,8 +2583,9 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         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');
+        DO_MODE_CHAR(REGISTERED, 'z');
+        DO_MODE_CHAR(SSLONLY, 'Z');
+       DO_MODE_CHAR(HIDEMODE, 'L');
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', change->new_key);
@@ -2358,15 +2601,18 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             chbuf.modes[chbuf.modes_used++] = mode = '+';
         switch (change->args[arg].mode) {
         case MODE_BAN:
-            mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
+            mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
+            break;
+        case MODE_EXEMPT:
+            mod_chanmode_append(&chbuf, 'e', change->args[arg].u.hostmask);
             break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
-                mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_HALFOP)
-                mod_chanmode_append(&chbuf, 'h', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'h', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
-                mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
             break;
         }
     }
@@ -2406,8 +2652,9 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         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');
+        DO_MODE_CHAR(REGISTERED, 'z');
+        DO_MODE_CHAR(SSLONLY, 'Z');
+       DO_MODE_CHAR(HIDEMODE, 'L');
 #undef DO_MODE_CHAR
     }
     if (change->modes_set) {
@@ -2429,8 +2676,9 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         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');
+        DO_MODE_CHAR(REGISTERED, 'z');
+        DO_MODE_CHAR(SSLONLY, 'Z');
+       DO_MODE_CHAR(HIDEMODE, 'L');
 #undef DO_MODE_CHAR
         switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
         case MODE_KEY|MODE_LIMIT:
@@ -2473,9 +2721,10 @@ clear_chanmode(struct chanNode *channel, const char *modes)
             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_NOCOLORS; break;
         case 'C': remove |= MODE_NOCTCPS; break;
         case 'S': remove |= MODE_STRIPCOLOR; break;
         case 'M': remove |= MODE_MODUNREG; break;
@@ -2483,6 +2732,9 @@ clear_chanmode(struct chanNode *channel, const char *modes)
         case 'Q': remove |= MODE_NOQUITMSGS; break;
         case 'T': remove |= MODE_NOAMSG; break;
         case 'O': remove |= MODE_OPERSONLY; break;
+        case 'z': remove |= MODE_REGISTERED; break;
+        case 'Z': remove |= MODE_SSLONLY; break;
+       case 'L': remove |= MODE_HIDEMODE; break;
         }
     }
 
@@ -2500,6 +2752,14 @@ clear_chanmode(struct chanNode *channel, const char *modes)
         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_HALFOP | MODE_VOICE)) && channel->members.used) {
         int mask = ~(remove & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));