]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
Fixed an issue with OPCHAN and channels using offchannel
[irc/evilnet/x3.git] / src / proto-p10.c
index 119f48768e18ec46b37928fde02e6987c37c2e75..140e2d5632ad5f4f174d405f240df6174492ad26 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,
 
 #include "nickserv.h"
 #include "chanserv.h"
+#include "hash.h"
+#include "helpfile.h"
+#include "hosthiding.h"
 #include "proto-common.c"
+#include "opserv.h"
 
 /* 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"
 #define CMD_SETTIME             "SETTIME"
 #define CMD_SHUN               "SHUN"
 #define CMD_SILENCE             "SILENCE"
+#define CMD_SNO                 "SNO"
 #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_TRACE               "TRACE"
 /* 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_SETTIME             "SE"
 #define TOK_SHUN               "SU"
 #define TOK_SILENCE             "U"
+#define TOK_SNO                 "SNO"
 #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_TRACE               "TR"
 #define P10_SETTIME             TYPE(SETTIME)
 #define P10_SHUN               TYPE(SHUN)
 #define P10_SILENCE             TYPE(SILENCE)
+#define P10_SNO                 TYPE(SNO)
 #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_TRACE               TYPE(TRACE)
  */
 #define PREHISTORY 780000000
 
+#define MODELEN                 40 + KEYLEN
+
 static struct server *servers_num[64*64];
 static privmsg_func_t *privmsg_funcs;
 static unsigned int num_privmsg_funcs;
@@ -311,9 +334,14 @@ static int extended_accounts;
 static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip);
 
 extern int off_channel;
+extern int DefConLevel;
+extern int DefConTimeOut;
+extern char *DefConChanModes;
 
 static int parse_oplevel(char *str);
 
+char privbuf[512] = "";
+
 /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the
  * server and Y's indentifying the client on that server. */
 struct server*
@@ -360,6 +388,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)
 {
@@ -371,11 +425,49 @@ 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);
         }
     }
 }
 
+/* equiv to user doing /connect server port target */
+void irc_connect(struct userNode *user, char *server, unsigned int port, struct server *target)
+{
+    putsock("%s " P10_CONNECT " %s %d %s", user->numeric, server, port, target->numeric);
+}
+
+void
+irc_squit_route(struct server *srv, const char *message, ...)
+{
+    va_list arg_list;
+    char buffer[MAXLEN];
+    va_start(arg_list, message);
+    vsnprintf(buffer, MAXLEN-2, message, arg_list);
+    buffer[MAXLEN-1] = 0;
+
+    /* When would we squit ourselves exactly?? -Rubin */
+    if(srv == self && cManager.uplink->state == CONNECTED ) {
+        unsigned int i;
+
+        /* Quit all clients linked to me. */
+        for(i = 0; i <= self->num_mask; i++) {
+            if(!self->users[i])
+                continue;
+            irc_quit(self->users[i], buffer);
+        }
+    }
+
+    putsock("%s " P10_SQUIT " %s %d :%s", self->numeric, srv->name, 0, buffer);
+
+    if(srv == self) {
+        /* Force a reconnect to the currently selected server. */
+        cManager.uplink->tries = 0;
+        log_module(MAIN_LOG, LOG_INFO, "Squitting from uplink: %s", buffer);
+        close_socket();
+    }
+}
+
 void
 irc_server(struct server *srv)
 {
@@ -391,7 +483,7 @@ irc_server(struct server *srv)
     }
 }
 
-static void
+void
 irc_p10_pton(irc_in_addr_t *ip, const char *input)
 {
     if (strlen(input) == 6) {
@@ -418,7 +510,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)) {
@@ -500,6 +592,15 @@ irc_user(struct userNode *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;
 
         /* we don't need to put the + in modes because it's in the format string. */
@@ -594,6 +695,18 @@ irc_wallchops(struct userNode *from, const char *to, const char *message)
     putsock("%s " P10_WALLCHOPS " %s :%s", from->numeric, to, message);
 }
 
+void
+irc_wallops(const char *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 " P10_WALLOPS " :%s", self->numeric, buffer);
+}
+
+
 void
 irc_notice(struct userNode *from, const char *to, const char *message)
 {
@@ -612,6 +725,18 @@ irc_privmsg(struct userNode *from, const char *to, const char *message)
     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
 irc_eob(void)
 {
@@ -622,6 +747,24 @@ void
 irc_eob_ack(void)
 {
     putsock("%s " P10_EOB_ACK, self->numeric);
+
+    char *nick;
+    const char *str;
+    str = conf_get_data("services/opserv/nick", RECDB_QSTRING);
+    nick = strdup(str);
+
+    if (nick && (DefConLevel < 5)) {
+        DefConProcess(GetUserH(nick));
+
+        if (DefConTimeOut > 0)
+            timeq_add(now + DefConTimeOut, defcon_timeout, NULL);
+    }
+}
+
+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
@@ -672,8 +815,8 @@ 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 %ld %ld :%s<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, now, silent ? "AUTO " : "", gline->issuer, gline->reason);
 }
 
 void
@@ -722,6 +865,8 @@ irc_burst(struct chanNode *chan)
     if (len)
         burst_line[pos++] = ' ';
 
+    if(chan->members.used < 1)
+        return; /* dont burst empty channels (created by discrims) */
     /* dump the users */
     for (n=0; n<chan->members.used; n++) {
         mn = chan->members.list[n];
@@ -731,6 +876,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)) {
@@ -860,7 +1006,7 @@ irc_invite(struct userNode *from, struct userNode *who, struct chanNode *to)
 void
 irc_silence(struct userNode *who, const char *mask, int add)
 {
-    putsock("%s " P10_SILENCE " %s %s%s", self->numeric, who->numeric, add ? "" : "-", mask);
+    putsock("%s " P10_SILENCE " %s %s%s", self->numeric, who->numeric, add ? "+" : "-", mask);
 }
 
 void
@@ -874,6 +1020,24 @@ irc_join(struct userNode *who, struct chanNode *what)
     }
 }
 
+void
+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 *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)
 {
@@ -896,6 +1060,14 @@ irc_svsnick(struct userNode *from, struct userNode *target, const char *newnick)
     putsock("%s " P10_SVSNICK " %s %s "FMT_TIME_T, from->uplink->numeric, target->numeric, newnick, now);
 }
 
+void
+irc_swhois(struct userNode *from, struct userNode *target, const char *message)
+{
+    putsock("%s " P10_SWHOIS " %s %s%s", from->uplink->numeric, target->numeric, message ? ":" : "",
+                                         message ? message : "");
+
+}
+
 void
 irc_part(struct userNode *who, struct chanNode *what, const char *reason)
 {
@@ -911,14 +1083,43 @@ irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what,
 {
 
    int type = 4;
-   const char *str;
-   str = conf_get_data("server/type", RECDB_QSTRING);
-   type = atoi(str);
+   int host_in_topic = 0;
+   const char *hstr, *tstr;
+   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);
+   if(tstr)
+     type = atoi(tstr);
+   else
+     type = 4;/* default to 040 style topics */
+
+   if (hstr) {
+      if (IsFakeHost(who))
+          safestrncpy(shost, who->fakehost, sizeof(shost));
+      else if (IsSetHost(who)) {
+          hostmask = strdup(who->sethost);
+          if ((host = (strrchr(hostmask, '@'))))
+              *host++ = '\0';
+          else
+              host = hostmask;
+
+          safestrncpy(sident, hostmask, sizeof(shost));
+          safestrncpy(shost, host, sizeof(shost));
+      } else
+          safestrncpy(shost, who->hostname, sizeof(shost));
+
+      host_in_topic = atoi(hstr);
+   }
 
-   if (type == 5) {
-     putsock("%s " P10_TOPIC " %s %s " FMT_TIME_T " " FMT_TIME_T " :%s", service->numeric, what->name, who->nick, what->timestamp, now, topic);
+   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);
    } else {
-     who = service; /* REMOVE LINE FOR NEFARIOUS 0.5.0 */
+     who = service;
      putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
    }
 }
@@ -940,6 +1141,48 @@ 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);
+        }
+    }
+}
+
 static void send_burst(void);
 
 static void
@@ -1042,6 +1285,7 @@ static CMD_FUNC(cmd_server)
     return 1;
 }
 
+
 static CMD_FUNC(cmd_eob)
 {
     struct server *sender;
@@ -1058,11 +1302,22 @@ static CMD_FUNC(cmd_eob)
         unbursted_channels = NULL;
         irc_eob();
         irc_eob_ack();
+
+        /* now that we know who our uplink is,
+         * we can center the routing map and activate auto-routing.
+         */
+        //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);
+    /* 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_ */
+    if(sender->uplink != self)
+        routing_handle_connect(sender->name, sender->uplink->name);
     return 1;
 }
 
@@ -1078,6 +1333,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;
@@ -1162,6 +1430,22 @@ static CMD_FUNC(cmd_join)
     return 1;
 }
 
+static CMD_FUNC(cmd_svsjoin)
+{
+    struct create_desc cd;
+
+    if (!(cd.user = GetUserH(argv[1])))
+        return 0;
+    if (argc < 3)
+        return 0;
+    else if (argc < 4)
+        cd.when = now;
+    else
+        cd.when = atoi(argv[3]);
+    parse_foreach(argv[2], join_helper, create_helper, NULL, NULL, &cd);
+    return 1;
+}
+
 static CMD_FUNC(cmd_pong)
 {
     if (argc < 3)
@@ -1185,6 +1469,7 @@ static CMD_FUNC(cmd_nick)
         NickChange(user, argv[1], 1);
     } else {
         struct server *serv;
+        struct userNode *nuser;
         char modes[MAXLEN];
         /* new nick */
         if (argc < 9)
@@ -1194,7 +1479,7 @@ static CMD_FUNC(cmd_nick)
             unsplit_string(argv+6, argc-9, modes);
         else
             strcpy(modes, "+");
-        AddUser(serv, argv[1], argv[4], argv[5], modes, argv[argc-2], argv[argc-1], atoi(argv[3]), argv[argc-3]);
+        nuser = AddUser(serv, argv[1], argv[4], argv[5], modes, argv[argc-2], argv[argc-1], atoi(argv[3]), argv[argc-3]);
     }
     return 1;
 }
@@ -1218,7 +1503,22 @@ static CMD_FUNC(cmd_account)
     
     if(!strcmp(argv[2],"C"))
     {
-        if((hi = loc_auth(argv[4], argv[5])))
+        if((hi = loc_auth(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);
+
+        }
+        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(argv[5], argv[6], argv[4] )))
         {
             /* Return a AC A */
             putsock("%s " P10_ACCOUNT " %s A %s %lu", self->numeric, server->numeric , argv[3], hi->registered);
@@ -1250,6 +1550,119 @@ static CMD_FUNC(cmd_fakehost)
     return 1;
 }
 
+static struct {
+  char        *name;
+  unsigned int priv;
+} privtab[] = {
+#define P(priv)         { #priv, PRIV_ ## priv }
+  P(CHAN_LIMIT),     P(MODE_LCHAN),     P(WALK_LCHAN),    P(DEOP_LCHAN),
+  P(SHOW_INVIS),     P(SHOW_ALL_INVIS), P(UNLIMIT_QUERY), P(KILL),
+  P(LOCAL_KILL),     P(REHASH),         P(RESTART),       P(DIE),
+  P(GLINE),          P(LOCAL_GLINE),    P(JUPE),          P(LOCAL_JUPE),
+  P(OPMODE),         P(LOCAL_OPMODE),   P(SET),           P(WHOX),
+  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(ZLINE),
+  P(LOCAL_ZLINE),    P(WIDE_ZLINE),     P(LIST_CHAN),
+#undef P
+  { 0, 0 }
+};
+
+char *client_report_privs(struct userNode *client)
+{
+  int i;
+
+  privbuf[0] = '\0';
+  for (i = 0; privtab[i].name; i++) {
+    if (HasPriv(client, privtab[i].priv)) {
+      strcat(privbuf, privtab[i].name);
+      strcat(privbuf, " ");
+    }
+  }
+
+  privbuf[strlen(privbuf)] = 0;
+
+  return privbuf;
+}
+
+int client_modify_priv_by_name(struct userNode *who, char *priv, int what) {
+ int i = 0;
+ assert(0 != priv);
+ assert(0 != who);
+
+  for (i = 0; privtab[i].name; i++) {
+    if (0 == strcmp(privtab[i].name, priv)) {
+      if (what == PRIV_ADD)
+        GrantPriv(who, privtab[i].priv);
+      else if (what == PRIV_DEL) {
+        RevokePriv(who, privtab[i].priv);
+      }
+    }
+  }
+  return 0;
+}
+
+int check_priv(char *priv)
+{
+  int i;
+
+  for (i = 0; privtab[i].name; i++) {
+    if (0 == strcmp(privtab[i].name, priv)) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+void
+irc_privs(struct userNode *target, char *flag, int add)
+{
+    client_modify_priv_by_name(target, flag, add);
+    putsock("%s " P10_PRIVS " %s %s%s", self->numeric, target->numeric, (add == PRIV_ADD) ? "+" : "-", flag);
+}
+
+static CMD_FUNC(cmd_privs)
+{
+  char *tstr = NULL;
+  int type = 0;
+
+  tstr = conf_get_data("server/type", RECDB_QSTRING);
+  if(tstr)
+    type = atoi(tstr);
+
+  if (type < 6)
+    return 1; /* silently ignore */
+
+  struct userNode *user = argc > 1 ? GetUserN(argv[1]) : NULL;
+  char buf[512] = "";
+  int what = PRIV_ADD;
+  char *p = 0;
+  char *tmp;
+  unsigned int i;
+
+  if (argc < 3)
+    return 0;
+
+  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, ",")) {
+      client_modify_priv_by_name(user, tmp, what);
+    }
+  }
+  return 1;
+}
+
 static CMD_FUNC(cmd_burst)
 {
     extern int rel_age;
@@ -1257,6 +1670,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;
@@ -1340,6 +1754,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) {
@@ -1383,6 +1806,57 @@ 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")) {
+        /* DNSBL_DATA name */
+        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;
+    }
+    /* unknown type of mark */
+    return 1;
+}
+
 static CMD_FUNC(cmd_mode)
 {
     struct chanNode *cn;
@@ -1428,6 +1902,22 @@ static CMD_FUNC(cmd_mode)
         cn->timestamp = atoi(argv[argc-1]);
     }
 
+    if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(un)) {
+        const char *str;
+        str = conf_get_data("services/opserv/nick", RECDB_QSTRING);
+        if (str) {
+            char modes[MODELEN];
+            struct userNode *opserv = GetUserH(str);
+            send_message_type(4, un, opserv, "Channel modes cannot be changed due to DefCon level %d in effect, please try again soon", DefConLevel);
+            irc_make_chanmode(cn, modes);
+            irc_mode(opserv, cn, modes);
+            irc_mode(opserv, cn, DefConChanModes);
+        }
+        return 1;
+    }
+
+
     return mod_chanmode(un, cn, argv+2, argc-2, MCP_ALLOW_OVB|MCP_FROM_SERVER|(un ? MC_NOTIFY : 0));
 }
 
@@ -1617,6 +2107,62 @@ static CMD_FUNC(cmd_part)
     return 1;
 }
 
+static CMD_FUNC(cmd_svspart)
+{
+    struct userNode *user;
+
+    if (argc < 3)
+        return 0;
+    user = GetUserN(argv[1]);
+    if (!user)
+        return 0;
+    parse_foreach(argv[2], part_helper, NULL, NULL, NULL, user);
+    return 1;
+}
+
+static CMD_FUNC(cmd_silence)
+{
+    struct userNode *user;
+    char *mask;
+    char *new_mask;
+    unsigned int i;
+
+    if (argc < 2)
+        return 0;
+
+    user = GetUserN(argv[1]);
+
+    /* Sanity, go nuts if this happens */
+    if (!user)
+        return 0;
+
+    /*  We can safely ignore this if a user adding a silence is not
+     *  ignored. However this brings up a TODO. If a user logs in and
+     *  they have silences on the IRCd then we need to set them here
+     *  somehow
+     */
+    if (!user->handle_info)
+        return 1;
+
+    mask = argv[2];
+
+    if (*mask == '-') {
+        for (i=0; i<user->handle_info->ignores->used; i++) {
+            if (!irccasecmp(mask+1, user->handle_info->ignores->list[i]))
+                user->handle_info->ignores->list[i] = user->handle_info->ignores->list[--user->handle_info->ignores->used];
+        }
+    } else {
+        for (i=0; i<user->handle_info->ignores->used; i++) {
+            if (!strcmp(mask+1, user->handle_info->ignores->list[i]))
+                return 1; /* Already on the users NickServ ignore list, safely ignore */
+        }
+
+        new_mask = strdup(mask+1);
+        string_list_append(user->handle_info->ignores, new_mask);
+    }
+    return 1;
+}
+
 static CMD_FUNC(cmd_kick)
 {
     if (argc < 3)
@@ -1628,6 +2174,7 @@ static CMD_FUNC(cmd_kick)
 static CMD_FUNC(cmd_squit)
 {
     struct server *server;
+    char *uplink;
 
     if (argc < 4)
         return 0;
@@ -1642,7 +2189,13 @@ static CMD_FUNC(cmd_squit)
         return 1;
     }
 
+    uplink = strdup(server->uplink->name);
     DelServer(server, 0, argv[3]);
+    /* if its a pingout and pingout connecting is enabled
+               or its a read error and readerror connecting is enabled
+               or were doing a "N" and i need to connect that server somewhere */
+    routing_handle_squit(argv[1], uplink, argv[3]);
+    free(uplink);
     return 1;
 }
 
@@ -1654,23 +2207,68 @@ static CMD_FUNC(cmd_privmsg)
     pd.user = GetUserH(origin);
     if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user)))
         return 1;
+
+    if (checkDefCon(DEFCON_OPER_ONLY) && !IsOper(pd.user)) {
+        const char *str;
+        str = conf_get_data("services/opserv/nick", RECDB_QSTRING);
+        if (str)
+            send_message_type(4, pd.user, GetUserH(str), "Services are currently not available, please try again soon");
+        return 1;
+    }
+
+    if (checkDefCon(DEFCON_SILENT_OPER_ONLY) && !IsOper(pd.user))
+        return 1; /* Silently Ignore */
+
     pd.is_notice = 0;
     pd.text = argv[2];
     parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
+
     return 1;
 }
 
 static CMD_FUNC(cmd_notice)
 {
     struct privmsg_desc pd;
+    struct server *srv;
+    int nuser = 0;
+
     if (argc != 3)
         return 0;
+
     pd.user = GetUserH(origin);
-    if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user)))
-        return 1;
-    pd.is_notice = 1;
-    pd.text = argv[2];
-    parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
+    if(!pd.user)
+        nuser = 1;
+    if (!pd.user || (IsGagged(pd.user) && !IsOper(pd.user))) {
+    }
+    else {
+        pd.is_notice = 1;
+        pd.text = argv[2];
+        parse_foreach(argv[1], privmsg_chan_helper, NULL, privmsg_user_helper, privmsg_invalid, &pd);
+    }
+
+    srv = GetServerH(origin);
+    if(srv) {
+        char *sargv[MAXNUMPARAMS];
+        int sargc;
+
+        sargc = split_line(argv[2], true, MAXNUMPARAMS, sargv);
+
+        if(!strcasecmp(sargv[0], "Connect:")) {
+            /* :Connect: Host shoe.loxxin.net not listed in ircd.conf */
+            if(!strcasecmp(sargv[3], "not") && !strcasecmp(sargv[4], "listed")) {
+                routing_handle_connect_failure(srv, sargv[2], unsplit_string(sargv+3, sargc-3, NULL));
+            }
+        }
+        else if(!strcasecmp(sargv[0], "Link")) {
+            /* :Link with mephisto.etheria.cx cancelled: Server mephisto.etheria.cx[216.46.33.71]
+             *
+             * :Link with laptop.afternet.org cancelled: Connection refused
+             */
+            if(!strcasecmp(sargv[3], "cancelled:")) {
+                routing_handle_connect_failure(srv, sargv[2], unsplit_string(sargv+4, sargc-4, NULL));
+            }
+        }
+    }
     return 1;
 }
 
@@ -1823,6 +2421,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);
@@ -1851,8 +2451,8 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_TOPIC, cmd_topic);
     dict_insert(irc_func_dict, CMD_AWAY, cmd_away);
     dict_insert(irc_func_dict, TOK_AWAY, cmd_away);
-    dict_insert(irc_func_dict, CMD_SILENCE, cmd_dummy);
-    dict_insert(irc_func_dict, TOK_SILENCE, cmd_dummy);
+    dict_insert(irc_func_dict, CMD_SILENCE, cmd_silence);
+    dict_insert(irc_func_dict, TOK_SILENCE, cmd_silence);
     dict_insert(irc_func_dict, CMD_KICK, cmd_kick);
     dict_insert(irc_func_dict, TOK_KICK, cmd_kick);
     dict_insert(irc_func_dict, CMD_SQUIT, cmd_squit);
@@ -1863,8 +2463,14 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_NOTICE, cmd_notice);
     dict_insert(irc_func_dict, CMD_STATS, cmd_stats);
     dict_insert(irc_func_dict, TOK_STATS, cmd_stats);
+    dict_insert(irc_func_dict, CMD_SVSJOIN, cmd_svsjoin);
+    dict_insert(irc_func_dict, TOK_SVSJOIN, cmd_svsjoin);
     dict_insert(irc_func_dict, CMD_SVSNICK, cmd_svsnick);
     dict_insert(irc_func_dict, TOK_SVSNICK, cmd_svsnick);
+    dict_insert(irc_func_dict, CMD_SVSPART, cmd_svspart);
+    dict_insert(irc_func_dict, TOK_SVSPART, cmd_svspart);
+    dict_insert(irc_func_dict, CMD_SWHOIS, cmd_dummy);
+    dict_insert(irc_func_dict, TOK_SWHOIS, cmd_dummy);
     dict_insert(irc_func_dict, CMD_WHOIS, cmd_whois);
     dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois);
     dict_insert(irc_func_dict, CMD_GLINE, cmd_gline);
@@ -1879,6 +2485,13 @@ 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_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
@@ -1907,9 +2520,11 @@ 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);
-    /* Ignore privs */
-    dict_insert(irc_func_dict, TOK_PRIVS, 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 remote luser */
     dict_insert(irc_func_dict, TOK_LUSERS, cmd_dummy);
     /* We have reliable clock!  Always!  Wraaa! */
@@ -1919,6 +2534,7 @@ init_parse(void)
     /* 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);
@@ -1953,6 +2569,7 @@ init_parse(void)
     userList_init(&dead_users);
     reg_del_channel_func(remove_unbursted_channel);
     reg_exit_func(parse_cleanup);
+    // reg_notice_func(opserv, check_ctcp);
 }
 
 int
@@ -2226,6 +2843,8 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
 {
     struct userNode *oldUser, *uNode;
     unsigned int n, ignore_user;
+    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);
@@ -2273,6 +2892,21 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname));
     safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
     irc_p10_pton(&uNode->ip, realip);
+
+    tstr = conf_get_data("server/type", RECDB_QSTRING);
+    type = atoi(tstr);
+    if (type > 6) {
+      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->timestamp = timestamp;
     modeList_init(&uNode->channels);
     uNode->uplink = uplink;
@@ -2282,6 +2916,9 @@ 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);
+
+    set_geoip_info(uNode);
+
     if (ignore_user)
         return uNode;
 
@@ -2344,6 +2981,25 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
     }
 
     modeList_clean(&user->channels);
+
+    /* Clean up version data */
+    if(user->version_reply) {
+        free(user->version_reply);
+        user->version_reply = 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.
@@ -2396,6 +3052,34 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
        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,