]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/proto-p10.c
If we have 0.5.0 specific features then use this conf option in the code
[irc/evilnet/x3.git] / src / proto-p10.c
index 8ccc8e9aca306ee9a40dc8b73c1af27c3d992821..a9731da75b8d1b302f2236b525c8a2afcaee514d 100644 (file)
@@ -3,7 +3,7 @@
  *
  * This file is part of x3.
  *
- * srvx is free software; you can redistribute it and/or modify
+ * 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
  * (at your option) any later version.
@@ -84,6 +84,7 @@
 #define CMD_SERVSET             "SERVSET"
 #define CMD_SET                        "SET"
 #define CMD_SETTIME             "SETTIME"
+#define CMD_SHUN               "SHUN"
 #define CMD_SILENCE             "SILENCE"
 #define CMD_SQUERY              "SQUERY"
 #define CMD_SQUIT               "SQUIT"
 #define TOK_SERVSET             "SERVSET"
 #define TOK_SET                        "SET"
 #define TOK_SETTIME             "SE"
+#define TOK_SHUN               "SU"
 #define TOK_SILENCE             "U"
 #define TOK_SQUERY              "SQUERY"
 #define TOK_SQUIT               "SQ"
 #define P10_SERVSET             TYPE(SERVSET)
 #define P10_SET                        TYPE(SET)
 #define P10_SETTIME             TYPE(SETTIME)
+#define P10_SHUN               TYPE(SHUN)
 #define P10_SILENCE             TYPE(SILENCE)
 #define P10_SQUERY              TYPE(SQUERY)
 #define P10_SQUIT               TYPE(SQUIT)
@@ -303,11 +306,14 @@ static unsigned int num_notice_funcs;
 static struct dict *unbursted_channels;
 static char *his_servername;
 static char *his_servercomment;
+static int extended_accounts;
 
 static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip);
 
 extern int off_channel;
 
+static int parse_oplevel(char *str);
+
 /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the
  * server and Y's indentifying the client on that server. */
 struct server*
@@ -377,22 +383,96 @@ irc_server(struct server *srv)
 
     inttobase64(extranum, srv->num_mask, (srv->numeric[1] || (srv->num_mask >= 64*64)) ? 3 : 2);
     if (srv == self) {
-        /* The +s, ignored by Run's ircu, means "service" to Undernet's ircu */
-        putsock(P10_SERVER " %s %d %li %li J10 %s%s +s :%s",
+        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);
     } else {
-        putsock("%s " P10_SERVER " %s %d %li %li %c10 %s%s +s :%s",
+        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);
     }
 }
 
+static void
+irc_p10_pton(irc_in_addr_t *ip, const char *input)
+{
+    if (strlen(input) == 6) {
+        unsigned int value;
+        memset(ip, 0, 6 * sizeof(ip->in6[0]));
+        value = base64toint(input, 6);
+        if (value)
+            ip->in6[5] = htons(65535);
+        ip->in6[6] = htons(value >> 16);
+        ip->in6[7] = htons(value & 65535);
+    } else {
+        unsigned int pos = 0;
+        do {
+            if (*input == '_') {
+                unsigned int left;
+                for (left = (25 - strlen(input)) / 3; left; left--)
+                    ip->in6[pos++] = 0;
+                input++;
+            } else {
+                ip->in6[pos++] = ntohs(base64toint(input, 3));
+                input += 3;
+            }
+        } while (pos < 8);
+    }
+}
+
+static void
+irc_p10_ntop(char *output, const irc_in_addr_t *ip)
+{
+    if (!irc_in_addr_is_valid(*ip)) {
+        strcpy(output, "AAAAAA");
+    } else if (irc_in_addr_is_ipv4(*ip)) {
+        unsigned int in4;
+        in4 = (ntohs(ip->in6[6]) << 16) | ntohs(ip->in6[7]);
+        inttobase64(output, in4, 6);
+        output[6] = '\0';
+    } else if (irc_in_addr_is_ipv6(*ip)) {
+        unsigned int max_start, max_zeros, curr_zeros, zero, ii;
+        /* Can start by printing out the leading non-zero parts. */
+        for (ii = 0; (ip->in6[ii]) && (ii < 8); ++ii) {
+            inttobase64(output, ntohs(ip->in6[ii]), 3);
+            output += 3;
+        }
+        /* Find the longest run of zeros. */
+        for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
+            if (!ip->in6[ii])
+                curr_zeros++;
+            else if (curr_zeros > max_zeros) {
+                max_start = ii - curr_zeros;
+                max_zeros = curr_zeros;
+                curr_zeros = 0;
+            }
+        }
+        if (curr_zeros > max_zeros) {
+            max_start = ii - curr_zeros;
+            max_zeros = curr_zeros;
+            curr_zeros = 0;
+        }
+        /* Print the rest of the address */
+        for (ii = zero; ii < 8; ) {
+            if ((ii == max_start) && max_zeros) {
+                *output++ = '_';
+                ii += max_zeros;
+            } else {
+                inttobase64(output, ntohs(ip->in6[ii]), 3);
+                output += 3;
+            }
+        }
+        *output = '\0';
+    } else {
+        strcpy(output, "???");
+    }
+}
+
 void
 irc_user(struct userNode *user)
 {
-    char b64ip[7];
+    char b64ip[25];
     if (!user)
         return;
-    inttobase64(b64ip, ntohl(user->ip.s_addr), 6);
+    irc_p10_ntop(b64ip, &user->ip);
     if (user->modes) {
         int modelen;
         char modes[32];
@@ -431,10 +511,27 @@ irc_user(struct userNode *user)
     }
 }
 
+void
+irc_rename(struct userNode *user, const char *new_handle)
+{
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s M %s", self->numeric, user->numeric, new_handle);
+}
+
+void
+irc_delete(struct userNode *user)
+{
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s U", self->numeric, user->numeric);
+}
+
 void
 irc_account(struct userNode *user, const char *stamp, time_t timestamp)
 {
-    putsock("%s " P10_ACCOUNT " %s R %s %lu", self->numeric, user->numeric, stamp, timestamp);
+    if(extended_accounts)
+        putsock("%s " P10_ACCOUNT " %s R %s %lu", self->numeric, user->numeric, stamp, timestamp); 
+    else
+        putsock("%s " P10_ACCOUNT " %s %s %lu", self->numeric, user->numeric, stamp, timestamp);
 }
 
 void
@@ -539,6 +636,21 @@ irc_pong(const char *who, const char *data)
     putsock("%s " P10_PONG " %s :%s", self->numeric, who, data);
 }
 
+void
+irc_pong_asll(const char *who, const char *orig_ts)
+{
+    char *delim;
+    struct timeval orig;
+    struct timeval 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);
+}
+
 void
 irc_pass(const char *passwd)
 {
@@ -558,10 +670,17 @@ irc_introduce(const char *passwd)
 }
 
 void
-irc_gline(struct server *srv, struct gline *gline)
+irc_gline(struct server *srv, struct gline *gline, int silent)
+{
+    putsock("%s " P10_GLINE " %s +%s %ld :%s<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, silent ? "AUTO " : "", gline->issuer, gline->reason);
+}
+
+void
+irc_shun(struct server *srv, struct shun *shun)
 {
-    putsock("%s " P10_GLINE " %s +%s %ld :<%s> %s",
-            self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires-now, gline->issuer, gline->reason);
+    putsock("%s " P10_SHUN " %s +%s %ld :<%s> %s",
+            self->numeric, (srv ? srv->numeric : "*"), shun->target, shun->expires-now, shun->issuer, shun->reason);
 }
 
 void
@@ -579,6 +698,12 @@ irc_ungline(const char *mask)
     putsock("%s " P10_GLINE " * -%s", self->numeric, mask);
 }
 
+void
+irc_unshun(const char *mask)
+{
+    putsock("%s " P10_SHUN " * -%s", self->numeric, mask);
+}
+
 static void
 irc_burst(struct chanNode *chan)
 {
@@ -710,6 +835,7 @@ irc_kill(struct userNode *from, struct userNode *target, const char *message)
 void
 irc_mode(struct userNode *from, struct chanNode *target, const char *modes)
 {
+    call_channel_mode_funcs(from, target, (char **)&modes, 0);
     putsock("%s " P10_MODE " %s %s "FMT_TIME_T,
             (from ? from->numeric : self->numeric),
             target->name, modes, target->timestamp);
@@ -775,9 +901,20 @@ irc_part(struct userNode *who, struct chanNode *what, const char *reason)
 }
 
 void
-irc_topic(struct userNode *who, struct chanNode *what, const char *topic)
+irc_topic(struct userNode *service, struct userNode *who, struct chanNode *what, const char *topic)
 {
-    putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
+
+   int type = 4;
+   const char *str;
+   str = conf_get_data("server/type", RECDB_QSTRING);
+   type = atoi(str);
+
+   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);
+   } else {
+     who = service; /* REMOVE LINE FOR NEFARIOUS 0.5.0 */
+     putsock("%s " P10_TOPIC " %s :%s", who->numeric, what->name, topic);
+   }
 }
 
 void
@@ -940,8 +1077,8 @@ static CMD_FUNC(cmd_ping)
     struct server *srv;
     struct userNode *un;
 
-    if(argc > 3)
-        irc_pong(argv[2], argv[1]);
+    if (argc > 3)
+        irc_pong_asll(argv[2], argv[3]);
     else if ((srv = GetServerH(origin)))
         irc_pong(self->name, srv->numeric);
     else if ((un = GetUserH(origin)))
@@ -1069,6 +1206,9 @@ static CMD_FUNC(cmd_account)
     user = GetUserN(argv[1]);
     if (!user)
         return 1; /* A QUIT probably passed the ACCOUNT. */
+
+    if(!extended_accounts) /* any need for this function without? */
+        return 1;
     
     if(!strcmp(argv[2],"C"))
     {
@@ -1115,6 +1255,7 @@ static CMD_FUNC(cmd_burst)
     struct userNode *un;
     struct modeNode *mNode;
     long mode;
+    int oplevel = -1;
     char *user, *end, sep;
     time_t in_timestamp;
     char* parm = NULL;
@@ -1132,7 +1273,8 @@ static CMD_FUNC(cmd_burst)
             const char *pos;
             int n_modes;
             for (pos=argv[next], n_modes = 1; *pos; pos++)
-                if ((*pos == 'k') || (*pos == 'l'))
+                if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A')
+                    || (*pos == 'U'))
                     n_modes++;
             unsplit_string(argv+next, n_modes, modes);
             next += n_modes;
@@ -1200,14 +1342,21 @@ static CMD_FUNC(cmd_burst)
         if (sep == ':') {
             mode = 0;
             while ((sep = *end++)) {
-                if (sep == 'o')
+                if (sep == 'o') {
                     mode |= MODE_CHANOP;
-                else if (sep == 'h')
+                    oplevel = -1;
+                } else if (sep == 'h') {
                     mode |= MODE_HALFOP;
-                else if (sep == 'v')
+                    oplevel = -1;
+                } else if (sep == 'v') {
                     mode |= MODE_VOICE;
-                else if (isdigit(sep)) {
+                    oplevel = -1;
+                } else if (isdigit(sep)) {
                     mode |= MODE_CHANOP;
+                    if (oplevel >= 0)
+                        oplevel += parse_oplevel(end);
+                    else
+                        oplevel = parse_oplevel(end);
                     while (isdigit(*end)) end++;
                 } else
                     break;
@@ -1219,8 +1368,10 @@ static CMD_FUNC(cmd_burst)
             res = 0;
             continue;
         }
-        if ((mNode = AddChannelUser(un, cNode)))
+        if ((mNode = AddChannelUser(un, cNode))) {
             mNode->modes = mode;
+            mNode->oplevel = oplevel;
+        }
     }
 
     return res;
@@ -1328,6 +1479,7 @@ static CMD_FUNC(cmd_topic)
 {
     struct chanNode *cn;
     time_t chan_ts, topic_ts;
+    struct userNode *user;
 
     if (argc < 3)
         return 0;
@@ -1335,15 +1487,23 @@ static CMD_FUNC(cmd_topic)
         log_module(MAIN_LOG, LOG_ERROR, "Unable to find channel %s whose topic is being set", argv[1]);
         return 0;
     }
-    if (argc >= 5) {
-        /* Looks like an Asuka style topic burst. */
+
+
+    if (argc == 5) {              /* Asuka / Topic Bursting IRCu's */
+        user = GetUserH(origin);
         chan_ts = atoi(argv[2]);
         topic_ts = atoi(argv[3]);
-    } else {
+    } else if (argc >= 6) {       /* Nefarious 0.5.0 */
+        user = GetUserH(strtok(argv[2], "!"));
+        chan_ts = atoi(argv[3]);
+        topic_ts = atoi(argv[4]);
+    } else {                      /* Regular IRCu (No Topic Bursting)*/
+        user = GetUserH(origin);
         chan_ts = cn->timestamp;
         topic_ts = now;
     }
-    SetChannelTopic(cn, GetUserH(origin), argv[argc-1], 0);
+
+    SetChannelTopic(cn, user, user, argv[argc-1], 0);
     cn->topic_time = topic_ts;
     return 1;
 }
@@ -1388,7 +1548,15 @@ static CMD_FUNC(cmd_num_gline)
 {
     if (argc < 6)
         return 0;
-    gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
+    gline_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0, 0);
+    return 1;
+}
+
+static CMD_FUNC(cmd_num_shun)
+{
+    if (argc < 6)
+        return 0;
+    shun_add(origin, argv[3], atoi(argv[4])-now, argv[5], now, 0);
     return 1;
 }
 
@@ -1521,7 +1689,7 @@ static CMD_FUNC(cmd_gline)
     if (argv[2][0] == '+') {
         if (argc < 5)
             return 0;
-        gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
+        gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0, 0);
         return 1;
     } else if (argv[2][0] == '-') {
         gline_remove(argv[2]+1, 0);
@@ -1530,6 +1698,22 @@ static CMD_FUNC(cmd_gline)
         return 0;
 }
 
+static CMD_FUNC(cmd_shun)
+{
+    if (argc < 3)
+        return 0;
+    if (argv[2][0] == '+') {
+        if (argc < 5)
+            return 0;
+        shun_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, 0);
+        return 1;
+    } else if (argv[2][0] == '-') {
+        shun_remove(argv[2]+1, 0);
+        return 1;
+    } else
+        return 0;
+}
+
 static CMD_FUNC(cmd_svsnick)
 {
     struct userNode *target, *dest;
@@ -1585,6 +1769,8 @@ init_parse(void)
     char numer[COMBO_NUMERIC_LEN+1];
 
     /* read config items */
+    str = conf_get_data("server/extended_accounts", RECDB_QSTRING);
+    extended_accounts = str ? enabled_string(str) : 1;
     str = conf_get_data("server/ping_freq", RECDB_QSTRING);
     ping_freq = str ? ParseInterval(str) : 120;
     str = conf_get_data("server/ping_timeout", RECDB_QSTRING);
@@ -1677,6 +1863,8 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois);
     dict_insert(irc_func_dict, CMD_GLINE, cmd_gline);
     dict_insert(irc_func_dict, TOK_GLINE, cmd_gline);
+    dict_insert(irc_func_dict, CMD_SHUN, cmd_shun);
+    dict_insert(irc_func_dict, TOK_SHUN, cmd_shun);
     dict_insert(irc_func_dict, CMD_OPMODE, cmd_opmode);
     dict_insert(irc_func_dict, TOK_OPMODE, cmd_opmode);
     dict_insert(irc_func_dict, CMD_CLEARMODE, cmd_clearmode);
@@ -1721,6 +1909,11 @@ init_parse(void)
     /* 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 /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);
+
     /* handle topics */
     dict_insert(irc_func_dict, "331", cmd_num_topic);
     dict_insert(irc_func_dict, "332", cmd_num_topic);
@@ -1730,6 +1923,7 @@ init_parse(void)
     /* ban list resetting */
     /* "stats g" responses */
     dict_insert(irc_func_dict, "247", cmd_num_gline);
+    dict_insert(irc_func_dict, "542", cmd_num_shun);
     dict_insert(irc_func_dict, "219", cmd_dummy); /* "End of /STATS report" */
     /* other numeric responses we might get */
     dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
@@ -1795,15 +1989,18 @@ static void
 parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data)
 {
     char *j, old;
+
     do {
         j = target_list;
         while (*j != 0 && *j != ',')
             j++;
         old = *j;
         *j = 0;
+
         if (IsChannelName(target_list)
             || (target_list[0] == '0' && target_list[1] == '\0')) {
             struct chanNode *chan = GetChannel(target_list);
+
             if (chan) {
                 if (cf)
                     cf(chan, data);
@@ -2069,7 +2266,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     safestrncpy(uNode->info, userinfo, sizeof(uNode->info));
     safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname));
     safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
-    uNode->ip.s_addr = htonl(base64toint(realip, 6));
+    irc_p10_pton(&uNode->ip, realip);
     uNode->timestamp = timestamp;
     modeList_init(&uNode->channels);
     uNode->uplink = uplink;
@@ -2159,6 +2356,9 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
 
     if (!user || !mode_change)
         return;
+
+    call_user_mode_funcs(user, mode_change);
+
     while (*word != ' ' && *word) word++;
     while (*word == ' ') word++;
     while (1) {
@@ -2237,7 +2437,7 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
 }
 
 struct mod_chanmode *
-mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
+mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
 {
     struct mod_chanmode *change;
     unsigned int ii, in_arg, ch_arg, add;
@@ -2311,6 +2511,37 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 }
             }
             break;
+        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));
+            } else {
+                change->modes_clear |= MODE_UPASS;
+                if (!(flags & MCP_UPASS_FREE)) {
+                    if (in_arg >= argc)
+                        goto error;
+                    in_arg++;
+                }
+            }
+            break;
+        case 'A':
+            if (add) {
+                if (in_arg >= argc)
+                    goto error;
+                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)) {
+                    if (in_arg >= argc)
+                      goto error;
+                    in_arg++;
+                }
+            }
+            break;
         case 'b':
             if (!(flags & MCP_ALLOW_OVB))
                 goto error;
@@ -2334,6 +2565,29 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'o': case 'h': case 'v':
         {
             struct userNode *victim;
+            char *oplevel_str;
+            int oplevel;
+
+            if (in_arg >= argc)
+                goto error;
+            oplevel_str = strchr(modes[in_arg], ':');
+            if (oplevel_str)
+            {
+                /* XXYYY M #channel +o XXYYY:<oplevel> */
+                *oplevel_str++ = '\0';
+                oplevel = parse_oplevel(oplevel_str);
+                if (oplevel <= base_oplevel && !(flags & MCP_FROM_SERVER))
+                    oplevel = base_oplevel + 1;
+            }
+            else if (channel->modes & MODE_UPASS)
+                oplevel = base_oplevel + 1;
+            else
+                oplevel = -1;
+
+            /* Check that oplevel is within bounds. */
+            if (oplevel > MAXOPLEVEL)
+                oplevel = MAXOPLEVEL;
+
             if (!(flags & MCP_ALLOW_OVB))
                 goto error;
             if (in_arg >= argc)
@@ -2355,7 +2609,11 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             if (!victim)
                 continue;
             if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
+            {
+                /* Apply the oplevel change */
+                change->args[ch_arg].u.member->oplevel = oplevel;
                 ch_arg++;
+            }
             break;
         }
         default:
@@ -2456,6 +2714,10 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
             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)
+            mod_chanmode_append(&chbuf, 'A', channel->apass);
     }
     for (arg = 0; arg < change->argc; ++arg) {
         if (!(change->args[arg].mode & MODE_REMOVE))
@@ -2508,6 +2770,10 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', change->new_key);
+        if (change->modes_set & MODE_UPASS)
+            mod_chanmode_append(&chbuf, 'U', change->new_upass);
+        if (change->modes_set & MODE_APASS)
+            mod_chanmode_append(&chbuf, 'A', change->new_apass);
         if(change->modes_set & MODE_LIMIT) {
             sprintf(int_buff, "%d", change->new_limit);
             mod_chanmode_append(&chbuf, 'l', int_buff);
@@ -2561,6 +2827,8 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(NOPRIVMSGS, 'n');
         DO_MODE_CHAR(LIMIT, 'l');
         DO_MODE_CHAR(KEY, 'k');
+        DO_MODE_CHAR(UPASS, 'U');
+        DO_MODE_CHAR(APASS, 'A');
         DO_MODE_CHAR(DELAYJOINS, 'D');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
@@ -2599,10 +2867,51 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(SSLONLY, 'Z');
        DO_MODE_CHAR(HIDEMODE, 'L');
 #undef DO_MODE_CHAR
-        switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
+        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;
+
+        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;
@@ -2635,6 +2944,14 @@ clear_chanmode(struct chanNode *channel, const char *modes)
             remove |= MODE_KEY;
             channel->key[0] = '\0';
             break;
+        case 'A':
+            remove |= MODE_APASS;
+            channel->apass[0] = '\0';
+            break;
+        case 'U':
+            remove |= MODE_UPASS;
+            channel->upass[0] = '\0';
+            break;
         case 'l':
             remove |= MODE_LIMIT;
             channel->limit = 0;
@@ -2811,3 +3128,16 @@ send_burst(void)
     for (it = dict_first(channels); it; it = iter_next(it))
         dict_insert(unbursted_channels, iter_key(it), iter_data(it));
 }
+
+/*
+ * Oplevel parsing
+ */
+static int
+parse_oplevel(char *str)
+{
+    int oplevel = 0;
+    while (isdigit(*str))
+        oplevel = oplevel * 10 + *str++ - '0';
+    return oplevel;
+}
+