]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
Added basic support to X3 for host hiding style 3
[irc/evilnet/x3.git] / src / proto-p10.c
index abda5ccaa368545dd686f520a3e1781910c592cb..f3cd0f9bf568dd8fb552ea1d0d4e0100715fd7c1 100644 (file)
 #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_RPONG               "RPONG"
+#define CMD_SASL                "SASL"
 #define CMD_SERVER              "SERVER"
 #define CMD_SERVLIST            "SERVLIST"
 #define CMD_SERVSET             "SERVSET"
@@ -95,6 +97,7 @@
 #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_WHO                 "WHO"
 #define CMD_WHOIS               "WHOIS"
 #define CMD_WHOWAS              "WHOWAS"
+#define CMD_ZLINE              "ZLINE"
 
 /* Tokenized commands. */
 #define TOK_ACCOUNT            "AC"
 #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_RPONG               "RO"
+#define TOK_SASL                "SASL"
 #define TOK_SERVER              "S"
 #define TOK_SERVLIST            "SERVSET"
 #define TOK_SERVSET             "SERVSET"
 #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_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_RPONG               TYPE(RPONG)
+#define P10_SASL                TYPE(SASL)
 #define P10_SERVER              CMD_SERVER
 #define P10_SERVLIST            TYPE(SERVLIST)
 #define P10_SERVSET             TYPE(SERVSET)
 #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_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
@@ -340,6 +352,9 @@ static const char *his_servername;
 static const char *his_servercomment;
 static int extended_accounts;
 
+/* These correspond to 1 << X:      01234567890123456789012345 */
+const char irc_user_mode_chars[] = "oOiw dkgh    x  BnIX  azDRWHLq";
+
 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;
@@ -484,10 +499,10 @@ 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 " FMT_TIME_T " " FMT_TIME_T " J10 %s%s +s6 :%s",
+        putsock(P10_SERVER " %s %d " FMT_TIME_T " " FMT_TIME_T " J10 %s%s +s6o :%s",
                 srv->name, srv->hops+1, srv->boot, srv->link_time, srv->numeric, extranum, srv->description);
     } else {
-        putsock("%s " P10_SERVER " %s %d " FMT_TIME_T " " FMT_TIME_T " %c10 %s%s +s6 :%s",
+        putsock("%s " P10_SERVER " %s %d " FMT_TIME_T " " FMT_TIME_T " %c10 %s%s +s6o :%s",
                 self->numeric, srv->name, srv->hops+1, srv->boot, srv->link_time, (srv->self_burst ? 'J' : 'P'), srv->numeric, extranum, srv->description);
     }
 }
@@ -508,7 +523,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 {
@@ -575,41 +590,9 @@ irc_user(struct userNode *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 (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';
-        if (IsBotM(user))
-            modes[modelen++] = 'B';
-        if (IsHideChans(user))
-            modes[modelen++] = 'n';
-        if (IsHideIdle(user))
-            modes[modelen++] = 'I';
-        if (IsXtraOp(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 " 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);
@@ -847,23 +830,15 @@ irc_introduce(const char *passwd)
 void
 irc_gline(struct server *srv, struct gline *gline, int silent)
 {
-    if (gline->lastmod)
-        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, gline->lastmod, silent ? "AUTO " : "", gline->issuer, gline->reason);
-    else
-        putsock("%s " P10_GLINE " %s +%s " FMT_TIME_T " :%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)
 {
-    if (shun->lastmod)
-        putsock("%s " P10_SHUN " %s +%s %ld %ld :%s",
-                self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->lastmod, shun->reason);
-    else
-        putsock("%s " P10_SHUN " %s +%s %ld :%s",
-                self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->reason);
+    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);
 }
 
 void
@@ -1073,7 +1048,7 @@ irc_svspart(struct userNode *from, struct userNode *who, struct chanNode *to)
 }
 
 void
-irc_svsquit(struct userNode *from, struct userNode *who, char *reason)
+irc_svsquit(struct userNode *from, struct userNode *who, char const *reason)
 {
     putsock("%s " P10_SVSQUIT " %s :%s", from->uplink->numeric, who->numeric, reason);
 }
@@ -1122,23 +1097,27 @@ void
 irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
 {
 
-   int type = 4, host_in_topic = 0, hasident = 0;
-   const char *hstr, *tstr;
+   int type = 4, host_in_topic = 0, hasident = 0, hhtype = 0;
+   const char *hstr, *tstr, *hhstr, *htstr;
    char *host, *hostmask;
    char shost[MAXLEN];
    char sident[MAXLEN];
 
    tstr = conf_get_data("server/type", RECDB_QSTRING);
    hstr = conf_get_data("server/host_in_topic", RECDB_QSTRING);
+   hhstr = conf_get_data("server/hidden_host", RECDB_QSTRING);
+   htstr = conf_get_data("server/hidden_host_type", RECDB_QSTRING);
    if(tstr)
      type = atoi(tstr);
    else
      type = 4;/* default to 040 style topics */
+   if (htstr)
+     hhtype = atoi(htstr);
 
    if (hstr) {
-      if (IsFakeHost(who))
+      if (IsHiddenHost(who) && IsFakeHost(who))
           safestrncpy(shost, who->fakehost, sizeof(shost));
-      else if (IsSetHost(who)) {
+      else if (IsHiddenHost(who) && IsSetHost(who)) {
           hostmask = strdup(who->sethost);
           if ((host = (strrchr(hostmask, '@')))) {
               hasident = 1;
@@ -1152,6 +1131,10 @@ irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what,
               safestrncpy(sident, who->ident, sizeof(shost));
 
           safestrncpy(shost, host, sizeof(shost));
+      } else if (IsHiddenHost(who) && ((hhtype == 1) || (hhtype == 3)) && who->handle_info && hhstr) {
+          snprintf(shost, sizeof(shost), "%s.%s", who->handle_info->handle, hhstr);
+      } else if (IsHiddenHost(who) && ((hhtype == 2) || (hhtype == 3)) && who->crypthost[0]) {
+          safestrncpy(shost, who->crypthost, sizeof(shost));
       } else
           safestrncpy(shost, who->hostname, sizeof(shost));
 
@@ -1236,6 +1219,12 @@ void irc_sno(unsigned int mask, char const* format, ...) {
     putsock("%s " CMD_SNO " %d :%s", self->numeric, mask, buffer);
 }
 
+void
+irc_sasl(struct server* dest, const char *identifier, const char *subcmd, const char *data)
+{
+    putsock("%s " P10_SASL " %s %s %s %s", self->numeric, dest->numeric, identifier, subcmd, data);
+}
+
 static void send_burst(void);
 
 static void
@@ -1281,7 +1270,7 @@ static CMD_FUNC(cmd_whois)
     else
         irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
 
-    if (IsService(who) || (from == who)) {
+    if (!IsService(who) || (from == who)) {
         struct modeNode *mn;
         mlen = strlen(self->name) + strlen(from->nick) + 12 + strlen(who->nick);
         len = 0;
@@ -1320,7 +1309,6 @@ static CMD_FUNC(cmd_whois)
         }
         if (buf[0] != '\0')
             irc_numeric(from, RPL_WHOISCHANNELS, "%s :%s", who->nick, buf);
-        return 1;
     }
 
     if (his_servername && his_servercomment && !IsOper(from) && from != who)
@@ -1401,7 +1389,6 @@ static CMD_FUNC(cmd_eob)
 {
     struct server *sender;
     dict_iterator_t it;
-    unsigned int ii;
 
     if (!(sender = GetServerH(origin)))
         return 0;
@@ -1422,8 +1409,7 @@ static CMD_FUNC(cmd_eob)
     }
     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_ */
@@ -1457,6 +1443,22 @@ static CMD_FUNC(cmd_rping)
     return 1;
 }
 
+static CMD_FUNC(cmd_sasl)
+{
+    struct server *serv;
+
+    if (argc < 5)
+        return 0;
+
+    serv = GetServerH(origin);
+    if (!serv)
+      return 0;
+
+    call_sasl_input_func(serv, argv[2], argv[3], argv[4], (argc>5 ? argv[5] : NULL));
+
+    return 1;
+}
+
 static CMD_FUNC(cmd_ping)
 {
     struct server *srv;
@@ -1614,7 +1616,7 @@ static CMD_FUNC(cmd_account)
     
     if(!strcmp(argv[2],"C"))
     {
-        if((hi = loc_auth(argv[4], argv[5], NULL)))
+        if((hi = loc_auth(NULL, argv[4], argv[5], NULL)))
         {
             /* Return a AC A */
             putsock("%s " P10_ACCOUNT " %s A %s "FMT_TIME_T, self->numeric, server->numeric , argv[3], hi->registered);
@@ -1630,7 +1632,21 @@ static CMD_FUNC(cmd_account)
     }
     else if(!strcmp(argv[2],"H")) /* New enhanced (host) version of C */
     {
-        if((hi = loc_auth(argv[5], argv[6], argv[4] )))
+        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);
@@ -1745,6 +1761,12 @@ 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;
@@ -1788,7 +1810,8 @@ static CMD_FUNC(cmd_privs)
             for (tmp = x3_strtok(&p, argv[i], ","); tmp;
                  tmp = x3_strtok(&p, NULL, ",")) {
                 if (!strcmp(tmp, "PRIV_NONE")) {
-                    clear_privs(user);
+                    if (now > user->timestamp+5)
+                        clear_privs(user);
                     break;
                 } else
                     client_modify_priv_by_name(user, tmp, what);
@@ -1811,7 +1834,9 @@ static CMD_FUNC(cmd_burst)
     struct modeNode *mNode;
     long mode;
     int oplevel = -1;
+    int type = 0;
     char *user, *end, sep;
+    char *tstr;
     time_t in_timestamp;
     char* parm = NULL;
 
@@ -1819,6 +1844,11 @@ static CMD_FUNC(cmd_burst)
         return 0;
     modes[0] = 0;
 
+    tstr = conf_get_data("server/type", RECDB_QSTRING);
+    if(tstr) {
+        type = atoi(tstr);
+    }
+
     exemptlist[0] = 0;
     banlist[0] = 0;
 
@@ -1829,7 +1859,7 @@ static CMD_FUNC(cmd_burst)
             int n_modes;
             for (pos=argv[next], n_modes = 1; *pos; pos++)
                 if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A')
-                    || (*pos == 'U'))
+                    || (*pos == 'U') || ((type > 7) && (*pos == 'L')))
                     n_modes++;
             if (next + n_modes > argc)
                 n_modes = argc - next;
@@ -1838,24 +1868,14 @@ static CMD_FUNC(cmd_burst)
             break;
         }
         case '%': {
+            ctype = 1;
             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 (0 == strcmp("~", parm))
+                ctype = 2;
               if (ctype == 1) {
                 if (bcheck == 0) {
                   /* strip % char off start of very first ban */
@@ -1972,7 +1992,7 @@ static CMD_FUNC(cmd_mark)
         
     }
     else if(!strcasecmp(argv[2], "CVERSION")) {
-        /* DNSBL_DATA name */
+        /* 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]);
@@ -1990,6 +2010,22 @@ static CMD_FUNC(cmd_mark)
 
         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;
 }
@@ -2179,37 +2215,28 @@ static CMD_FUNC(cmd_num_topic)
 
 static CMD_FUNC(cmd_num_gline)
 {
-    time_t lastmod;
     if (argc < 7) {
         if (argc < 6)
             return 0;
-        else {
-            lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0;
-            gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, lastmod, 0, 0);
-        }
+        else
+            gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
     } else {
-        if (!strcmp(argv[5], "+")) {
-          lastmod = (argc > 6) ? strtoul(argv[5], NULL, 0) : 0;
-          gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, now, 0, 0);
-        }
+        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)
 {
-    time_t lastmod;
-
     if (argc < 7) {
         if (argc < 6)
             return 0;
         else
-            lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0;
-            shun_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, now, 0);
+            shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
     } else {
         if (!strcmp(argv[5], "+"))
-            lastmod = (argc > 6) ? strtoul(argv[5], NULL, 0) : 0;
-            shun_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, now, 0);
+            shun_add(origin, argv[3], atoi(argv[4])-now, argv[6], now, 0);
     }
     return 1;
 }
@@ -2237,9 +2264,11 @@ static CMD_FUNC(cmd_kill)
          * Ghost response to a KILL we sent out earlier.  So we only
          * whine if the target is local.
          */
-        if (!strncmp(argv[1], self->numeric, strlen(self->numeric)))
+        if (!strncmp(argv[1], self->numeric, strlen(self->numeric))) {
             log_module(MAIN_LOG, LOG_ERROR, "Unable to find kill victim %s", argv[1]);
-        return 0;
+            return 0;
+        }
+        return 1;
     }
 
     if (IsLocal(user) && IsService(user)) {
@@ -2433,15 +2462,12 @@ static CMD_FUNC(cmd_away)
 
 static CMD_FUNC(cmd_gline)
 {
-    time_t lastmod;
-
     if (argc < 3)
         return 0;
     if (argv[2][0] == '+') {
         if (argc < 5)
             return 0;
-        lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0;
-        gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, lastmod, 0, 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);
@@ -2460,7 +2486,7 @@ static CMD_FUNC(cmd_sgline)
     if (!(sender = GetServerH(origin)))
         return 0;
 
-    gline_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, now, 1, 0);
+    gline_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, 1, 0);
     return 1;
 }
 
@@ -2474,21 +2500,18 @@ static CMD_FUNC(cmd_sshun)
     if (!(sender = GetServerH(origin)))
         return 0;
 
-    shun_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, now, 1);
+    shun_add(origin, argv[1], strtoul(argv[2], NULL, 0), argv[argc-1], now, 1);
     return 1;
 }
 
 static CMD_FUNC(cmd_shun)
 {
-    time_t lastmod;
-
     if (argc < 3)
         return 0;
     if (argv[2][0] == '+') {
         if (argc < 5)
             return 0;
-        lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0;
-        shun_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, now, 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);
@@ -2517,10 +2540,11 @@ 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);
@@ -2540,7 +2564,7 @@ p10_conf_reload(void) {
 }
 
 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);
 }
@@ -2674,6 +2698,9 @@ init_parse(void)
     dict_insert(irc_func_dict, CMD_RPONG, cmd_dummy);
     dict_insert(irc_func_dict, TOK_RPONG, cmd_dummy);
 
+    dict_insert(irc_func_dict, CMD_SASL, cmd_sasl);
+    dict_insert(irc_func_dict, TOK_SASL, cmd_sasl);
+
     /* In P10, DESTRUCT doesn't do anything except be broadcast to servers.
      * Apparently to obliterate channels from any servers that think they
      * exist?
@@ -2706,11 +2733,19 @@ init_parse(void)
     /* 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);
@@ -2749,8 +2784,8 @@ 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);
 }
 
@@ -3026,7 +3061,7 @@ 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, dummy;
+    unsigned int ignore_user, dummy;
     char *tstr;
     int type;
 
@@ -3088,7 +3123,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
 
     tstr = conf_get_data("server/type", RECDB_QSTRING);
     type = atoi(tstr);
-    if (type > 6) {
+    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);
@@ -3124,8 +3159,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) && !uNode->dead; n++)
-        nuf_list[n](uNode);
+    call_new_user_funcs(uNode);
 
     if ((uNode->loc == 1) && (uNode->handle_info))
         send_func_list(uNode);
@@ -3137,7 +3171,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);
 
@@ -3153,8 +3186,7 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
 
     /* 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;
@@ -3190,6 +3222,12 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
         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);
@@ -3320,6 +3358,14 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
                 assign_fakehost(user, host, 0);
             }
             break;
+        case 'a': do_user_mode(FLAGS_ADMIN); break;
+        case 'z': do_user_mode(FLAGS_SSL); break;
+        case 'D': do_user_mode(FLAGS_PRIVDEAF); break;
+        case 'R': do_user_mode(FLAGS_ACCOUNTONLY); break;
+        case 'W': do_user_mode(FLAGS_WHOIS); break;
+        case 'H': do_user_mode(FLAGS_HIDEOPER); break;
+        case 'L': do_user_mode(FLAGS_NOLINK); break;
+        case 'q': do_user_mode(FLAGS_COMMONCHANSONLY); break;
        }
 #undef do_user_mode
     }
@@ -3440,7 +3486,8 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'A':
             if (add) {
                 if ((in_arg >= argc)
-                    || keyncpy(change->new_upass, modes[in_arg++], sizeof(change->new_upass)))
+                    || keyncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass)))
+                    goto error;
                 change->modes_set |= MODE_APASS;
             } else {
                 change->modes_clear |= MODE_APASS;
@@ -3617,6 +3664,7 @@ 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');
+        DO_MODE_CHAR(ADMINSONLY, 'a');
         DO_MODE_CHAR(REGISTERED, 'z');
         DO_MODE_CHAR(SSLONLY, 'Z');
        DO_MODE_CHAR(HIDEMODE, 'L');
@@ -3673,6 +3721,7 @@ 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');
+        DO_MODE_CHAR(ADMINSONLY, 'a');
         DO_MODE_CHAR(REGISTERED, 'z');
         DO_MODE_CHAR(SSLONLY, 'Z');
        DO_MODE_CHAR(HIDEMODE, 'L');
@@ -3748,6 +3797,7 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(NOQUITMSGS, 'Q');
         DO_MODE_CHAR(NOAMSG, 'T');
         DO_MODE_CHAR(OPERSONLY, 'O');
+        DO_MODE_CHAR(ADMINSONLY, 'a');
         DO_MODE_CHAR(REGISTERED, 'z');
         DO_MODE_CHAR(SSLONLY, 'Z');
        DO_MODE_CHAR(HIDEMODE, 'L');
@@ -3772,62 +3822,23 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(NOQUITMSGS, 'Q');
         DO_MODE_CHAR(NOAMSG, 'T');
         DO_MODE_CHAR(OPERSONLY, 'O');
+        DO_MODE_CHAR(ADMINSONLY, 'a');
         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|MODE_APASS|MODE_UPASS)) {
-        /* Doing this implementation has been a pain in the arse, I hope I didn't forget a possible combination */
-        case MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS:
-            used += sprintf(outbuff+used, "lkAU %d %s %s %s", change->new_limit, change->new_key,change->new_apass, change->new_upass);
-            break;
-
-        case MODE_KEY|MODE_LIMIT|MODE_APASS:
-            used += sprintf(outbuff+used, "lkA %d %s %s", change->new_limit, change->new_key, change->new_apass);
-            break;
-        case MODE_KEY|MODE_LIMIT|MODE_UPASS:
-            used += sprintf(outbuff+used, "lkU %d %s %s", change->new_limit, change->new_key, change->new_upass);
-            break;
-        case MODE_KEY|MODE_APASS|MODE_UPASS:
-            used += sprintf(outbuff+used, "kAU %s %s %s", change->new_key, change->new_apass, change->new_upass);
-            break;
 
-        case MODE_KEY|MODE_APASS:
-            used += sprintf(outbuff+used, "kA %s %s", change->new_key, change->new_apass);
-            break;
-        case MODE_KEY|MODE_UPASS:
-            used += sprintf(outbuff+used, "kU %s %s", change->new_key, change->new_upass);
-            break;
-        case MODE_KEY|MODE_LIMIT:
-            used += sprintf(outbuff+used, "lk %d %s", change->new_limit, change->new_key);
-            break;
-
-        case MODE_LIMIT|MODE_UPASS:
-            used += sprintf(outbuff+used, "lU %d %s", change->new_limit, change->new_upass);
-            break;
-        case MODE_LIMIT|MODE_APASS:
-            used += sprintf(outbuff+used, "lA %d %s", change->new_limit, change->new_apass);
-            break;
-        case MODE_APASS|MODE_UPASS:
-            used += sprintf(outbuff+used, "AU %s %s", change->new_apass, change->new_upass);
-
-        case MODE_LIMIT|MODE_APASS|MODE_UPASS:
-            used += sprintf(outbuff+used, "lAU %d %s %s", change->new_limit, change->new_apass, change->new_upass);
-            break;
+       DO_MODE_CHAR(KEY, 'k');
+       DO_MODE_CHAR(LIMIT, 'l');
+       DO_MODE_CHAR(APASS, 'A');
+       DO_MODE_CHAR(UPASS, 'U');
+#undef DO_MODE_CHAR
 
-        case MODE_APASS:
-            used += sprintf(outbuff+used, "A %s", change->new_apass);
-            break;
-        case MODE_UPASS:
-            used += sprintf(outbuff+used, "U %s", change->new_upass);
-            break;
-        case MODE_KEY:
-            used += sprintf(outbuff+used, "k %s", change->new_key);
-            break;
-        case MODE_LIMIT:
-            used += sprintf(outbuff+used, "l %d", change->new_limit);
-            break;
-        }
+#define DO_MODE_PARM(BIT, PARM) if (change->modes_set & MODE_##BIT) used += sprintf(outbuff+used, " %s", PARM);
+       DO_MODE_PARM(KEY, change->new_key);
+       if (change->modes_set & MODE_LIMIT) used += sprintf(outbuff+used, " %d", change->new_limit);
+       DO_MODE_PARM(APASS, change->new_apass);
+       DO_MODE_PARM(UPASS, change->new_upass);
+#undef DO_MODE_PARM
     }
     outbuff[used] = 0;
     return outbuff;