]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
Fixed bug in P10 B64 IPv6 parsing where the entire IPv6 IP after the :: was ommited.
[irc/evilnet/x3.git] / src / proto-p10.c
index 37466e8fc1aeba5d427cfdef69bbc93f17bbaac2..421bfe6a0849a146f64d406ffd57e9e2df9c257f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * x3 is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -29,6 +29,7 @@
 /* Full commands. */
 #define CMD_ACCOUNT            "ACCOUNT"
 #define CMD_ADMIN               "ADMIN"
+#define CMD_ALIST               "ALIST"
 #define CMD_ASLL               "ASLL"
 #define CMD_AWAY                "AWAY"
 #define CMD_BURST               "BURST"
@@ -79,6 +80,7 @@
 #define CMD_PROTO               "PROTO"
 #define CMD_QUIT                "QUIT"
 #define CMD_REHASH              "REHASH"
+#define CMD_REMOVE             "REMOVE"
 #define CMD_RESET              "RESET"
 #define CMD_RESTART             "RESTART"
 #define CMD_RPING               "RPING"
 #define CMD_SERVSET             "SERVSET"
 #define CMD_SET                        "SET"
 #define CMD_SETTIME             "SETTIME"
+#define CMD_SGLINE              "SGLINE"
 #define CMD_SHUN               "SHUN"
 #define CMD_SILENCE             "SILENCE"
+#define CMD_SMO                 "SMO"
+#define CMD_SNO                 "SNO"
+#define CMD_SSHUN              "SSHUN"
+#define CMD_SPAMFILTER          "SPAMFILTER"
 #define CMD_SQUERY              "SQUERY"
 #define CMD_SQUIT               "SQUIT"
 #define CMD_STATS               "STATS"
 #define CMD_SVSJOIN             "SVSJOIN"
 #define CMD_SVSNICK             "SVSNICK"
 #define CMD_SVSPART             "SVSPART"
+#define CMD_SVSQUIT             "SVSQUIT"
 #define CMD_SWHOIS              "SWHOIS"
 #define CMD_TIME                "TIME"
 #define CMD_TOPIC               "TOPIC"
 #define CMD_WHO                 "WHO"
 #define CMD_WHOIS               "WHOIS"
 #define CMD_WHOWAS              "WHOWAS"
+#define CMD_ZLINE              "ZLINE"
 
 /* Tokenized commands. */
 #define TOK_ACCOUNT            "AC"
 #define TOK_ADMIN               "AD"
+#define TOK_ALIST               "AL"
 #define TOK_ASLL               "LL"
 #define TOK_AWAY                "A"
 #define TOK_BURST               "B"
 #define TOK_PROTO               "PROTO"
 #define TOK_QUIT                "Q"
 #define TOK_REHASH              "REHASH"
+#define TOK_REMOVE             "RM"
 #define TOK_RESET              "RESET"
 #define TOK_RESTART             "RESTART"
 #define TOK_RPING               "RI"
 #define TOK_SERVSET             "SERVSET"
 #define TOK_SET                        "SET"
 #define TOK_SETTIME             "SE"
+#define TOK_SGLINE              "SGL"
 #define TOK_SHUN               "SU"
 #define TOK_SILENCE             "U"
+#define TOK_SMO                 "SMO"
+#define TOK_SNO                 "SNO"
+#define TOK_SSHUN              "SSU"
+#define TOK_SPAMFILTER          "SF"
 #define TOK_SQUERY              "SQUERY"
 #define TOK_SQUIT               "SQ"
 #define TOK_STATS               "R"
 #define TOK_SVSJOIN             "SJ"
 #define TOK_SVSNICK             "SN"
 #define TOK_SVSPART             "SP"
+#define TOK_SVSQUIT             "SX"
 #define TOK_SWHOIS              "SW"
 #define TOK_TIME                "TI"
 #define TOK_TOPIC               "T"
 #define TOK_WHO                 "H"
 #define TOK_WHOIS               "W"
 #define TOK_WHOWAS              "X"
+#define TOK_ZLINE              "ZL"
 
 /* Protocol messages; aliased to full commands or tokens depending
    on compile-time configuration. ircu prefers tokens WITH THE
 #define P10_PROTO               TYPE(PROTO)
 #define P10_QUIT                TYPE(QUIT)
 #define P10_REHASH              TYPE(REHASH)
+#define P10_REMOVE             TYPE(REMOVE)
 #define P10_RESET              TYPE(RESET)
 #define P10_RESTART             TYPE(RESTART)
 #define P10_RPING               TYPE(RPING)
 #define P10_SERVSET             TYPE(SERVSET)
 #define P10_SET                        TYPE(SET)
 #define P10_SETTIME             TYPE(SETTIME)
+#define P10_SGLINE              TYPE(SGLINE)
 #define P10_SHUN               TYPE(SHUN)
 #define P10_SILENCE             TYPE(SILENCE)
+#define P10_SMO                 TYPE(SMO)
+#define P10_SNO                 TYPE(SNO)
+#define P10_SSHUN              TYPE(SSHUN)
+#define P10_SPAMFILTER          TYPE(SPAMFILTER)
 #define P10_SQUERY              TYPE(SQUERY)
 #define P10_SQUIT               TYPE(SQUIT)
 #define P10_STATS               TYPE(STATS)
 #define P10_SVSJOIN             TYPE(SVSJOIN)
 #define P10_SVSNICK             TYPE(SVSNICK)
 #define P10_SVSPART             TYPE(SVSPART)
+#define P10_SVSQUIT            TYPE(SVSQUIT)
 #define P10_SWHOIS              TYPE(SWHOIS)
 #define P10_TIME                TYPE(TIME)
 #define P10_TOPIC               TYPE(TOPIC)
 #define P10_WHO                 TYPE(WHO)
 #define P10_WHOIS               TYPE(WHOIS)
 #define P10_WHOWAS              TYPE(WHOWAS)
+#define P10_ZLINE              TYPE(ZLINE)
 #define P10_EXEMPT             TYPE(EXEMPT)
 
 /* Servers claiming to have a boot or link time before PREHISTORY
@@ -319,10 +345,13 @@ static unsigned int num_privmsg_funcs;
 static privmsg_func_t *notice_funcs;
 static unsigned int num_notice_funcs;
 static struct dict *unbursted_channels;
-static char *his_servername;
-static char *his_servercomment;
+static const char *his_servername;
+static const char *his_servercomment;
 static int extended_accounts;
 
+/* These correspond to 1 << X:      012345678901234567 */
+const char irc_user_mode_chars[] = "o iw dkgn        I";
+
 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);
 
 extern int off_channel;
@@ -380,6 +409,32 @@ GetUserN(const char *numeric) /* using numeric */
     return un;
 }
 
+extern struct userNode *opserv;
+static void
+check_ctcp(struct userNode *user, struct userNode *bot, char *text, UNUSED_ARG(int server_qualified))
+{
+    char *cmd;
+    /* if its a version reply, do an alert check (only alerts with version=something) */
+    if(bot == opserv) {
+        if(text[0] == '\001') {
+            text++;
+            cmd = mysep(&text, " ");
+            if(!irccasecmp(cmd, "VERSION")) {
+                char *version = mysep(&text, "\n");
+                if(!version)
+                    version = "";
+                /* opserv_debug("Opserv got CTCP VERSION Notice from %s: %s", user->nick, version); */
+                /* TODO: setup a ctcp_funcs thing to handle this and other CTCPS properly */
+                user->version_reply = strdup(version);
+                /* TODO: put this in the db */
+                if(match_ircglob(version, "WebTV;*"))
+                    user->no_notice = true; /* webbies cant see notices */
+            }
+        }
+    }
+}
+
+
 static void
 privmsg_user_helper(struct userNode *un, void *data)
 {
@@ -391,6 +446,7 @@ privmsg_user_helper(struct userNode *un, void *data)
         }
     } else {
         if ((num < num_notice_funcs) && notice_funcs[num]) {
+            check_ctcp(pd->user, un, pd->text, pd->is_qualified);
             notice_funcs[num](pd->user, un, pd->text, pd->is_qualified);
         }
     }
@@ -440,15 +496,15 @@ irc_server(struct server *srv)
 
     inttobase64(extranum, srv->num_mask, (srv->numeric[1] || (srv->num_mask >= 64*64)) ? 3 : 2);
     if (srv == self) {
-        putsock(P10_SERVER " %s %d %li %li J10 %s%s +s6 :%s",
-                srv->name, srv->hops+1, srv->boot, srv->link, srv->numeric, extranum, srv->description);
+        putsock(P10_SERVER " %s %d " FMT_TIME_T " " FMT_TIME_T " J10 %s%s +s6 :%s",
+                srv->name, srv->hops+1, srv->boot, srv->link_time, srv->numeric, extranum, srv->description);
     } else {
-        putsock("%s " P10_SERVER " %s %d %li %li %c10 %s%s +s6 :%s",
-                self->numeric, srv->name, srv->hops+1, srv->boot, srv->link, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description);
+        putsock("%s " P10_SERVER " %s %d " FMT_TIME_T " " FMT_TIME_T " %c10 %s%s +s6 :%s",
+                self->numeric, srv->name, srv->hops+1, srv->boot, srv->link_time, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description);
     }
 }
 
-static void
+void
 irc_p10_pton(irc_in_addr_t *ip, const char *input)
 {
     if (strlen(input) == 6) {
@@ -464,7 +520,7 @@ irc_p10_pton(irc_in_addr_t *ip, const char *input)
         do {
             if (*input == '_') {
                 unsigned int left;
-                for (left = (25 - strlen(input)) / 3; left; left--)
+                for (left = (25 - strlen(input)) / 3 - pos; left; left--)
                     ip->in6[pos++] = 0;
                 input++;
             } else {
@@ -475,7 +531,7 @@ irc_p10_pton(irc_in_addr_t *ip, const char *input)
     }
 }
 
-static void
+void
 irc_p10_ntop(char *output, const irc_in_addr_t *ip)
 {
     if (!irc_in_addr_is_valid(*ip)) {
@@ -527,43 +583,18 @@ void
 irc_user(struct userNode *user)
 {
     char b64ip[25];
-    if (!user)
+    if (!user || IsDummy(user))
         return;
     irc_p10_ntop(b64ip, &user->ip);
     if (user->modes) {
-        int modelen;
         char modes[32];
 
-        modelen = 0;
-        if (IsOper(user))
-            modes[modelen++] = 'o';
-        if (IsInvisible(user))
-            modes[modelen++] = 'i';
-        if (IsWallOp(user))
-            modes[modelen++] = 'w';
-        if (IsService(user))
-            modes[modelen++] = 'k';
-        if (IsServNotice(user))
-            modes[modelen++] = 's';
-        if (IsDeaf(user))
-            modes[modelen++] = 'd';
-        if (IsGlobal(user))
-            modes[modelen++] = 'g';
-        // sethost - reed/apples
-        // if (IsHelperIrcu(user))
-        if (IsSetHost(user))
-            modes[modelen++] = 'h';
-        if (IsFakeHost(user))
-            modes[modelen++] = 'f';
-        if (IsHiddenHost(user))
-            modes[modelen++] = 'x';
-        modes[modelen] = 0;
-
+        irc_user_modes(user, modes, sizeof(modes));
         /* we don't need to put the + in modes because it's in the format string. */
-        putsock("%s " P10_NICK " %s %d %li %s %s +%s %s %s :%s",
+        putsock("%s " P10_NICK " %s %d " FMT_TIME_T " %s %s +%s %s %s :%s",
                 user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, modes, b64ip, user->numeric, user->info);
     } else {
-        putsock("%s " P10_NICK " %s %d %li %s %s %s %s :%s",
+        putsock("%s " P10_NICK " %s %d " FMT_TIME_T " %s %s %s %s :%s",
                 user->uplink->numeric, user->nick, user->uplink->hops+1, user->timestamp, user->ident, user->hostname, b64ip, user->numeric, user->info);
     }
 }
@@ -586,9 +617,9 @@ 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); 
+        putsock("%s " P10_ACCOUNT " %s R %s "FMT_TIME_T, self->numeric, user->numeric, stamp, timestamp); 
     else
-        putsock("%s " P10_ACCOUNT " %s %s %lu", self->numeric, user->numeric, stamp, timestamp);
+        putsock("%s " P10_ACCOUNT " %s %s "FMT_TIME_T, self->numeric, user->numeric, stamp, timestamp);
 }
 
 void
@@ -662,22 +693,60 @@ irc_wallops(const char *format, ...)
     putsock("%s " P10_WALLOPS " :%s", self->numeric, buffer);
 }
 
+static int
+deliver_to_dummy(struct userNode *source, struct userNode *dest, const char *message, int type)
+{
+    unsigned int num;
+
+    if (!dest || !IsDummy(dest) || !IsLocal(dest))
+        return 0;
+    num = dest->num_local;
+    switch (type) {
+    default:
+        if ((num < num_notice_funcs) && notice_funcs[num])
+            notice_funcs[num](source, dest, message, 0);
+        break;
+    case 1:
+        if ((num < num_privmsg_funcs) && privmsg_funcs[num])
+            privmsg_funcs[num](source, dest, message, 0);
+        break;
+    }
+    return 1;
+}
+
 void
 irc_notice(struct userNode *from, const char *to, const char *message)
 {
-    putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message);
+    if (to[0] == '#' || to[0] == '$'
+        || !deliver_to_dummy(from, GetUserN(to), message, 0))
+        putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message);
 }
 
 void
 irc_notice_user(struct userNode *from, struct userNode *to, const char *message)
 {
-    putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message);
+    if (!deliver_to_dummy(from, to, message, 0))
+        putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message);
 }
 
 void
 irc_privmsg(struct userNode *from, const char *to, const char *message)
 {
-    putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message);
+    if (to[0] == '#' || to[0] == '$'
+        || !deliver_to_dummy(from, GetUserN(to), message, 1))
+        putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message);
+}
+
+void
+irc_privmsg_user(struct userNode *from, struct userNode *to, const char *message)
+{
+    putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to->numeric, message);
+}
+
+void 
+irc_version_user(struct userNode *from, struct userNode *to)
+{
+    irc_privmsg_user(from, to, "\001VERSION\001");
 }
 
 void
@@ -704,6 +773,12 @@ irc_eob_ack(void)
     }
 }
 
+void
+irc_rpong(const char *from1, const char *from2, const char *pingtime, const char *clientinfo)
+{
+    putsock("%s " P10_RPONG " %s %s %s :%s", self->numeric, from1, from2, pingtime, clientinfo);
+}
+
 void
 irc_ping(const char *payload)
 {
@@ -721,14 +796,14 @@ irc_pong_asll(const char *who, const char *orig_ts)
 {
     char *delim;
     struct timeval orig;
-    struct timeval now;
+    struct timeval sys_now;
     int diff;
 
     orig.tv_sec = strtoul(orig_ts, &delim, 10);
     orig.tv_usec = (*delim == '.') ? strtoul(delim + 1, NULL, 10) : 0;
-    gettimeofday(&now, NULL);
-    diff = (now.tv_sec - orig.tv_sec) * 1000 + (now.tv_usec - orig.tv_usec) / 1000;
-    putsock("%s " P10_PONG " %s %s %d " FMT_TIME_T ".%06u", self->numeric, who, orig_ts, diff, now.tv_sec, (unsigned)now.tv_usec);
+    gettimeofday(&sys_now, NULL);
+    diff = (sys_now.tv_sec - orig.tv_sec) * 1000 + (sys_now.tv_usec - orig.tv_usec) / 1000;
+    putsock("%s " P10_PONG " %s %s %d %lu.%06lu", self->numeric, who, orig_ts, diff, (unsigned long)sys_now.tv_sec, (unsigned long)sys_now.tv_usec);
 }
 
 void
@@ -752,14 +827,14 @@ irc_introduce(const char *passwd)
 void
 irc_gline(struct server *srv, struct gline *gline, int silent)
 {
-    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);
+    putsock("%s " P10_GLINE " %s +%s " FMT_TIME_T " " FMT_TIME_T " :%s<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, 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",
+    putsock("%s " P10_SHUN " %s +%s " FMT_TIME_T " :<%s> %s",
             self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->issuer, shun->reason);
 }
 
@@ -784,7 +859,7 @@ irc_unshun(const char *mask)
     putsock("%s " P10_SHUN " * -%s", self->numeric, mask);
 }
 
-static void
+void
 irc_burst(struct chanNode *chan)
 {
     char burst_line[512];
@@ -793,13 +868,15 @@ irc_burst(struct chanNode *chan)
     struct banNode *bn;
     struct exemptNode *en;
     long last_mode=-1;
+    unsigned int first_ban;
+    unsigned int first_exempt;
     unsigned int n;
 
     base_len = sprintf(burst_line, "%s " P10_BURST " %s " FMT_TIME_T " ",
                        self->numeric, chan->name, chan->timestamp);
     len = irc_make_chanmode(chan, burst_line+base_len);
     pos = base_len + len;
-    if (len)
+    if (len > 0 && chan->members.used > 0)
         burst_line[pos++] = ' ';
 
     if(chan->members.used < 1)
@@ -813,6 +890,7 @@ irc_burst(struct chanNode *chan)
             pos = base_len;
             last_mode = -1;
         }
+
         memcpy(burst_line+pos, mn->user->numeric, strlen(mn->user->numeric));
         pos += strlen(mn->user->numeric);
         if (mn->modes && (mn->modes != last_mode)) {
@@ -830,55 +908,53 @@ irc_burst(struct chanNode *chan)
     }
     if (chan->banlist.used) {
         /* dump the bans */
-        if (pos+2+strlen(chan->banlist.list[0]->ban) > 505) {
-            burst_line[pos-1] = 0;
-            putsock("%s", burst_line);
-            pos = base_len;
-        } else {
-            burst_line[pos++] = ' ';
-        }
+        first_ban = 1;
 
-        burst_line[pos++] = ':';
-        burst_line[pos++] = '%';
-        base_len = pos;
-        for (n=0; n<chan->banlist.used; n++) {
+        for (n=0; n<chan->banlist.used; ) {
+            if (first_ban && (pos < 500)) {
+                burst_line[pos++] = ':';
+                burst_line[pos++] = '%';
+            }
             bn = chan->banlist.list[n];
             len = strlen(bn->ban);
-            if (pos+len+1 > 510) {
-                burst_line[pos-1] = 0; /* -1 to back up over the space or comma */
+            if (pos + 2 + len < 505) {
+                memcpy(burst_line + pos, bn->ban, len);
+                pos += len;
+                burst_line[pos++] = ' ';
+                first_ban = 0;
+                n++;
+            } else {
+                burst_line[pos-1] = 0;
                 putsock("%s", burst_line);
                 pos = base_len;
+                first_ban = 1;
             }
-            memcpy(burst_line+pos, bn->ban, len);
-            pos += len;
-            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++) {
+        /* dump the exempts */
+        first_exempt = 1;
+
+        for (n=0; n<chan->exemptlist.used; ) {
+            if (first_exempt && (pos < 500)) {
+                burst_line[pos++] = ' ';
+                burst_line[pos++] = '~';
+                burst_line[pos++] = ' ';
+            }
             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 */
+            if (pos + 2 + len < 505) {
+                memcpy(burst_line + pos, en->exempt, len);
+                pos += len;
+                burst_line[pos++] = ' ';
+                first_exempt = 0;
+                n++;
+            } else {
+                burst_line[pos-1] = 0;
                 putsock("%s", burst_line);
                 pos = base_len;
+                first_exempt = 1;
             }
-            memcpy(burst_line+pos, en->exempt, len);
-            pos += len;
-            burst_line[pos++] = ' ';
         }
     }
     /* print the last line */
@@ -949,10 +1025,10 @@ void
 irc_join(struct userNode *who, struct chanNode *what)
 {
     if (what->members.used == 1) {
-        putsock("%s " P10_CREATE " %s %lu",
+        putsock("%s " P10_CREATE " %s "FMT_TIME_T,
                 who->numeric, what->name, what->timestamp);
     } else {
-        putsock("%s " P10_JOIN " %s %lu", who->numeric, what->name, what->timestamp);
+        putsock("%s " P10_JOIN " %s "FMT_TIME_T, who->numeric, what->name, what->timestamp);
     }
 }
 
@@ -962,6 +1038,18 @@ irc_svsjoin(struct userNode *from, struct userNode *who, struct chanNode *to)
     putsock("%s " P10_SVSJOIN " %s %s "FMT_TIME_T, from->uplink->numeric, who->numeric, to->name, now);
 }
 
+void
+irc_svspart(struct userNode *from, struct userNode *who, struct chanNode *to)
+{
+    putsock("%s " P10_SVSPART " %s %s", from->uplink->numeric, who->numeric, to->name);
+}
+
+void
+irc_svsquit(struct userNode *from, struct userNode *who, char const *reason)
+{
+    putsock("%s " P10_SVSQUIT " %s :%s", from->uplink->numeric, who->numeric, reason);
+}
+
 void
 irc_kick(struct userNode *who, struct userNode *target, struct chanNode *channel, const char *msg)
 {
@@ -1006,8 +1094,7 @@ void
 irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
 {
 
-   int type = 4;
-   int host_in_topic = 0;
+   int type = 4, host_in_topic = 0, hasident = 0;
    const char *hstr, *tstr;
    char *host, *hostmask;
    char shost[MAXLEN];
@@ -1025,12 +1112,17 @@ irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what,
           safestrncpy(shost, who->fakehost, sizeof(shost));
       else if (IsSetHost(who)) {
           hostmask = strdup(who->sethost);
-          if ((host = (strrchr(hostmask, '@'))))
+          if ((host = (strrchr(hostmask, '@')))) {
+              hasident = 1;
               *host++ = '\0';
-          else
+          else
               host = hostmask;
 
-          safestrncpy(sident, hostmask, sizeof(shost));
+          if (hasident)
+              safestrncpy(sident, hostmask, sizeof(shost));
+          else
+              safestrncpy(sident, who->ident, sizeof(shost));
+
           safestrncpy(shost, host, sizeof(shost));
       } else
           safestrncpy(shost, who->hostname, sizeof(shost));
@@ -1038,7 +1130,7 @@ irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what,
       host_in_topic = atoi(hstr);
    }
 
-   if (type == 5) {
+   if (type >= 5) {
      putsock("%s " P10_TOPIC " %s %s%s%s%s%s " FMT_TIME_T " " FMT_TIME_T " :%s", service->numeric, what->name,
              who->nick, host_in_topic ? "!" : "", host_in_topic ? (IsSetHost(who) ? sident : who->ident) : "", 
              host_in_topic ? "@" : "", host_in_topic ? shost : "", what->timestamp, now, topic);
@@ -1065,6 +1157,57 @@ irc_numeric(struct userNode *user, unsigned int num, const char *format, ...)
     putsock(":%s %03d %s %s", self->name, num, user->nick, buffer);
 }
 
+void
+irc_mark(struct userNode *user, char *mark)
+{
+    char *host = user->hostname;
+
+    /* TODO: Allow mark overwrite. If they are marked, and their fakehost is oldmark.hostname, update it to newmark.hostname so mark can be called multiple times. Probably requires ircd modification also */
+    if(user->mark)
+        return;
+
+    /* if the mark will put us over the  host length, clip some off the left hand side
+     * to make room...
+     */
+    if(strlen(host) + 1 + strlen(mark) > HOSTLEN)
+        host += 1 + ( (strlen(host) + 1 + strlen(mark)) - HOSTLEN );
+    putsock("%s " CMD_MARK " %s DNSBL +m %s.%s", self->numeric, user->nick, mark, host);
+    putsock("%s " CMD_MARK " %s DNSBL_DATA %s", self->numeric, user->nick, mark);
+
+    /* Save it in the user */
+    user->mark = strdup(mark);
+
+    /* If they are not otherwise marked, mark their host with fakehost */
+    if(!IsFakeHost(user) && !IsSetHost(user) && !(IsHiddenHost(user) && user->handle_info) )
+    {
+        struct modeNode *mn = NULL;
+        char fakehost[HOSTLEN];
+        unsigned int count = 0;
+        unsigned int n = 0;
+
+        putsock("%s " CMD_FAKEHOST " %s %s.%s", self->numeric, user->numeric, mark, host);
+        putsock("%s " CMD_MODE " %s +x", self->numeric, user->nick);
+        
+        snprintf(fakehost, sizeof(fakehost), "%s.%s", mark, host);
+        safestrncpy(user->fakehost, fakehost, sizeof(user->fakehost));
+
+        for (n=count=0; n<user->channels.used; n++) {
+            mn = user->channels.list[n];
+            if (strlen(mn->channel->name) >= 1) /* Sanity */
+                check_bans(user, mn->channel->name);
+        }
+    }
+}
+
+void irc_sno(unsigned int mask, char const* format, ...) {
+    va_list arg_list;
+    char buffer[MAXLEN];
+    va_start(arg_list, format);
+    vsnprintf(buffer, MAXLEN-2, format, arg_list);
+    buffer[MAXLEN-1] = 0;
+    putsock("%s " CMD_SNO " %d :%s", self->numeric, mask, buffer);
+}
+
 static void send_burst(void);
 
 static void
@@ -1089,6 +1232,8 @@ static CMD_FUNC(cmd_whois)
 {
     struct userNode *from;
     struct userNode *who;
+    char buf[MAXLEN];
+    unsigned int i, mlen, len;
 
     if (argc < 3)
         return 0;
@@ -1096,21 +1241,75 @@ static CMD_FUNC(cmd_whois)
         log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
         return 0;
     }
-    if(!(who = GetUserH(argv[2]))) {
+    if (!(who = GetUserH(argv[2]))) {
         irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
         return 1;
     }
-    if (IsHiddenHost(who) && !IsOper(from)) {
-        /* Just stay quiet. */
-        return 1;
+
+    if (IsFakeHost(who) && IsHiddenHost(who))
+        irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->fakehost, who->info);
+    else if (IsHiddenHost(who) && who->handle_info && hidden_host_suffix)
+        irc_numeric(from, RPL_WHOISUSER, "%s %s %s.%s * :%s", who->nick, who->ident, who->handle_info->handle, hidden_host_suffix, who->info);
+    else
+        irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
+
+    if (IsService(who) || (from == who)) {
+        struct modeNode *mn;
+        mlen = strlen(self->name) + strlen(from->nick) + 12 + strlen(who->nick);
+        len = 0;
+        *buf = '\0';
+        for (i = who->channels.used; i > 0; )
+        {
+            mn = who->channels.list[--i];
+
+            if (!IsOper(from) && (mn->channel->modes & (MODE_PRIVATE | MODE_SECRET)) && !GetUserMode(mn->channel, from))
+                continue;
+
+            if (len + strlen(mn->channel->name) + mlen > MAXLEN - 5)
+            {
+                irc_numeric(from, RPL_WHOISCHANNELS, "%s :%s", who->nick, buf);
+                *buf = '\0';
+                len = 0;
+            }
+
+            if (IsDeaf(who))
+                *(buf + len++) = '-';
+            if ((mn->channel->modes & (MODE_PRIVATE | MODE_SECRET)) && !GetUserMode(mn->channel, from))
+                *(buf + len++) = '*';
+            if (mn->modes & MODE_CHANOP)
+                *(buf + len++) = '@';
+            else if (mn->modes & MODE_HALFOP)
+                *(buf + len++) = '%';
+            else if (mn->modes & MODE_VOICE)
+                *(buf + len++) = '+';
+
+            if (len)
+                *(buf + len) = '\0';
+            strcpy(buf + len, mn->channel->name);
+            len += strlen(mn->channel->name);
+            strcat(buf + len, " ");
+            len++;
+        }
+        if (buf[0] != '\0')
+            irc_numeric(from, RPL_WHOISCHANNELS, "%s :%s", who->nick, buf);
     }
-    irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
-    if (his_servername && his_servercomment)
+
+    if (his_servername && his_servercomment && !IsOper(from) && from != who)
         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 (IsAway(who))
+        irc_numeric(from, RPL_AWAY, "%s :Away", who->nick);
     if (IsOper(who))
-        irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
+        irc_numeric(from, RPL_WHOISOPERATOR, "%s :%s", who->nick, IsLocal(who) ? "is a megalomaniacal power hungry tyrant" : "is an IRC Operator");
+    if (who->handle_info)
+        irc_numeric(from, RPL_WHOISACCOUNT, "%s %s :is logged in as", who->nick, who->handle_info->handle);
+    if (IsHiddenHost(who) && who->handle_info && (IsOper(from) || from == who))
+        irc_numeric(from, RPL_WHOISACTUALLY, "%s %s@%s %s :Actual user@host, Actual IP", who->nick, who->ident, who->hostname, irc_ntoa(&who->ip));
+    if (IsLocal(who) && !IsService(who) && (IsOper(from) || from == who))
+        irc_numeric(from, RPL_WHOISIDLE, "%s %ld %ld :seconds idle, signon time", who->nick, now - who->idle_since, who->timestamp);
+
     irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
     return 1;
 }
@@ -1157,7 +1356,8 @@ static CMD_FUNC(cmd_server)
              * Alternately, we are same age, but we accept their time
              * since we are linking to them. */
             self->boot = srv->boot;
-            ioset_set_time(srv->link);
+            ioset_set_time(srv->link_time
+);
         }
     }
     if (srv == self->uplink) {
@@ -1172,7 +1372,6 @@ static CMD_FUNC(cmd_eob)
 {
     struct server *sender;
     dict_iterator_t it;
-    unsigned int ii;
 
     if (!(sender = GetServerH(origin)))
         return 0;
@@ -1188,12 +1387,12 @@ static CMD_FUNC(cmd_eob)
         /* now that we know who our uplink is,
          * we can center the routing map and activate auto-routing.
          */
-        activate_routing(NULL, NULL, NULL);
+        //activate_routing(NULL, NULL, NULL);
+        routing_init();
     }
     sender->self_burst = 0;
     recalc_bursts(sender);
-    for (ii=0; ii<slf_used; ii++)
-        slf_list[ii](sender);
+    call_server_link_funcs(sender);
     /* let auto-routing figure out if we were
      * wating on this server to link a child to it */
     /* DONT call this if uplink is _US_ */
@@ -1214,6 +1413,19 @@ static CMD_FUNC(cmd_eob_ack)
     return 1;
 }
 
+static CMD_FUNC(cmd_rping)
+{
+    struct server *dest;
+
+    if (!(dest = GetServerN(argv[1])))
+        return 1; /* if its a jupe or something, return 1 so its silently ignored */
+
+    if (dest == self)
+        irc_rpong(argv[2], argv[3], argv[4], argv[5]);
+
+    return 1;
+}
+
 static CMD_FUNC(cmd_ping)
 {
     struct server *srv;
@@ -1371,11 +1583,40 @@ static CMD_FUNC(cmd_account)
     
     if(!strcmp(argv[2],"C"))
     {
-        if((hi = loc_auth(argv[4], argv[5])))
+        if((hi = loc_auth(NULL, argv[4], argv[5], NULL)))
         {
             /* Return a AC A */
-            putsock("%s " P10_ACCOUNT " %s A %s %lu", self->numeric, server->numeric , argv[3], hi->registered);
+            putsock("%s " P10_ACCOUNT " %s A %s "FMT_TIME_T, self->numeric, server->numeric , argv[3], hi->registered);
 
+                       log_module(MAIN_LOG, LOG_DEBUG, "loc_auth: %s\n", user->nick);
+         }
+        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],"H")) /* New enhanced (host) version of C */
+    {
+        if((hi = loc_auth(NULL, argv[5], argv[6], argv[4] )))
+        {
+            /* Return a AC A */
+            putsock("%s " P10_ACCOUNT " %s A %s "FMT_TIME_T, 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],"S"))
+    {
+        if((hi = loc_auth(argv[5], argv[6], argv[7], argv[4])))
+        {
+            /* Return a AC A */
+            putsock("%s " P10_ACCOUNT " %s A %s "FMT_TIME_T, self->numeric, server->numeric , argv[3], hi->registered);
         }
         else
         {
@@ -1416,7 +1657,10 @@ static struct {
   P(BADCHAN),        P(LOCAL_BADCHAN),  P(SEE_CHAN),      P(PROPAGATE),
   P(DISPLAY),        P(SEE_OPERS),      P(WIDE_GLINE),    P(FORCE_OPMODE),
   P(FORCE_LOCAL_OPMODE), P(REMOTEREHASH), P(CHECK), P(SEE_SECRET_CHAN),
-  P(SHUN),           P(LOCAL_SHUN),     P(WIDE_SHUN),
+  P(SHUN),           P(LOCAL_SHUN),     P(WIDE_SHUN),     P(ZLINE),
+  P(LOCAL_ZLINE),    P(WIDE_ZLINE),     P(LIST_CHAN),     P(WHOIS_NOTICE),
+  P(HIDE_IDLE),      P(XTRAOP),         P(HIDE_CHANNELS), P(DISPLAY_MODE),
+  P(FREEFORM),       P(REMOVE),         P(SPAMFILTER),
 #undef P
   { 0, 0 }
 };
@@ -1438,6 +1682,16 @@ char *client_report_privs(struct userNode *client)
   return privbuf;
 }
 
+int clear_privs(struct userNode *who) {
+  int i = 0;
+  assert(0 != who);
+
+  for (i = 0; privtab[i].name; i++)
+    RevokePriv(who, privtab[i].priv);
+
+  return 0;
+}
+
 int client_modify_priv_by_name(struct userNode *who, char *priv, int what) {
  int i = 0;
  assert(0 != priv);
@@ -1474,45 +1728,64 @@ irc_privs(struct userNode *target, char *flag, int add)
     putsock("%s " P10_PRIVS " %s %s%s", self->numeric, target->numeric, (add == PRIV_ADD) ? "+" : "-", flag);
 }
 
+void
+irc_raw_privs(struct userNode *target, const char *privs)
+{
+    putsock("%s " P10_PRIVS " %s %s", self->numeric, target->numeric,  privs);
+}
+
 static CMD_FUNC(cmd_privs)
 {
-  char *tstr = NULL;
-  int type = 0;
+    char *tstr = NULL;
+    char buf[512] = "";
+    char *p = 0;
+    char *tmp;
 
-  tstr = conf_get_data("server/type", RECDB_QSTRING);
-  if(tstr)
-    type = atoi(tstr);
+    int what = PRIV_ADD;
+    int type = 0;
 
-  if (type < 6)
-    return 1; /* silently ignore */
+    unsigned int i;
 
-  struct userNode *user = argc > 1 ? GetUserN(argv[1]) : NULL;
-  char buf[512] = "";
-  int what = PRIV_ADD;
-  char *p = 0;
-  char *tmp;
-  unsigned int i;
+    struct server *sender;
+    struct userNode *user;
 
-  if (argc < 3)
-    return 0;
 
-  if (!user)
-    return 0;
+    tstr = conf_get_data("server/type", RECDB_QSTRING);
+    if(tstr)
+        type = atoi(tstr);
 
-  for (i=1; i<argc; i++) {
-    strcat(buf, argv[i]);
-    strcat(buf, " ");
-  }
 
-  for (i = 2; i < argc; i++) {
-    if (*argv[i] == '+') { what = PRIV_ADD; argv[i]++; }
-    if (*argv[i] == '-') { what = PRIV_DEL; argv[i]++; }
-    for (tmp = x3_strtok(&p, argv[i], ","); tmp;
-         tmp = x3_strtok(&p, NULL, ",")) {
-      client_modify_priv_by_name(user, tmp, what);
+    if (!(sender = GetServerH(origin))) { /* from oper */
+        return 1; /* ignore as no services have privs set */
+    } else { /* from server */
+        if (type < 5)
+            return 1; /* silently ignore */
+
+        user = argc > 1 ? GetUserN(argv[1]) : NULL;
+
+        if (!user)
+            return 0;
+
+        for (i=1; i<argc; i++) {
+            strcat(buf, argv[i]);
+            strcat(buf, " ");
+        }
+
+        for (i = 2; i < argc; i++) {
+            if (*argv[i] == '+') { what = PRIV_ADD; argv[i]++; }
+            if (*argv[i] == '-') { what = PRIV_DEL; argv[i]++; }
+            for (tmp = x3_strtok(&p, argv[i], ","); tmp;
+                 tmp = x3_strtok(&p, NULL, ",")) {
+                if (!strcmp(tmp, "PRIV_NONE")) {
+                    if (now > user->timestamp+5)
+                        clear_privs(user);
+                    break;
+                } else
+                    client_modify_priv_by_name(user, tmp, what);
+            }
+        }
     }
-  }
-  return 1;
+    return 1;
 }
 
 static CMD_FUNC(cmd_burst)
@@ -1522,6 +1795,7 @@ static CMD_FUNC(cmd_burst)
     static char exemptlist[MAXLEN], banlist[MAXLEN];
     unsigned int next = 3, res = 1;
     int ctype = 0, echeck = 0, bcheck = 0;
+    struct chanData *cData;
     struct chanNode *cNode;
     struct userNode *un;
     struct modeNode *mNode;
@@ -1547,6 +1821,8 @@ static CMD_FUNC(cmd_burst)
                 if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A')
                     || (*pos == 'U'))
                     n_modes++;
+            if (next + n_modes > argc)
+                n_modes = argc - next;
             unsplit_string(argv+next, n_modes, modes);
             next += n_modes;
             break;
@@ -1605,6 +1881,15 @@ static CMD_FUNC(cmd_burst)
         irc_burst(cNode);
     }
     cNode = AddChannel(argv[1], in_timestamp, modes, banlist, exemptlist);
+    cData = cNode->channel_info;
+
+    if (!cData) {
+        if (cNode->modes & MODE_REGISTERED) {
+            irc_join(opserv, cNode);
+            irc_mode(opserv, cNode, "-z");
+            irc_part(opserv, cNode, "");
+        }
+    }
 
     /* Burst channel members in now. */
     for (user = members, sep = *members, mode = 0; sep; user = end) {
@@ -1648,6 +1933,73 @@ static CMD_FUNC(cmd_burst)
     return res;
 }
 
+/* TODO: 
+ * This is a stub that doesn't actually do anything. It should be completed
+ * so that bans on *!*@markname.* match users as it does in nefarious
+ */
+static CMD_FUNC(cmd_mark)
+{
+    struct userNode *target;
+    /* 
+     * log_module(MAIN_LOG, LOG_ERROR, "DEBUG: mark, user %s, type %s, arg %s", argv[1], argv[2], argv[3]);
+     */
+
+    if(argc < 4)
+        return 0;
+    if(!strcasecmp(argv[2], "DNSBL")) {
+        /* DNSBL <modes> */
+        return 1;
+    }
+    else if(!strcasecmp(argv[2], "DNSBL_DATA")) {
+        /* DNSBL_DATA name */
+        target = GetUserH(argv[1]);
+        if(!target) {
+            log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose dnsbl mark is changing.", argv[1]);
+            return 0;
+        }
+        target->mark = strdup(argv[3]);
+        return 1;
+        
+    }
+    else if(!strcasecmp(argv[2], "CVERSION")) {
+        /* CTCP VERSION mark */
+        target = GetUserH(argv[1]);
+        if(!target) {
+            log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose version mark is changing.", argv[1]);
+            return 0;
+        }
+
+        char *version = unsplit_string(argv + 3, argc - 3, NULL);
+        if(!version)
+            version = "";
+
+        target->version_reply = strdup(version);
+
+        if(match_ircglob(version, "WebTV;*"))
+            target->no_notice = true; /* webbies cant see notices */
+
+        return 1;
+    }
+    else if(!strcasecmp(argv[2], "SSLCLIFP")) {
+        /* SSL fingerprint mark */
+        target = GetUserH(argv[1]);
+        if(!target) {
+            log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose SSL fingerprint mark is changing.", argv[1]);
+            return 0;
+        }
+
+        char *sslfp = unsplit_string(argv + 3, argc - 3, NULL);
+        if(!sslfp)
+            sslfp = "";
+
+        target->sslfp = strdup(sslfp);
+
+        return 1;
+    }
+    /* unknown type of mark */
+    return 1;
+}
+
 static CMD_FUNC(cmd_mode)
 {
     struct chanNode *cn;
@@ -1833,17 +2185,29 @@ static CMD_FUNC(cmd_num_topic)
 
 static CMD_FUNC(cmd_num_gline)
 {
-    if (argc < 6)
-        return 0;
-    gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
+    if (argc < 7) {
+        if (argc < 6)
+            return 0;
+        else
+            gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
+    } else {
+        if (!strcmp(argv[5], "+"))
+          gline_add(origin, argv[3], atoi(argv[4])-now, argv[6], 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);
+    if (argc < 7) {
+        if (argc < 6)
+            return 0;
+        else
+            shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
+    } else {
+        if (!strcmp(argv[5], "+"))
+            shun_add(origin, argv[3], atoi(argv[4])-now, argv[6], now, 0);
+    }
     return 1;
 }
 
@@ -1885,26 +2249,13 @@ static CMD_FUNC(cmd_kill)
     return 1;
 }
 
-static CMD_FUNC(cmd_part)
-{
-    struct userNode *user;
-
-    if (argc < 2)
-        return 0;
-    user = GetUserH(origin);
-    if (!user)
-        return 0;
-    parse_foreach(argv[1], part_helper, NULL, NULL, NULL, user);
-    return 1;
-}
-
 static CMD_FUNC(cmd_svspart)
 {
     struct userNode *user;
 
     if (argc < 3)
         return 0;
-    user = GetUserH(argv[1]);
+    user = GetUserN(argv[1]);
     if (!user)
         return 0;
     parse_foreach(argv[2], part_helper, NULL, NULL, NULL, user);
@@ -1993,7 +2344,7 @@ static CMD_FUNC(cmd_squit)
 static CMD_FUNC(cmd_privmsg)
 {
     struct privmsg_desc pd;
-    if (argc != 3)
+    if (argc < 3)
         return 0;
     pd.user = GetUserH(origin);
     if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user)))
@@ -2011,8 +2362,9 @@ static CMD_FUNC(cmd_privmsg)
         return 1; /* Silently Ignore */
 
     pd.is_notice = 0;
-    pd.text = argv[2];
+    pd.text = argv[argc - 1];
     parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
+
     return 1;
 }
 
@@ -2092,6 +2444,34 @@ static CMD_FUNC(cmd_gline)
         return 0;
 }
 
+static CMD_FUNC(cmd_sgline)
+{
+    struct server *sender;
+
+    if (argc < 4)
+        return 0;
+
+    if (!(sender = GetServerH(origin)))
+        return 0;
+
+    gline_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, 1, 0);
+    return 1;
+}
+
+static CMD_FUNC(cmd_sshun)
+{
+    struct server *sender;
+
+    if (argc < 4)
+        return 0;
+
+    if (!(sender = GetServerH(origin)))
+        return 0;
+
+    shun_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, 1);
+    return 1;
+}
+
 static CMD_FUNC(cmd_shun)
 {
     if (argc < 3)
@@ -2120,9 +2500,6 @@ static CMD_FUNC(cmd_svsnick)
     return 1;
 }
 
-static oper_func_t *of_list;
-static unsigned int of_size = 0, of_used = 0;
-
 void
 free_user(struct userNode *user)
 {
@@ -2131,12 +2508,15 @@ free_user(struct userNode *user)
 }
 
 static void
-parse_cleanup(void)
+parse_cleanup(UNUSED_ARG(void *extra))
 {
     unsigned int nn;
     free(of_list);
+    free(of_list_extra);
     free(privmsg_funcs);
+    num_privmsg_funcs = 0;
     free(notice_funcs);
+    num_notice_funcs = 0;
     free(mcf_list);
     dict_delete(irc_func_dict);
     for (nn=0; nn<dead_users.used; nn++)
@@ -2147,10 +2527,12 @@ parse_cleanup(void)
 static void
 p10_conf_reload(void) {
     hidden_host_suffix = conf_get_data("server/hidden_host", RECDB_QSTRING);
+    his_servername = conf_get_data("server/his_servername", RECDB_QSTRING);
+    his_servercomment = conf_get_data("server/his_servercomment", RECDB_QSTRING);
 }
 
 static void
-remove_unbursted_channel(struct chanNode *cNode) {
+remove_unbursted_channel(struct chanNode *cNode, UNUSED_ARG(void *extra)) {
     if (unbursted_channels)
         dict_remove(unbursted_channels, cNode->name);
 }
@@ -2186,11 +2568,6 @@ init_parse(void)
     else
         inttobase64(numer, (numnick << 18) + (usermask & 0x3ffff), 5);
 
-    str = conf_get_data("server/his_servername", RECDB_QSTRING);
-    his_servername = str ? strdup(str) : NULL;
-    str = conf_get_data("server/his_servercomment", RECDB_QSTRING);
-    his_servercomment = str ? strdup(str) : NULL;
-
     str = conf_get_data("server/hostname", RECDB_QSTRING);
     desc = conf_get_data("server/description", RECDB_QSTRING);
     if (!str || !desc) {
@@ -2211,6 +2588,8 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_EOB_ACK, cmd_eob_ack);
     dict_insert(irc_func_dict, CMD_MODE, cmd_mode);
     dict_insert(irc_func_dict, TOK_MODE, cmd_mode);
+    dict_insert(irc_func_dict, CMD_MARK, cmd_mark);
+    dict_insert(irc_func_dict, TOK_MARK, cmd_mark);
     dict_insert(irc_func_dict, CMD_NICK, cmd_nick);
     dict_insert(irc_func_dict, TOK_NICK, cmd_nick);
     dict_insert(irc_func_dict, CMD_ACCOUNT, cmd_account);
@@ -2263,8 +2642,12 @@ 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_SGLINE, cmd_sgline);
+    dict_insert(irc_func_dict, TOK_SGLINE, cmd_sgline);
     dict_insert(irc_func_dict, CMD_SHUN, cmd_shun);
     dict_insert(irc_func_dict, TOK_SHUN, cmd_shun);
+    dict_insert(irc_func_dict, CMD_SSHUN, cmd_sshun);
+    dict_insert(irc_func_dict, TOK_SSHUN, cmd_sshun);
     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);
@@ -2273,6 +2656,15 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_VERSION, cmd_version);
     dict_insert(irc_func_dict, CMD_ADMIN, cmd_admin);
     dict_insert(irc_func_dict, TOK_ADMIN, cmd_admin);
+    dict_insert(irc_func_dict, CMD_SMO, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_SMO, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_SNO, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_SNO, cmd_dummy);
+
+    dict_insert(irc_func_dict, CMD_RPING, cmd_rping);
+    dict_insert(irc_func_dict, TOK_RPING, cmd_rping);
+    dict_insert(irc_func_dict, CMD_RPONG, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_RPONG, cmd_dummy);
 
     /* In P10, DESTRUCT doesn't do anything except be broadcast to servers.
      * Apparently to obliterate channels from any servers that think they
@@ -2301,18 +2693,29 @@ init_parse(void)
     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);
     dict_insert(irc_func_dict, CMD_PRIVS, cmd_privs);
     dict_insert(irc_func_dict, TOK_PRIVS, cmd_privs);
+    /* ignore ALIST for now */
+    dict_insert(irc_func_dict, TOK_ALIST, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_ALIST, cmd_dummy);
+    /* ignore SPAMFILTER */
+    dict_insert(irc_func_dict, TOK_SPAMFILTER, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_SPAMFILTER, 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);
+    /* Ignore ZLINE/ZL/REMOVE/RM */
+    dict_insert(irc_func_dict, CMD_ZLINE, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_ZLINE, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_REMOVE, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_REMOVE, cmd_dummy);
 
     /* ignore /trace and /motd commands targetted at us */
     dict_insert(irc_func_dict, TOK_TRACE, cmd_dummy);
     dict_insert(irc_func_dict, TOK_MOTD, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_UPING, cmd_dummy);
 
     /* handle topics */
     dict_insert(irc_func_dict, "331", cmd_num_topic);
@@ -2322,6 +2725,7 @@ init_parse(void)
     dict_insert(irc_func_dict, "432", cmd_error_nick); /* Erroneus [sic] nickname */
     /* ban list resetting */
     /* "stats g" responses */
+    dict_insert(irc_func_dict, "230", cmd_dummy); /* ignore stats headers */
     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" */
@@ -2345,8 +2749,9 @@ init_parse(void)
     memset(notice_funcs, 0, sizeof(privmsg_func_t)*num_notice_funcs);
 
     userList_init(&dead_users);
-    reg_del_channel_func(remove_unbursted_channel);
-    reg_exit_func(parse_cleanup);
+    reg_del_channel_func(remove_unbursted_channel, NULL);
+    reg_exit_func(parse_cleanup, NULL);
+    // reg_notice_func(opserv, check_ctcp);
 }
 
 int
@@ -2473,7 +2878,7 @@ make_numeric(struct server *svr, int local_num, char *outbuf)
 }
 
 struct server *
-AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t link, const char *numeric, const char *description)
+AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t link_time, const char *numeric, const char *description)
 {
     struct server* sNode;
     int slen, mlen;
@@ -2505,7 +2910,7 @@ AddServer(struct server *uplink, const char *name, int hops, time_t boot, time_t
     sNode->num_mask = base64toint(numeric+slen, mlen);
     sNode->hops = hops;
     sNode->boot = boot;
-    sNode->link = link;
+    sNode->link_time = link_time;
     strncpy(sNode->numeric, numeric, slen);
     safestrncpy(sNode->description, description, sizeof(sNode->description));
     sNode->users = calloc(sNode->num_mask+1, sizeof(*sNode->users));
@@ -2556,13 +2961,16 @@ void DelServer(struct server* serv, int announce, const char *message)
 }
 
 struct userNode *
-AddService(const char *nick, const char *modes, const char *desc, const char *hostname)
+AddLocalUser(const char *nick, const char *ident, const char *hostname, const char *desc, const char *modes)
 {
     char numeric[COMBO_NUMERIC_LEN+1];
     int local_num = get_local_numeric();
     time_t timestamp = now;
     struct userNode *old_user = GetUserH(nick);
 
+    if (!modes)
+        modes = "+oik";
+
     if (old_user) {
         if (IsLocal(old_user))
             return old_user;
@@ -2575,8 +2983,7 @@ AddService(const char *nick, const char *modes, const char *desc, const char *ho
     if (!hostname)
         hostname = self->name;
     make_numeric(self, local_num, numeric);
-    /* TODO: Make these modes part of the conf file */
-    return AddUser(self, nick, nick, hostname, modes ? modes : "+oik", numeric, desc, now, "AAAAAA");
+    return AddUser(self, nick, ident, hostname, modes, numeric, desc, now, "AAAAAA");
 }
 
 struct userNode *
@@ -2619,36 +3026,47 @@ 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)
 {
     struct userNode *oldUser, *uNode;
-    unsigned int n, ignore_user;
+    unsigned int ignore_user, dummy;
+    char *tstr;
+    int type;
 
     if ((strlen(numeric) < 3) || (strlen(numeric) > 5)) {
-        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): numeric %s wrong length!", uplink, nick, numeric);
+        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): numeric %s wrong length!", (void*)uplink, nick, numeric);
         return NULL;
     }
 
     if (!uplink) {
-        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s doesn't exist!", uplink, nick, numeric);
+        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s doesn't exist!", (void*)uplink, nick, numeric);
         return NULL;
     }
 
     if (uplink != GetServerN(numeric)) {
-        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s differs from nominal uplink %s.", uplink, nick, numeric, uplink->name);
+        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): server for numeric %s differs from nominal uplink %s.", (void*)uplink, nick, numeric, uplink->name);
         return NULL;
     }
 
-    if (!is_valid_nick(nick)) {
-        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", uplink, nick);
+    dummy = modes && modes[0] == '*';
+    if (dummy) {
+        ++modes;
+    } else if (!is_valid_nick(nick)) {
+        log_module(MAIN_LOG, LOG_WARNING, "AddUser(%p, %s, ...): invalid nickname detected.", (void*)uplink, nick);
         return NULL;
     }
 
     ignore_user = 0;
     if ((oldUser = GetUserH(nick))) {
-        if (IsLocal(oldUser) && (IsService(oldUser) || IsPersistent(oldUser))) {
-            /* The service should collide the new user off. */
+        if (IsLocal(oldUser)
+            && (IsService(oldUser) || IsPersistent(oldUser))) {
+            /* The service should collide the new user off - but not
+             * if the new user is coming in during a burst.  (During a
+             * burst, the bursting server will kill either our user --
+             * triggering a ReintroduceUser() -- or its own.)
+             */
             oldUser->timestamp = timestamp - 1;
-            irc_user(oldUser);
-        }
-        if (oldUser->timestamp > timestamp) {
+            ignore_user = 1;
+            if (!uplink->burst)
+                irc_user(oldUser);
+        } else if (oldUser->timestamp > timestamp) {
             /* "Old" user is really newer; remove them */
             DelUser(oldUser, 0, 1, "Overruled by older nick");
         } else {
@@ -2668,13 +3086,20 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
     irc_p10_pton(&uNode->ip, realip);
 
-    if (irc_in_addr_is_ipv4(uNode->ip)) {
-      make_virtip((char*)irc_ntoa(&uNode->ip), (char*)irc_ntoa(&uNode->ip), uNode->cryptip);
-      make_virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
-    } else if (irc_in_addr_is_ipv6(uNode->ip)) {
-      make_ipv6virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
+    tstr = conf_get_data("server/type", RECDB_QSTRING);
+    type = atoi(tstr);
+    if (type == 7) {
+      if (irc_in_addr_is_ipv4(uNode->ip)) {
+        make_virtip((char*)irc_ntoa(&uNode->ip), (char*)irc_ntoa(&uNode->ip), uNode->cryptip);
+        make_virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
+      } else if (irc_in_addr_is_ipv6(uNode->ip)) {
+        make_ipv6virthost((char*)irc_ntoa(&uNode->ip), uNode->hostname, uNode->crypthost);
+      }
     }
 
+    if (!uNode->crypthost && uNode->cryptip)
+        snprintf(uNode->crypthost, sizeof(uNode->crypthost), "%s", strdup(uNode->cryptip));
+    uNode->idle_since = timestamp;
     uNode->timestamp = timestamp;
     modeList_init(&uNode->channels);
     uNode->uplink = uplink;
@@ -2684,6 +3109,8 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     uNode->num_local = base64toint(numeric+strlen(uNode->uplink->numeric), 3) & uNode->uplink->num_mask;
     uNode->uplink->users[uNode->num_local] = uNode;
     mod_usermode(uNode, modes);
+    if (dummy)
+        uNode->modes |= FLAGS_DUMMY;
 
     set_geoip_info(uNode);
 
@@ -2697,9 +3124,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     }
     if (IsLocal(uNode))
         irc_user(uNode);
-    for (n=0; n<nuf_used; n++)
-        if (nuf_list[n](uNode))
-            break;
+    call_new_user_funcs(uNode);
 
     if ((uNode->loc == 1) && (uNode->handle_info))
         send_func_list(uNode);
@@ -2711,7 +3136,6 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
 void
 DelUser(struct userNode* user, struct userNode *killer, int announce, const char *why)
 {
-    unsigned int n;
 
     verify(user);
 
@@ -2723,12 +3147,11 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
 
     /* remove user from all channels */
     while (user->channels.used > 0)
-        DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, false, 0);
+        DelChannelUser(user, user->channels.list[user->channels.used-1]->channel, NULL, 0);
 
     /* Call these in reverse order so ChanServ can update presence
        information before NickServ nukes the handle_info. */
-    for (n = duf_used; n > 0; )
-        duf_list[--n](user, killer, why);
+    call_del_user_funcs(user, killer, why);
 
     user->uplink->clients--;
     user->uplink->users[user->num_local] = NULL;
@@ -2748,7 +3171,40 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
             irc_kill(killer, user, why);
     }
 
+    if (IsLocal(user)) {
+        unsigned int num = user->num_local;
+        if (num < num_privmsg_funcs)
+            privmsg_funcs[num] = NULL;
+        if (num < num_notice_funcs)
+            notice_funcs[num] = NULL;
+    }
+
     modeList_clean(&user->channels);
+
+    /* Clean up version data */
+    if(user->version_reply) {
+        free(user->version_reply);
+        user->version_reply = NULL;
+    }
+
+    /* Clean up SSL fingerprint data */
+    if(user->sslfp) {
+        free(user->sslfp);
+        user->sslfp = NULL;
+    }
+
+    /* clean up mark */
+    if(user->mark) {
+        free(user->mark);
+        user->mark = NULL;
+    }
+
+    /* clean up geoip data if any */
+    if(user->country_code) free(user->country_code);
+    if(user->city) free(user->city);
+    if(user->region) free(user->region);
+    if(user->postal_code) free(user->postal_code);
+
     /* We don't free them, in case we try to privmsg them or something
      * (like when a stupid oper kills themself).  We just put them onto
      * a list of clients that get freed after processing each line.
@@ -2779,15 +3235,13 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
        case '+': add = 1; break;
        case '-': add = 0; break;
        case 'o':
-           if (add) {
-                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);
+            if (!add) {
+                userList_remove(&curr_opers, user);
+            } else if (!userList_contains(&curr_opers, user)) {
+                userList_append(&curr_opers, user);
+                call_oper_funcs(user);
+           }
            break;
        case 'O': do_user_mode(FLAGS_LOCOP); break;
        case 'i': do_user_mode(FLAGS_INVISIBLE);
@@ -2797,10 +3251,37 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
                 invis_clients--;
            break;
        case 'w': do_user_mode(FLAGS_WALLOP); break;
-       case 's': do_user_mode(FLAGS_SERVNOTICE); break;
        case 'd': do_user_mode(FLAGS_DEAF); break;
        case 'k': do_user_mode(FLAGS_SERVICE); break;
        case 'g': do_user_mode(FLAGS_GLOBAL); break;
+       case 'B': do_user_mode(FLAGS_BOT); break;
+       case 'n': do_user_mode(FLAGS_HIDECHANS); break;
+       case 'I': do_user_mode(FLAGS_HIDEIDLE); break;
+       case 'X': do_user_mode(FLAGS_XTRAOP); break;
+       case 'C': do_user_mode(FLAGS_CLOAKHOST);
+           if (*word) {
+               char cloakhost[MAXLEN];
+               unsigned int ii;
+               for (ii=0; (*word != ' ') && (*word != '\0'); )
+                   cloakhost[ii++] = *word++;
+               cloakhost[ii] = 0;
+               while (*word == ' ')
+                   word++;
+               safestrncpy(user->crypthost, cloakhost, sizeof(user->crypthost));
+           }
+           break;
+       case 'c': do_user_mode(FLAGS_CLOAKIP);
+           if (*word) {
+               char cloakip[MAXLEN];
+               unsigned int ii;
+               for (ii=0; (*word != ' ') && (*word != '\0'); )
+                   cloakip[ii++] = *word++;
+               cloakip[ii] = 0;
+               while (*word == ' ')
+                   word++;
+               safestrncpy(user->cryptip, cloakip, sizeof(user->cryptip));
+           }
+           break;
        // sethost - reed/apples
        // case 'h': do_user_mode(FLAGS_HELPER); break;
        // I check if there's an 'h' in the first part, and if there, 
@@ -2847,6 +3328,26 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
     }
 }
 
+static int
+keyncpy(char output[], char input[], size_t output_size)
+{
+    size_t ii;
+
+    if (input[0] == ':')
+    {
+        output[0] = '\0';
+        return 1;
+    }
+
+    for (ii = 0; (ii + 1 < output_size) && (input[ii] != '\0'); ++ii)
+    {
+        output[ii] = input[ii];
+    }
+
+    output[ii] = '\0';
+    return 0;
+}
+
 struct mod_chanmode *
 mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
 {
@@ -2885,14 +3386,15 @@ 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;
+        case 'a': do_chan_mode(MODE_ADMINSONLY); 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);
+              do_chan_mode(MODE_REGISTERED);
          } else {
-          mod_chanmode_free(change);
-          return NULL;
+              mod_chanmode_free(change);
+              return NULL;
          }
          break;
 #undef do_chan_mode
@@ -2909,10 +3411,10 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             break;
         case 'k':
             if (add) {
-                if (in_arg >= argc)
+                if ((in_arg >= argc)
+                    || keyncpy(change->new_key, modes[in_arg++], sizeof(change->new_key)))
                     goto error;
                 change->modes_set |= MODE_KEY;
-                safestrncpy(change->new_key, modes[in_arg++], sizeof(change->new_key));
             } else {
                 change->modes_clear |= MODE_KEY;
                 if (!(flags & MCP_KEY_FREE)) {
@@ -2925,10 +3427,10 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'U':
             if (add)
             {
-              if (in_arg >= argc)
-                  goto error;
-              change->modes_set |= MODE_UPASS;
-              safestrncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass));
+                if ((in_arg >= argc)
+                    || keyncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass)))
+                    goto error;
+                change->modes_set |= MODE_UPASS;
             } else {
                 change->modes_clear |= MODE_UPASS;
                 if (!(flags & MCP_UPASS_FREE)) {
@@ -2940,10 +3442,9 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             break;
         case 'A':
             if (add) {
-                if (in_arg >= argc)
-                    goto error;
+                if ((in_arg >= argc)
+                    || keyncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass)))
                 change->modes_set |= MODE_APASS;
-                safestrncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass));
             } else {
                 change->modes_clear |= MODE_APASS;
                 if (!(flags & MCP_APASS_FREE)) {
@@ -3127,7 +3628,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             mod_chanmode_append(&chbuf, 'k', channel->key);
         if (change->modes_clear & channel->modes & MODE_UPASS)
             mod_chanmode_append(&chbuf, 'U', channel->upass);
-        if (change->modes_clear * channel->modes & MODE_APASS)
+        if (change->modes_clear & channel->modes & MODE_APASS)
             mod_chanmode_append(&chbuf, 'A', channel->apass);
     }
     for (arg = 0; arg < change->argc; ++arg) {
@@ -3338,61 +3839,62 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
 static int
 clear_chanmode(struct chanNode *channel, const char *modes)
 {
-    unsigned int remove;
+    unsigned int cleared;
 
-    for (remove = 0; *modes; modes++) {
+    for (cleared = 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 'm': remove |= MODE_MODERATED; break;
-        case 't': remove |= MODE_TOPICLIMIT; break;
-        case 'i': remove |= MODE_INVITEONLY; break;
-        case 'n': remove |= MODE_NOPRIVMSGS; break;
+        case 'o': cleared |= MODE_CHANOP; break;
+        case 'h': cleared |= MODE_HALFOP; break;
+        case 'v': cleared |= MODE_VOICE; break;
+        case 'p': cleared |= MODE_PRIVATE; break;
+        case 's': cleared |= MODE_SECRET; break;
+        case 'm': cleared |= MODE_MODERATED; break;
+        case 't': cleared |= MODE_TOPICLIMIT; break;
+        case 'i': cleared |= MODE_INVITEONLY; break;
+        case 'n': cleared |= MODE_NOPRIVMSGS; break;
         case 'k':
-            remove |= MODE_KEY;
+            cleared |= MODE_KEY;
             channel->key[0] = '\0';
             break;
         case 'A':
-            remove |= MODE_APASS;
+            cleared |= MODE_APASS;
             channel->apass[0] = '\0';
             break;
         case 'U':
-            remove |= MODE_UPASS;
+            cleared |= MODE_UPASS;
             channel->upass[0] = '\0';
             break;
         case 'l':
-            remove |= MODE_LIMIT;
+            cleared |= MODE_LIMIT;
             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; break;
-        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;
-        case 'z': remove |= MODE_REGISTERED; break;
-        case 'Z': remove |= MODE_SSLONLY; break;
-       case 'L': remove |= MODE_HIDEMODE; break;
+        case 'b': cleared |= MODE_BAN; break;
+        case 'e': cleared |= MODE_EXEMPT; break;
+        case 'D': cleared |= MODE_DELAYJOINS; break;
+        case 'r': cleared |= MODE_REGONLY; break;
+        case 'c': cleared |= MODE_NOCOLORS; break;
+        case 'C': cleared |= MODE_NOCTCPS; break;
+        case 'S': cleared |= MODE_STRIPCOLOR; break;
+        case 'M': cleared |= MODE_MODUNREG; break;
+        case 'N': cleared |= MODE_NONOTICE; break;
+        case 'Q': cleared |= MODE_NOQUITMSGS; break;
+        case 'T': cleared |= MODE_NOAMSG; break;
+        case 'O': cleared |= MODE_OPERSONLY; break;
+        case 'a': cleared |= MODE_ADMINSONLY; break;
+        case 'z': cleared |= MODE_REGISTERED; break;
+        case 'Z': cleared |= MODE_SSLONLY; break;
+       case 'L': cleared |= MODE_HIDEMODE; break;
         }
     }
 
-    if (!remove)
+    if (!cleared)
         return 1;
 
     /* Remove simple modes. */
-    channel->modes &= ~remove;
+    channel->modes &= ~cleared;
 
     /* If removing bans, kill 'em all. */
-    if ((remove & MODE_BAN) && channel->banlist.used) {
+    if ((cleared & MODE_BAN) && channel->banlist.used) {
         unsigned int i;
         for (i=0; i<channel->banlist.used; i++)
             free(channel->banlist.list[i]);
@@ -3400,16 +3902,16 @@ clear_chanmode(struct chanNode *channel, const char *modes)
     }
 
     /* If removing exempts, kill 'em all. */
-    if ((remove & MODE_EXEMPT) && channel->exemptlist.used) {
+    if ((cleared & 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));
+    /* Removed member modes. */
+    if ((cleared & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) && channel->members.used) {
+        int mask = ~(cleared & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));
         unsigned int i;
 
         for (i = 0; i < channel->members.used; i++)
@@ -3424,9 +3926,10 @@ reg_privmsg_func(struct userNode *user, privmsg_func_t handler)
 {
     unsigned int numeric = user->num_local;
     if (numeric >= num_privmsg_funcs) {
-        int newnum = numeric + 8;
+        int newnum = numeric + 8, ii;
         privmsg_funcs = realloc(privmsg_funcs, newnum*sizeof(privmsg_func_t));
-        memset(privmsg_funcs+num_privmsg_funcs, 0, (newnum-num_privmsg_funcs)*sizeof(privmsg_func_t));
+        for (ii = num_privmsg_funcs; ii < newnum; ++ii)
+            privmsg_funcs[ii] = NULL;
         num_privmsg_funcs = newnum;
     }
     if (privmsg_funcs[numeric])
@@ -3435,20 +3938,12 @@ reg_privmsg_func(struct userNode *user, privmsg_func_t handler)
 }
 
 void
-unreg_privmsg_func(struct userNode *user, privmsg_func_t handler)
+unreg_privmsg_func(struct userNode *user)
 {
-    unsigned int x;
-
-    if (!user || handler)
-      return; /* this really only works with users */
+    if (!IsLocal(user) || user->num_local >= num_privmsg_funcs)
+        return; /* this really only works with users */
 
-    memset(privmsg_funcs+user->num_local, 0, sizeof(privmsg_func_t));
-
-    for (x = user->num_local+1; x < num_privmsg_funcs; x++) 
-       memmove(privmsg_funcs+x-1, privmsg_funcs+x, sizeof(privmsg_func_t));
-    
-    privmsg_funcs = realloc(privmsg_funcs, num_privmsg_funcs*sizeof(privmsg_func_t)); 
-    num_privmsg_funcs--;
+    privmsg_funcs[user->num_local] = NULL;
 }
 
 
@@ -3457,9 +3952,10 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler)
 {
     unsigned int numeric = user->num_local;
     if (numeric >= num_notice_funcs) {
-        int newnum = numeric + 8;
+        int newnum = numeric + 8, ii;
         notice_funcs = realloc(notice_funcs, newnum*sizeof(privmsg_func_t));
-        memset(notice_funcs+num_notice_funcs, 0, (newnum-num_notice_funcs)*sizeof(privmsg_func_t));
+        for (ii = num_privmsg_funcs; ii < newnum; ++ii)
+            privmsg_funcs[ii] = NULL;
         num_notice_funcs = newnum;
     }
     if (notice_funcs[numeric])
@@ -3468,46 +3964,12 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler)
 }
 
 void
-unreg_notice_func(struct userNode *user, privmsg_func_t handler)
+unreg_notice_func(struct userNode *user)
 {
-    unsigned int x;
-
-    if (!user || handler)
-          return; /* this really only works with users */
-
-    memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
-
-    for (x = user->num_local+1; x < num_notice_funcs; x++)
-       memmove(notice_funcs+x-1, notice_funcs+x, sizeof(privmsg_func_t));
-
-    memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
-    notice_funcs = realloc(notice_funcs, num_notice_funcs*sizeof(privmsg_func_t));
-    num_notice_funcs--;
-}
+    if (!IsLocal(user) || user->num_local >= num_privmsg_funcs)
+        return; /* this really only works with users */
 
-void
-reg_oper_func(oper_func_t handler)
-{
-    if (of_used == of_size) {
-       if (of_size) {
-           of_size <<= 1;
-           of_list = realloc(of_list, of_size*sizeof(oper_func_t));
-       } else {
-           of_size = 8;
-           of_list = malloc(of_size*sizeof(oper_func_t));
-       }
-    }
-    of_list[of_used++] = handler;
-}
-
-static void
-call_oper_funcs(struct userNode *user)
-{
-    unsigned int n;
-    if (IsLocal(user))
-        return;
-    for (n=0; n<of_used; n++)
-       of_list[n](user);
+    notice_funcs[user->num_local] = NULL;
 }
 
 static void