Merge remote branch 'upstream/master'
[srvx.git] / src / proto-p10.c
index 71f6d3766acd5f2ef97de6a6f155b5d1ec31038a..d08fb1804fd82c48aaf5a3f9ffcd624d1a48d05a 100644 (file)
@@ -40,6 +40,7 @@
 #define CMD_EOB_ACK             "EOB_ACK"
 #define CMD_ERROR               "ERROR"
 #define CMD_FAKEHOST            "FAKE"
+#define CMD_FAKEHOST2           "FAKE2"
 #define CMD_GET                 "GET"
 #define CMD_GLINE               "GLINE"
 #define CMD_HASH                "HASH"
@@ -72,6 +73,7 @@
 #define CMD_PROTO               "PROTO"
 #define CMD_QUIT                "QUIT"
 #define CMD_REHASH              "REHASH"
+#define CMD_RELAY               "RELAY"
 #define CMD_RESET               "RESET"
 #define CMD_RESTART             "RESTART"
 #define CMD_RPING               "RPING"
@@ -86,6 +88,8 @@
 #define CMD_SQUIT               "SQUIT"
 #define CMD_STATS               "STATS"
 #define CMD_SVSNICK             "SVSNICK"
+#define CMD_SVSMODE             "SVSMODE"
+#define CMD_SVSJOIN             "SVSJOIN"
 #define CMD_TIME                "TIME"
 #define CMD_TOPIC               "TOPIC"
 #define CMD_TRACE               "TRACE"
 #define TOK_EOB_ACK             "EA"
 #define TOK_ERROR               "Y"
 #define TOK_FAKEHOST            "FA"
+#define TOK_FAKEHOST2           "NFH"
 #define TOK_GET                 "GET"
 #define TOK_GLINE               "GL"
 #define TOK_HASH                "HASH"
 #define TOK_PROTO               "PROTO"
 #define TOK_QUIT                "Q"
 #define TOK_REHASH              "REHASH"
+#define TOK_RELAY               "RL"
 #define TOK_RESET               "RESET"
 #define TOK_RESTART             "RESTART"
 #define TOK_RPING               "RI"
 #define TOK_SQUIT               "SQ"
 #define TOK_STATS               "R"
 #define TOK_SVSNICK             "SN"
+#define TOK_SVSMODE             "SM"
+#define TOK_SVSJOIN             "SJ"
 #define TOK_TIME                "TI"
 #define TOK_TOPIC               "T"
 #define TOK_TRACE               "TR"
 #define P10_EOB_ACK             TYPE(EOB_ACK)
 #define P10_ERROR               TYPE(ERROR)
 #define P10_FAKEHOST            TYPE(FAKEHOST)
+#define P10_FAKEHOST2           TYPE(FAKEHOST2)
 #define P10_GET                 TYPE(GET)
 #define P10_GLINE               TYPE(GLINE)
 #define P10_HASH                TYPE(HASH)
 #define P10_PROTO               TYPE(PROTO)
 #define P10_QUIT                TYPE(QUIT)
 #define P10_REHASH              TYPE(REHASH)
+#define P10_RELAY               TYPE(RELAY)
 #define P10_RESET               TYPE(RESET)
 #define P10_RESTART             TYPE(RESTART)
 #define P10_RPING               TYPE(RPING)
 #define P10_SQUIT               TYPE(SQUIT)
 #define P10_STATS               TYPE(STATS)
 #define P10_SVSNICK             TYPE(SVSNICK)
+#define P10_SVSMODE             TYPE(SVSMODE)
+#define P10_SVSJOIN             TYPE(SVSJOIN)
 #define P10_TIME                TYPE(TIME)
 #define P10_TOPIC               TYPE(TOPIC)
 #define P10_TRACE               TYPE(TRACE)
@@ -299,8 +311,8 @@ static const char *his_servername;
 static const char *his_servercomment;
 static struct channelList dead_channels;
 
-/* These correspond to 1 << X:      012345678901234567 */
-const char irc_user_mode_chars[] = "o iw dkgn    x   I";
+/* These correspond to 1 << X:      012345678901234567890123 */
+const char irc_user_mode_chars[] = "o iw dkgn    x   ISDXHst";
 
 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, unsigned long timestamp, const char *realip);
 
@@ -499,7 +511,25 @@ irc_account(struct userNode *user, const char *stamp, unsigned long timestamp, u
 void
 irc_fakehost(struct userNode *user, const char *host, const char *ident, int force)
 {
-    putsock("%s " P10_FAKEHOST " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : "");
+    /* SRVX added the possibility for FAKE IDENTS
+     * but this is currently *NOT* supported by our IRCu
+     *
+     * edit 24.11.11: 
+     *  NFH (P10_FAKEHOST2) is now supported by our IRCu (git-65-21592a4)
+     */
+    putsock("%s " P10_FAKEHOST2 " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : "");
+}
+
+void 
+irc_relay(char *message)
+{
+    putsock("%s " P10_RELAY " %s", self->numeric, message);
+}
+
+void 
+irc_simul(struct userNode *target, char *command)
+{
+    putsock("%s " P10_RELAY " %s SI %s :%s", self->numeric, target->numeric, target->numeric, command);
 }
 
 void
@@ -600,6 +630,24 @@ irc_privmsg(struct userNode *from, const char *to, const char *message)
         putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message);
 }
 
+void
+irc_svsmode(struct userNode *from, struct userNode *user, const char *modes)
+{
+putsock("%s " P10_SVSMODE " %s %s", from->numeric, user->numeric, modes);
+}
+
+void
+irc_svsjoin(struct userNode *from, struct userNode *user, struct chanNode *chan)
+{
+putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan->name);
+}
+
+void
+irc_svsjoinchan(struct userNode *from, struct userNode *user, const char *chan)
+{
+putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan);
+}
+
 void
 irc_eob(void)
 {
@@ -660,12 +708,16 @@ irc_introduce(const char *passwd)
 void
 irc_gline(struct server *srv, struct gline *gline)
 {
+    //<prefix> GL <target> [!][+|-|>|<]<mask> [<expiration>] [<lastmod>] [<lifetime>] [:<reason>]
+    //expiration = relative time (seconds)
+    //lastmod = timestamp
+    //livetime = timestamp
     if (gline->lastmod)
-        putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s",
-                self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->lastmod, gline->lifetime, gline->reason);
+        putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s", self->numeric, (srv ? srv->numeric : "*"), 
+                gline->target, gline->expires-now, gline->lastmod, gline->lifetime, gline->reason);
     else
-        putsock("%s " P10_GLINE " %s +%s %lu :%s",
-                self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->reason);
+        putsock("%s " P10_GLINE " %s +%s %lu :%s", self->numeric, (srv ? srv->numeric : "*"), 
+                gline->target, gline->expires-now, gline->reason);
 }
 
 void
@@ -681,6 +733,7 @@ void
 irc_ungline(const char *mask)
 {
     putsock("%s " P10_GLINE " * -%s %lu", self->numeric, mask, now);
+    //putsock("%s " P10_GLINE " * %s * %lu", self->numeric, mask, now);
 }
 
 /* Return negative if *(struct modeNode**)pa is "less than" pb,
@@ -1168,6 +1221,8 @@ create_helper(char *name, void *data)
         return;
     }
 
+    handle_new_channel_created(name, cd->user);
+
     AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL));
 }
 
@@ -1301,7 +1356,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') || (*pos == 'a') || (*pos == 'F'))
                     n_modes++;
             if (next + n_modes > argc)
                 n_modes = argc - next;
@@ -1556,6 +1611,8 @@ static CMD_FUNC(cmd_kick)
 {
     if (argc < 3)
         return 0;
+    if (GetUserN(argv[2]) && IsOper(GetUserN(argv[2])))
+        operpart(GetChannel(argv[1]), GetUserN(argv[2]));
     ChannelUserKicked(GetUserH(origin), GetUserN(argv[2]), GetChannel(argv[1]));
     return 1;
 }
@@ -1678,6 +1735,105 @@ static CMD_FUNC(cmd_time)
     return 1;
 }
 
+static CMD_FUNC(cmd_relay)
+{
+    struct server *sNode;
+    unsigned int len;
+    char buf[3];
+    //<sender> RELAY <destination> <command>
+    len = strlen(argv[1]);
+    buf[2] = 0;
+    switch(len) {
+        case 2:
+            sNode = GetServerN(argv[1]);
+            break;
+        case 5:
+            buf[0] = argv[1][0];
+            buf[1] = argv[1][1];
+            sNode = GetServerN(buf);
+            break;
+        case 6:
+            buf[0] = argv[1][1];
+            buf[1] = argv[1][2];
+            sNode = GetServerN(buf);
+            break;
+        default:
+            /* Invalid destination. Ignore. */
+            return 0;
+    }
+    if(sNode->numeric == self->numeric) {
+        //ok  someone relayed something to us!
+        if(strcmp("LQ", argv[2]) == 0) {
+            //oooh thats exciting - we've got a LOC Query! :D
+            //LQ !ABADE pk910 80.153.5.212 server.zoelle1.de ~watchcat :test
+            //ok  let's check the login datas
+            struct handle_info *hi;
+            char tmp[MAXLEN], tmp2[MAXLEN];
+            sprintf(tmp, "%s@%s",argv[7],argv[6]);
+            sprintf(tmp2, "%s@%s",argv[7],argv[5]);
+            if((hi = checklogin(argv[4],argv[argc-1],&argv[3][1],tmp,tmp2))) {
+             //login ok
+             struct devnull_class *th;
+             char devnull[512];
+             if(hi->devnull && (th = devnull_get(hi->devnull))) {
+                const char *devnull_modes = DEVNULL_MODES;
+                int ii, flen;
+                char flags[50];
+                for (ii=flen=0; devnull_modes[ii]; ++ii)
+                    if (th->modes & (1 << ii))
+                        flags[flen++] = devnull_modes[ii];
+                flags[flen] = 0;
+                sprintf(devnull, "+%s %s %lu %lu",flags,th->name,th->maxchan,th->maxsendq);
+             } else {
+                devnull[0] = 0;
+             }
+             if(!HANDLE_FLAGGED(hi, AUTOHIDE)) {
+                sprintf(tmp,"%s LA %s 0 %s\n",argv[3],hi->handle,devnull);
+             } else if(getfakehost(argv[4])) {
+                sprintf(tmp,"%s LA %s %s %s\n",argv[3],hi->handle,getfakehost(argv[4]),devnull);
+             } else {
+                extern const char *hidden_host_suffix;
+                sprintf(tmp,"%s LA %s %s.%s %s\n",argv[3],hi->handle,hi->handle,hidden_host_suffix,devnull);
+             }
+             irc_relay(tmp);
+            } else {
+             //login rejected
+             sprintf(tmp,"%s LR\n",argv[3]);
+             irc_relay(tmp);
+            }
+        } else if(strcmp("UC", argv[2]) == 0) {
+            char tmp[MAXLEN];
+            sprintf(tmp,"%s UC %s %s",argv[3],argv[3],argv[4]);
+            irc_relay(tmp);
+        } else if(strcmp("JA", argv[2]) == 0) {
+            struct userData *uData;
+            struct chanNode *cn;
+            struct userNode *user;
+            char tmp[MAXLEN];
+            cn = GetChannel(argv[4]);
+            if (!cn) return 0;
+            if (!(user = GetUserN(argv[3]))) return 0;
+            if(!cn->channel_info) {
+                //channel not registered
+                sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]);
+            } else if((uData = GetChannelUser(cn->channel_info, user->handle_info))) {
+                if(uData->access >= atoi(argv[5])) {
+                    //we can join
+                    sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]);
+                } else {
+                    //access too low
+                    sprintf(tmp,"%s JAR %s %i %i\n",argv[3],cn->name,uData->access,uData->access);
+                }
+            } else {
+                //0 access
+                sprintf(tmp,"%s JAR %s %s %s\n",argv[3],cn->name,"0","0");
+            }
+            irc_relay(tmp);
+        }
+    }
+    return 1;
+}
+
 static CMD_FUNC(cmd_xquery)
 {
     struct server *source;
@@ -1815,6 +1971,8 @@ init_parse(void)
     dict_insert(irc_func_dict, TOK_STATS, cmd_stats);
     dict_insert(irc_func_dict, CMD_SVSNICK, cmd_svsnick);
     dict_insert(irc_func_dict, TOK_SVSNICK, cmd_svsnick);
+    dict_insert(irc_func_dict, CMD_RELAY, cmd_relay);
+    dict_insert(irc_func_dict, TOK_RELAY, cmd_relay);
     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);
@@ -2322,8 +2480,10 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
         case 'o':
             do_user_mode(FLAGS_OPER);
             if (!add) {
+                operdel(user);
                 userList_remove(&curr_opers, user);
             } else if (!userList_contains(&curr_opers, user)) {
+                operadd(user);
                 userList_append(&curr_opers, user);
                 call_oper_funcs(user);
             }
@@ -2340,6 +2500,12 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
         case 'g': do_user_mode(FLAGS_GLOBAL); break;
         case 'n': do_user_mode(FLAGS_NOCHAN); break;
         case 'I': do_user_mode(FLAGS_NOIDLE); break;
+        case 'S': do_user_mode(FLAGS_NETSERV); break;
+        case 'D': do_user_mode(FLAGS_SECURITYSERV); break;
+        case 'X': do_user_mode(FLAGS_XTRAOP); break;
+        case 's': do_user_mode(FLAGS_SERVERNOTICE); break;
+        case 'H': do_user_mode(FLAGS_HIDDENOPER); break;
+        case 't': do_user_mode(FLAGS_SEENOIDLE); break;
         case 'x': do_user_mode(FLAGS_HIDDEN_HOST); break;
         case 'r':
             if (*word) {
@@ -2415,7 +2581,7 @@ keyncpy(char output[], char input[], size_t output_size)
 }
 
 struct mod_chanmode *
-mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
+mod_chanmode_parse(struct chanNode *channel, struct userNode *user, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
 {
     struct mod_chanmode *change;
     unsigned int ii, in_arg, ch_arg, add;
@@ -2439,6 +2605,10 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'C': do_chan_mode(MODE_NOCTCPS); break;
         case 'D': do_chan_mode(MODE_DELAYJOINS); break;
         case 'c': do_chan_mode(MODE_NOCOLORS); break;
+        case 'M': do_chan_mode(MODE_NOAMSGS); break;
+        case 'N': do_chan_mode(MODE_NONOTICES); break;
+        case 'u': do_chan_mode(MODE_AUDITORIUM); break;
+        case 'S': do_chan_mode(MODE_SSLCHAN); break;
         case 'i': do_chan_mode(MODE_INVITEONLY); break;
         case 'm': do_chan_mode(MODE_MODERATED); break;
         case 'n': do_chan_mode(MODE_NOPRIVMSGS); break;
@@ -2447,8 +2617,14 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 's': do_chan_mode(MODE_SECRET); break;
         case 't': do_chan_mode(MODE_TOPICLIMIT); break;
         case 'z':
-          if (!(flags & MCP_REGISTERED))
-              do_chan_mode(MODE_REGISTERED);
+          if (!(flags & MCP_REGISTERED) && (!(flags & MCP_IGN_REGISTERED) || add)) {
+            do_chan_mode(MODE_REGISTERED);
+          } else if (flags & MCP_IGN_REGISTERED) {
+            /* ignore the modechange but continue parsing */
+          } else {
+            mod_chanmode_free(change);
+            return NULL;
+          }
           break;
 #undef do_chan_mode
         case 'l':
@@ -2462,6 +2638,17 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 change->modes_clear |= MODE_LIMIT;
             }
             break;
+        case 'a':
+            if (add) {
+                if (in_arg >= argc)
+                    goto error;
+                change->modes_set |= MODE_ACCESS;
+                change->new_access = atoi(modes[in_arg++]);
+            } else {
+                change->modes_set &= ~MODE_ACCESS;
+                change->modes_clear |= MODE_ACCESS;
+            }
+            break;
         case 'k':
             if (add) {
                 if ((in_arg >= argc)
@@ -2477,6 +2664,70 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 }
             }
             break;
+        case 'f':
+                   if (add) {
+                           if (in_arg >= argc)
+                    goto error;
+                char *mode = modes[in_arg++];
+                if(mode[0] == '!' && !(flags & MCP_OPERMODE)) //noflood flag also for overriders
+                   goto error;//only allow opers
+                else if(mode[0] == '!')
+                   mode++;
+                
+                if(mode[0] == '+' || mode[0] == '@') {
+                    mode++;
+                }
+                char *p;
+                int count = 0, time = 0;
+                for(p = mode; p[0]; p++) {
+                    if(p[0] == ':') {
+                        char tmpchar = p[0];
+                        p[0] = '\0';
+                        count = strtoul(mode,0,10);
+                        p[0] = tmpchar;
+                                               p++;
+                                               time = strtoul(p,0,10);
+                                               break;
+                               }
+                }
+                if(count <= 0 || time <= 0 || count > 100 || time > 600)
+                                   goto error;
+                               change->modes_set |= MODE_NOFLOOD;
+                               safestrncpy(change->new_noflood, modes[in_arg - 1], sizeof(change->new_noflood));
+                       } else {
+                           change->modes_clear |= MODE_NOFLOOD;
+                       }
+            break;
+        case 'F':
+            if (add) {
+                if (in_arg >= argc)
+                    goto error;
+                char *altchan = modes[in_arg++];
+                struct chanNode *target;
+                if(!IsChannelName(altchan) || !(target = GetChannel(altchan)))
+                    goto error;
+                if(!(flags & MCP_OPERMODE)) {
+                    //check if the user has the permissions to use this channel as target
+                    struct modeNode *mn;
+                    struct userData *uData;
+                    struct chanData *cData;
+                    if(user && (mn = GetUserMode(target, user)) && (mn->modes & MODE_CHANOP)) {
+                        //allow - user is opped on target channel
+                    } else if(user && user->handle_info && 
+                              (uData = GetChannelUser(channel->channel_info, user->handle_info)) && 
+                              (cData = uData->channel) && 
+                              uData->access >= cData->lvlOpts[lvlGiveOps]
+                             ) {
+                        //allow - user has access to get op on the channel
+                    } else 
+                        goto error;
+                }
+                change->modes_set |= MODE_ALTCHAN;
+                safestrncpy(change->new_altchan, altchan, sizeof(change->new_altchan));
+            } else {
+                change->modes_clear |= MODE_ALTCHAN;
+            }
+            break;
         case 'U':
             if (flags & MCP_NO_APASS)
                 goto error;
@@ -2657,10 +2908,17 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(INVITEONLY, 'i');
         DO_MODE_CHAR(NOPRIVMSGS, 'n');
         DO_MODE_CHAR(LIMIT, 'l');
+        DO_MODE_CHAR(ACCESS, 'a');
+        DO_MODE_CHAR(ALTCHAN, 'F');
+        DO_MODE_CHAR(NOFLOOD, 'f');
         DO_MODE_CHAR(DELAYJOINS, 'D');
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(NONOTICES, 'N');
+        DO_MODE_CHAR(NOAMSGS, 'M');
+        DO_MODE_CHAR(AUDITORIUM, 'u');
+        DO_MODE_CHAR(SSLCHAN, 'S');
         DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
@@ -2704,6 +2962,10 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(NONOTICES, 'N');
+        DO_MODE_CHAR(NOAMSGS, 'M');
+        DO_MODE_CHAR(AUDITORIUM, 'u');
+        DO_MODE_CHAR(SSLCHAN, 'S');
         DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
@@ -2716,6 +2978,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             sprintf(int_buff, "%d", change->new_limit);
             mod_chanmode_append(&chbuf, 'l', int_buff);
         }
+        if(change->modes_set & MODE_ACCESS) {
+            sprintf(int_buff, "%d", change->new_access);
+            mod_chanmode_append(&chbuf, 'a', int_buff);
+        }
+        if (change->modes_set & MODE_ALTCHAN)
+            mod_chanmode_append(&chbuf, 'F', change->new_altchan);
+        if (change->modes_set & MODE_NOFLOOD)
+            mod_chanmode_append(&chbuf, 'f', change->new_noflood);
     }
     for (arg = 0; arg < change->argc; ++arg) {
         if (change->args[arg].mode & MODE_REMOVE)
@@ -2762,6 +3032,9 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(INVITEONLY, 'i');
         DO_MODE_CHAR(NOPRIVMSGS, 'n');
         DO_MODE_CHAR(LIMIT, 'l');
+        DO_MODE_CHAR(ACCESS, 'a');
+        DO_MODE_CHAR(ALTCHAN, 'F');
+        DO_MODE_CHAR(NOFLOOD, 'f');
         DO_MODE_CHAR(KEY, 'k');
         DO_MODE_CHAR(UPASS, 'U');
         DO_MODE_CHAR(APASS, 'A');
@@ -2769,6 +3042,10 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(NONOTICES, 'N');
+        DO_MODE_CHAR(NOAMSGS, 'M');
+        DO_MODE_CHAR(AUDITORIUM, 'u');
+        DO_MODE_CHAR(SSLCHAN, 'S');
         DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
     }
@@ -2785,9 +3062,16 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(NONOTICES, 'N');
+               DO_MODE_CHAR(NOAMSGS, 'M');
+        DO_MODE_CHAR(AUDITORIUM, 'u');
+        DO_MODE_CHAR(SSLCHAN, 'S');
         DO_MODE_CHAR(REGISTERED, 'z');
         DO_MODE_CHAR(LIMIT, 'l'), args_used += sprintf(args + args_used, " %d", change->new_limit);
         DO_MODE_CHAR(KEY, 'k'), args_used += sprintf(args + args_used, " %s", change->new_key);
+        DO_MODE_CHAR(ACCESS, 'a'), args_used += sprintf(args + args_used, " %d", change->new_access);
+        DO_MODE_CHAR(ALTCHAN, 'F'), args_used += sprintf(args + args_used, " %s", change->new_altchan);
+        DO_MODE_CHAR(NOFLOOD, 'f'), args_used += sprintf(args + args_used, " %s", change->new_noflood);
         DO_MODE_CHAR(UPASS, 'U'), args_used += sprintf(args + args_used, " %s", change->new_upass);
         DO_MODE_CHAR(APASS, 'A'), args_used += sprintf(args + args_used, " %s", change->new_apass);
 #undef DO_MODE_CHAR
@@ -2812,6 +3096,14 @@ clear_chanmode(struct chanNode *channel, const char *modes)
         case 't': cleared |= MODE_TOPICLIMIT; break;
         case 'i': cleared |= MODE_INVITEONLY; break;
         case 'n': cleared |= MODE_NOPRIVMSGS; break;
+        case 'F':
+            cleared |= MODE_ALTCHAN;
+            channel->altchan[0] = '\0';
+            break;
+        case 'f':
+            cleared |= MODE_NOFLOOD;
+            channel->noflood[0] = '\0';
+            break;
         case 'k':
             cleared |= MODE_KEY;
             channel->key[0] = '\0';
@@ -2828,11 +3120,19 @@ clear_chanmode(struct chanNode *channel, const char *modes)
             cleared |= MODE_LIMIT;
             channel->limit = 0;
             break;
+        case 'a':
+            cleared |= MODE_ACCESS;
+            channel->access = 0;
+            break;
         case 'b': cleared |= MODE_BAN; break;
         case 'D': cleared |= MODE_DELAYJOINS; break;
         case 'r': cleared |= MODE_REGONLY; break;
         case 'c': cleared |= MODE_NOCOLORS; break;
         case 'C': cleared |= MODE_NOCTCPS; break;
+        case 'M': cleared |= MODE_NOAMSGS; break;
+        case 'u': cleared |= MODE_AUDITORIUM; break;
+        case 'S': cleared |= MODE_SSLCHAN; break;
+        case 'N': cleared |= MODE_NONOTICES; break;
         case 'z': cleared |= MODE_REGISTERED; break;
         }
     }