filter out invisible message content on mode +M check
[ircu2.10.12-pk.git] / ircd / channel.c
index 01d25072c95275dc46bbafdbd9bdcc908244764f..a467750ea39496f483da2cad7b247b18a138e4ae 100644 (file)
@@ -68,6 +68,8 @@ static unsigned int membershipAllocCount;
 static struct Membership* membershipFreeList;
 /** Freelist for struct Ban*'s */
 static struct Ban* free_bans;
+/** Freelist for struct MemberFlood*'s */
+static struct MemberFlood* free_MemberFlood;
 /** Number of ban structures allocated. */
 static size_t bans_alloc;
 /** Number of ban structures in use. */
@@ -489,6 +491,7 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who,
     member->user         = who;
     member->channel      = chptr;
     member->status       = flags;
+    member->flood        = NULL;
     SetOpLevel(member, oplevel);
 
     member->next_member  = chptr->members;
@@ -549,6 +552,8 @@ static int remove_member_from_channel(struct Membership* member)
 
   --(cli_user(member->user))->joined;
 
+  member->flood = NULL;
+  
   member->next_member = membershipFreeList;
   membershipFreeList = member;
 
@@ -870,6 +875,8 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'M';
   if (chptr->mode.mode & MODE_QUARANTINE)
     *mbuf++ = 'Q';
+  if (chptr->mode.mode & MODE_AUDITORIUM)
+    *mbuf++ = 'u';
   if (chptr->mode.limit) {
     *mbuf++ = 'l';
     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
@@ -889,6 +896,13 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     strcat(pbuf, chptr->mode.altchan);
     previous_parameter = 1;
   }
+  if (*chptr->mode.noflood) {
+    *mbuf++ = 'f';
+    if (previous_parameter)
+      strcat(pbuf, " ");
+    strcat(pbuf, chptr->mode.noflood);
+    previous_parameter = 1;
+  }
   if (*chptr->mode.key) {
     *mbuf++ = 'k';
     if (previous_parameter)
@@ -1577,7 +1591,7 @@ static int
 modebuf_flush_int(struct ModeBuf *mbuf, int all)
 {
   /* we only need the flags that don't take args right now */
-  static int flags[] = {
+  static ulong64 flags[] = {
 /*  MODE_CHANOP,       'o', */
 /*  MODE_VOICE,                'v', */
     MODE_PRIVATE,      'p',
@@ -1599,18 +1613,20 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_NOCTCP,    'C',
 /*  MODE_EXCEPTION, 'e', */
 /*  MODE_ALTCHAN,   'F', */
+/*  MODE_NOFLOOD,   'f', */
     MODE_ACCESS,    'a',
     MODE_NOAMSGS,   'M',
        MODE_NONOTICE,  'N',
        MODE_QUARANTINE,  'Q',
+       MODE_AUDITORIUM,  'u',
     0x0, 0x0
   };
-  static int local_flags[] = {
+  static ulong64 local_flags[] = {
     MODE_WASDELJOINS,   'd',
     0x0, 0x0
   };
   int i;
-  int *flag_p;
+  ulong64 *flag_p;
 
   struct Client *app_source; /* where the MODE appears to come from */
 
@@ -1633,8 +1649,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
 
   char limitbuf[20],accessbuf[20]; /* convert limits to strings */
 
-  unsigned int limitdel = MODE_LIMIT;
-  unsigned int accessdel = MODE_ACCESS;
+  ulong64 limitdel = MODE_LIMIT;
+  ulong64 accessdel = MODE_ACCESS;
 
   assert(0 != mbuf);
 
@@ -1694,14 +1710,14 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
        totalbuflen -= IRCD_MAX(9, tmp) + 1;
       }
-    } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN)) {
+    } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD)) {
       tmp = strlen(MB_STRING(mbuf, i));
 
       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
        MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
       else {
        char mode_char;
-       switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN))
+       switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
        {
          case MODE_APASS:
            mode_char = 'A';
@@ -1715,6 +1731,9 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       case MODE_ALTCHAN:
                mode_char = 'F';
                break;
+         case MODE_NOFLOOD:
+               mode_char = 'f';
+               break;
          default:
            mode_char = 'b';
            break;
@@ -1817,6 +1836,9 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        
          else if (MB_TYPE(mbuf, i) & MODE_ALTCHAN)
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
+         else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_NOFLOOD)) ==
+              (MODE_ADD | MODE_NOFLOOD))
+       build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
     }
 
     /* send the messages off to their destination */
@@ -1905,7 +1927,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
 
       /* deal with modes that take strings */
-      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN))
+      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
 
       /*
@@ -2032,7 +2054,7 @@ modebuf_init(struct ModeBuf *mbuf, struct Client *source,
  * @param mode         MODE_ADD or MODE_DEL OR'd with MODE_PRIVATE etc.
  */
 void
-modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
+modebuf_mode(struct ModeBuf *mbuf, ulong64 mode)
 {
   assert(0 != mbuf);
   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
@@ -2040,7 +2062,8 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
           MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
            MODE_DELJOINS | MODE_WASDELJOINS | MODE_REGISTERED | MODE_PERSIST |
-           MODE_NOCOLOUR | MODE_NOCTCP | MODE_NOAMSGS | MODE_NONOTICE | MODE_QUARANTINE);
+           MODE_NOCOLOUR | MODE_NOCTCP | MODE_NOAMSGS | MODE_NONOTICE | 
+                  MODE_QUARANTINE | MODE_AUDITORIUM);
 
   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
     return;
@@ -2054,10 +2077,10 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
   }
 }
 
-/** Append a mode that takes an int argument to the modebuf
+/** Append a mode that takes an ulong64 argument to the modebuf
  *
  * This routine adds a mode to be added or deleted that takes a unsigned
- * int parameter; mode may *only* be the relevant mode flag ORed with one
+ * ulong64 parameter; mode may *only* be the relevant mode flag ORed with one
  * of MODE_ADD or MODE_DEL
  *
  * @param mbuf         The mode buffer to append to.
@@ -2065,7 +2088,7 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
  * @param uint         The argument to the mode.
  */
 void
-modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
+modebuf_mode_uint(struct ModeBuf *mbuf, ulong64 mode, unsigned int uint)
 {
   assert(0 != mbuf);
   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
@@ -2098,7 +2121,7 @@ modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
  * @param free         If the string should be free'd later.
  */
 void
-modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
+modebuf_mode_string(struct ModeBuf *mbuf, ulong64 mode, char *string,
                    int free)
 {
   assert(0 != mbuf);
@@ -2124,7 +2147,7 @@ modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
  * @param oplevel       The oplevel the user had or will have
  */
 void
-modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
+modebuf_mode_client(struct ModeBuf *mbuf, ulong64 mode,
                    struct Client *client, int oplevel)
 {
   assert(0 != mbuf);
@@ -2192,7 +2215,7 @@ modebuf_flush(struct ModeBuf *mbuf)
 void
 modebuf_extract(struct ModeBuf *mbuf, char *buf)
 {
-  static int flags[] = {
+  static ulong64 flags[] = {
 /*  MODE_CHANOP,       'o', */
 /*  MODE_VOICE,                'v', */
     MODE_PRIVATE,      'p',
@@ -2218,13 +2241,15 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
        MODE_QUARANTINE,  'Q',
        MODE_ALTCHAN,    'F',
        MODE_ACCESS,    'a',
+       MODE_AUDITORIUM, 'u',
+       MODE_NOFLOOD,   'f',
     0x0, 0x0
   };
-  unsigned int add;
+  ulong64 add;
   int i, bufpos = 0, len;
-  int *flag_p;
+  ulong64 *flag_p;
   char *key = 0, limitbuf[20], accessbuf[20];
-  char *apass = 0, *upass = 0, *altchan = 0;
+  char *apass = 0, *upass = 0, *altchan = 0, *noflood = 0;
 
   assert(0 != mbuf);
   assert(0 != buf);
@@ -2235,7 +2260,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
 
   for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
     if (MB_TYPE(mbuf, i) & MODE_ADD) {
-      add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_ACCESS);
+      add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_ACCESS | MODE_NOFLOOD);
 
       if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
        key = MB_STRING(mbuf, i);
@@ -2249,6 +2274,8 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
        apass = MB_STRING(mbuf, i);
       else if (MB_TYPE(mbuf, i) & MODE_ALTCHAN)
        altchan = MB_STRING(mbuf, i);
+         else if (MB_TYPE(mbuf, i) & MODE_NOFLOOD)
+       noflood = MB_STRING(mbuf, i);
     }
   }
 
@@ -2274,6 +2301,8 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
       build_string(buf, &bufpos, apass, 0, ' ');
        else if (buf[i] == 'F')
       build_string(buf, &bufpos, altchan, 0, ' ');
+       else if (buf[i] == 'f')
+      build_string(buf, &bufpos, noflood, 0, ' ');
   }
 
   buf[bufpos] = '\0';
@@ -2325,6 +2354,7 @@ mode_invite_clear(struct Channel *chan)
 #define DONE_EXCEPTIONLIST 0x400 /**< We've sent the exception list */
 #define DONE_ALTCHAN   0x800   /**< We've set the altchan */
 #define DONE_ACCESS    0x1000  /**< We've set the access */
+#define DONE_NOFLOOD   0x2000  /**< We've set the noflood options */
 
 struct ParseState {
   struct ModeBuf *mbuf;
@@ -2334,17 +2364,17 @@ struct ParseState {
   struct Membership *member;
   int parc;
   char **parv;
-  unsigned int flags;
-  unsigned int dir;
-  unsigned int done;
-  unsigned int add;
-  unsigned int del;
+  ulong64 flags;
+  ulong64 dir;
+  ulong64 done;
+  ulong64 add;
+  ulong64 del;
   int args_used;
   int max_args;
   int numbans;
   struct Ban banlist[MAXPARA];
   struct {
-    unsigned int flag;
+    ulong64 flag;
     unsigned short oplevel;
     struct Client *client;
   } cli_change[MAXPARA];
@@ -2375,7 +2405,7 @@ send_notoper(struct ParseState *state)
  * @param flag_p       ?
  */
 static void
-mode_parse_limit(struct ParseState *state, int *flag_p)
+mode_parse_limit(struct ParseState *state, ulong64 *flag_p)
 {
   unsigned int t_limit;
 
@@ -2440,7 +2470,7 @@ mode_parse_limit(struct ParseState *state, int *flag_p)
 
 
 static void
-mode_parse_access(struct ParseState *state, int *flag_p)
+mode_parse_access(struct ParseState *state, ulong64 *flag_p)
 {
   unsigned int t_access;
 
@@ -2513,7 +2543,7 @@ mode_parse_access(struct ParseState *state, int *flag_p)
 
 
 static void
-mode_parse_altchan(struct ParseState *state, int *flag_p)
+mode_parse_altchan(struct ParseState *state, ulong64 *flag_p)
 {
   char *t_str;
 
@@ -2597,7 +2627,121 @@ mode_parse_altchan(struct ParseState *state, int *flag_p)
 }
 
 static void
-mode_parse_quarantine(struct ParseState *state, int *flag_p)
+mode_parse_noflood(struct ParseState *state, ulong64 *flag_p)
+{
+  char *t_str;
+  char *tmp;
+  unsigned int count = 0, time = 0, flags = 0;
+
+  if (state->dir == MODE_ADD) { /* convert arg only if adding noflood */
+    if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
+      return;
+    
+    if (state->parc <= 0) { /* warn if not enough args */
+      if (MyUser(state->sptr))
+       need_more_params(state->sptr, "MODE +f");
+      return;
+    }
+
+    t_str = state->parv[state->args_used++]; /* grab arg */
+    state->parc--;
+    state->max_args--;
+
+    tmp = t_str;
+    
+    if(tmp[0] == '!') {
+        if(state->flags & MODE_PARSE_FORCE) flags |= FLFL_NOFLOOD;
+        else t_str++; //simply ignore if it's not an opmode
+        tmp++;
+    }
+    if(tmp[0] == '+' || tmp[0] == '@') {
+        if(tmp[0] == '+') flags |= FLFL_VOICE;
+        if(tmp[0] == '@') flags |= FLFL_CHANOP;
+        tmp++;
+    }
+    char *p;
+    for(p = tmp; p[0]; p++) {
+      if(p[0] == ':') {
+        char tmpchar = p[0];
+        p[0] = '\0';
+        count = strtoul(tmp,0,10);
+        p[0] = tmpchar;
+        p++;
+        time = strtoul(p,0,10);
+        break;
+      }
+    }
+    if(count <= 0 || time <= 0 || count > 100 || time > 600) return;
+    
+    if (!(state->flags & MODE_PARSE_WIPEOUT) &&
+       (!t_str || t_str == state->chptr->mode.noflood))
+      return;
+  } else
+    t_str = state->chptr->mode.noflood;
+
+  /* If they're not an oper, they can't change modes */
+  if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
+    send_notoper(state);
+    return;
+  }
+
+  /* Can't remove a noflood that's not there */
+  if (state->dir == MODE_DEL && !*state->chptr->mode.noflood)
+    return;
+    
+  /* Skip if this is a burst and a lower noflood than this is set already */
+  if ((state->flags & MODE_PARSE_BURST) &&
+      *(state->chptr->mode.noflood))
+    return;
+
+  if (state->done & DONE_NOFLOOD) /* allow noflood to be set only once */
+    return;
+  state->done |= DONE_NOFLOOD;
+
+  if (!state->mbuf)
+    return;
+
+  if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
+      !ircd_strcmp(state->chptr->mode.noflood, t_str))
+    return; /* no change */
+
+  if (state->flags & MODE_PARSE_BOUNCE) {
+    if (*state->chptr->mode.noflood) /* reset old noflood */
+      modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0], state->chptr->mode.noflood, 0);
+    else /* remove new bogus noflood */
+      modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
+  } else /* send new noflood */
+    modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
+
+  if (state->flags & MODE_PARSE_SET) {
+    if (state->dir == MODE_DEL) /* remove the old noflood */
+      *state->chptr->mode.noflood = '\0';
+    else
+      ircd_strncpy(state->chptr->mode.noflood, t_str, CHANNELLEN);
+  }
+  
+  if (state->dir == MODE_ADD) {
+    unsigned int noflood_value = time;
+    noflood_value <<= 10;
+    noflood_value |= count;
+    noflood_value <<= 3;
+    noflood_value |= flags;
+    state->chptr->mode.noflood_value = noflood_value;
+  } else {
+    //removed the mode so free all flood objects
+    struct Membership *member;
+    for(member = state->chptr->members; member; member = member->next_member) {
+        struct MemberFlood *floodnode;
+        for(floodnode = member->flood; floodnode; floodnode = floodnode->next_memberflood) {} //simply walk to the end
+        floodnode->next_memberflood = free_MemberFlood;
+        free_MemberFlood  = floodnode;
+        member->flood = NULL;
+    }
+  }
+}
+
+static void
+mode_parse_quarantine(struct ParseState *state, ulong64 *flag_p)
 {
     
 }
@@ -2643,7 +2787,7 @@ is_clean_key(struct ParseState *state, char *s, char *command)
  * Helper function to convert keys
  */
 static void
-mode_parse_key(struct ParseState *state, int *flag_p)
+mode_parse_key(struct ParseState *state, ulong64 *flag_p)
 {
   char *t_str;
 
@@ -2730,7 +2874,7 @@ mode_parse_key(struct ParseState *state, int *flag_p)
  * Helper function to convert user passes
  */
 static void
-mode_parse_upass(struct ParseState *state, int *flag_p)
+mode_parse_upass(struct ParseState *state, ulong64 *flag_p)
 {
   char *t_str;
 
@@ -2849,7 +2993,7 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
  * Helper function to convert admin passes
  */
 static void
-mode_parse_apass(struct ParseState *state, int *flag_p)
+mode_parse_apass(struct ParseState *state, ulong64 *flag_p)
 {
   struct Membership *memb;
   char *t_str;
@@ -3102,7 +3246,7 @@ int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free)
 /* Removes MODE_WASDELJOINS in a channel.
  * Reveals all hidden users.
  */
-static void reveal_hidden_chan_users(struct ParseState *state, int *flag_p) {
+static void reveal_hidden_chan_users(struct ParseState *state, ulong64 *flag_p) {
     struct Membership *member;
 
     /* If the channel is not +d, do nothing. */
@@ -3116,11 +3260,33 @@ static void reveal_hidden_chan_users(struct ParseState *state, int *flag_p) {
     }
 }
 
+/* Handle MODE_AUDITORIUM changes 
+ * set Delayed for all hidden users on MODE_DEL
+ * part all nonoped users on MODE_ADD
+ */
+static void audit_chan_users(struct ParseState *state, ulong64 *flag_p) {
+       struct Membership *member;
+       if (state->dir == MODE_ADD) {
+               for(member = state->chptr->members; member; member = member->next_member) {
+                       if(!IsChanOp(member) && !HasVoice(member)) {
+                               sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "mode +u set.");
+                       }
+               }
+       } else {
+               for(member = state->chptr->members; member; member = member->next_member) {
+                       if(!IsChanOp(member) && !HasVoice(member)) {
+                               sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, SKIP_OPS, ":%H", member->channel);
+                       }
+               }
+       }
+}
+
+
 /*
  * Helper function to convert bans
  */
 static void
-mode_parse_ban(struct ParseState *state, int *flag_p)
+mode_parse_ban(struct ParseState *state, ulong64 *flag_p)
 {
   char *t_str, *s;
   struct Ban *ban, *newban;
@@ -3284,7 +3450,7 @@ mode_process_bans(struct ParseState *state)
  * Helper function to process client changes
  */
 static void
-mode_parse_client(struct ParseState *state, int *flag_p)
+mode_parse_client(struct ParseState *state, ulong64 *flag_p)
 {
   char *t_str;
   char *colon;
@@ -3469,7 +3635,9 @@ mode_process_clients(struct ParseState *state)
       else
         SetOpLevel(member, OpLevel(state->member) + 1);
     }
-
+       
+       int user_visible = (member->status & CHFL_VOICED_OR_OPPED);
+       
     /* actually effect the change */
     if (state->flags & MODE_PARSE_SET) {
       if (state->cli_change[i].flag & MODE_ADD) {
@@ -3488,6 +3656,35 @@ mode_process_clients(struct ParseState *state)
     modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
                        state->cli_change[i].client,
                         state->cli_change[i].oplevel);
+       
+       if((member->channel->mode.mode & MODE_AUDITORIUM)) {
+               //join or part the user
+               if((member->status & CHFL_VOICED_OR_OPPED) && !user_visible) {
+                       sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, SKIP_OPS, ":%H", member->channel);
+               } else if(!(member->status & CHFL_VOICED_OR_OPPED) && user_visible) {
+                       sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "user deoped/devoiced on a +u channel.");
+               }
+               if(MyUser(member->user) && (state->cli_change[i].flag & MODE_CHANOP)) {
+                       //do_names(member->user, member->channel, NAMES_ALL|NAMES_EON|((member->status & MODE_CHANOP) ? 0 : NAMES_OPS));
+                       //this is not working for all users :(  so we have to send join/part events
+                       struct Membership *member2;
+                       if (state->cli_change[i].flag & MODE_ADD) {
+                               //JOIN events
+                               for(member2 = state->chptr->members; member2; member2 = member2->next_member) {
+                                       if(!IsChanOp(member2) && !HasVoice(member2)) {
+                                               sendcmdto_one(member2->user, CMD_JOIN, member->user, ":%H", member->channel);
+                                       }
+                               }
+                       } else {
+                               //PART events
+                               for(member2 = state->chptr->members; member2; member2 = member2->next_member) {
+                                       if(!IsChanOp(member2) && !HasVoice(member2) && member != member2) {
+                                               sendcmdto_one(member2->user, CMD_PART, member->user, "%H :%s", member->channel, "invisible user on +u channel.");
+                                       }
+                               }
+                       }
+               }
+       }
   } /* for (i = 0; state->cli_change[i].flags; i++) */
 }
 
@@ -3495,7 +3692,7 @@ mode_process_clients(struct ParseState *state)
  * Helper function to process the simple modes
  */
 static void
-mode_parse_mode(struct ParseState *state, int *flag_p)
+mode_parse_mode(struct ParseState *state, ulong64 *flag_p)
 {
   /* If they're not an oper, they can't change modes */
   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
@@ -3509,6 +3706,9 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
   /* Local users are not permitted to change registration status */
   if (flag_p[0] == MODE_REGISTERED && !(state->flags & MODE_PARSE_FORCE) && MyUser(state->sptr))
     return;
+       
+  if(flag_p[0] == MODE_AUDITORIUM)
+    audit_chan_users(state, flag_p);
 
   if (state->dir == MODE_ADD) {
     state->add |= flag_p[0];
@@ -3550,7 +3750,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
           struct Channel *chptr, int parc, char *parv[], unsigned int flags,
           struct Membership* member)
 {
-  static int chan_flags[] = {
+  static ulong64 chan_flags[] = {
     MODE_CHANOP,       'o',
     MODE_VOICE,                'v',
     MODE_PRIVATE,      'p',
@@ -3577,13 +3777,15 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
        MODE_QUARANTINE,      'Q',
        MODE_ALTCHAN,        'F',
     MODE_ACCESS,        'a',
+       MODE_AUDITORIUM,    'u',
+       MODE_NOFLOOD,       'f',
     MODE_ADD,          '+',
     MODE_DEL,          '-',
     0x0, 0x0
   };
   int i;
-  int *flag_p;
-  unsigned int t_mode;
+  ulong64 *flag_p;
+  ulong64 t_mode;
   char *modestr;
   struct ParseState state;
 
@@ -3647,6 +3849,9 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
        break;
          case 'F':
        mode_parse_altchan(&state, flag_p);
+       break;
+         case 'f':
+       mode_parse_noflood(&state, flag_p);
        break;
       case 'Q':
       if(IsNetServ(state.sptr) && IsSecurityServ(state.sptr))
@@ -3794,6 +3999,9 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     if (state.chptr->mode.altchan && !(state.done & DONE_ALTCHAN))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_ALTCHAN,
                        state.chptr->mode.altchan, 0);
+       if (state.chptr->mode.noflood && !(state.done & DONE_NOFLOOD))
+      modebuf_mode_string(state.mbuf, MODE_DEL | MODE_NOFLOOD,
+                       state.chptr->mode.noflood, 0);
     if (*state.chptr->mode.key && !(state.done & DONE_KEY_DEL))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
                          state.chptr->mode.key, 0);
@@ -3871,7 +4079,12 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
     SetUserParting(member);
 
     /* Send notification to channel */
-    if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED)))
+       if((chan->mode.mode & MODE_AUDITORIUM) && !(member->status & CHFL_VOICED_OR_OPPED)) {
+         //send part to ops only
+         sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, SKIP_NONOPS, "%H :%s", chan, jbuf->jb_comment);
+         if(MyUser(jbuf->jb_source))
+                 sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source, "%H :%s", chan, jbuf->jb_comment);
+    } else if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED)))
       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, 0,
                                (flags & CHFL_BANNED || !jbuf->jb_comment) ?
                                ":%H" : "%H :%s", chan, jbuf->jb_comment);
@@ -3909,8 +4122,14 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
                            "%H %Tu %i", chan, chan->creationtime, 0);
          }
        }
-
-    if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED)) && !((flags & CHFL_INVISIBLE) && !(flags & CHFL_VOICED_OR_OPPED))) {
+       
+       if((chan->mode.mode & MODE_AUDITORIUM) && !(flags & CHFL_VOICED_OR_OPPED)) {
+               //we have to send this JOIN event to ops only...                  
+               sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, SKIP_NONOPS, "%H", chan);
+               if(MyUser(jbuf->jb_source))
+                 sendcmdto_one(jbuf->jb_source, CMD_JOIN, jbuf->jb_source, ":%H", chan);
+       }
+    else if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED)) && !((flags & CHFL_INVISIBLE) && !(flags & CHFL_VOICED_OR_OPPED))) {
       /* Send the notification to the channel */
       sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, 0, "%H", chan);
 
@@ -4023,6 +4242,16 @@ void CheckDelayedJoins(struct Channel *chan)
   }
 }
 
+void CheckEnableDelayedJoins(struct Channel *chan) {
+  if (!(chan->mode.mode & MODE_WASDELJOINS) && find_delayed_joins(chan)) {
+       chan->mode.mode |= MODE_WASDELJOINS;
+       sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan, NULL, 0,
+                                     "%H +d", chan);
+    sendcmdto_channel_servers_butone(&me, CMD_MODE, chan, NULL, 0,
+                                     "%H +d", chan);
+  }
+}
+
 /* checks whether a channel is nonpersistent with no users and deletes it
  * returns 1 if deleted, otherwise 0
  */
@@ -4076,7 +4305,7 @@ void RevealDelayedJoinIfNeeded(struct Client *sptr, struct Channel *chptr)
 int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg)
 {
   int amsg_time;
-
+  
   /* First on every message we check whether the mechanism is enabled.
    * If it is enabled, we check:
    *  - whether the channel has MODE_NOAMSGS
@@ -4090,6 +4319,59 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg)
    */
   amsg_time = feature_int(FEAT_NOAMSG_TIME);
   if(amsg_time > 0) {
+    /* first of all strip the message (filter out invisible content) */
+    char *stripped_message = MyMalloc(BUFSIZE + 1);
+    strcpy(stripped_message, msg);
+    char p = stripped_message[0];
+    int p_pos = 0;
+    int is_visible = 1, is_ccode = 0; j = 0;
+    char codes[5];
+    for(int i = 0; p != '\n'; p = stripped_message[++i]) {
+      if(c == 3) {
+        j = 0;
+        is_ccode = 1;
+      } else if(is_ccode) {
+        if((c >= 48 && c <= 57) || c == 44) {
+          if(ccode == 1) {
+            if(c == 44) {
+              ccode = 2;
+              codes[j++] = 0;
+              j = 0;
+            } else
+              codes[j++] = c;
+          } else {
+            //compare
+            if(c != codes[j++]) {
+             ccode = 3;
+            }
+          }
+        } else {
+          //END of color code...
+          is_ccode = 0;
+          if(ccode != 1 && codes[j] != 0) ccode = 3;
+          if(ccode == 1) {
+            codes[j] = 0;
+            for(int k = 0; k < j-1; k++) {
+              if(codes[k] != 48) {
+                is_visible = 1;
+                goto normalchar;
+              }
+            }
+            is_visible = 0;
+          } else if(ccode == 2) {
+            is_visible = 0;
+          } else if(ccode == 3) {
+            is_visible = 1;
+            goto normalchar;
+          }
+        }
+      } else {
+        :normalchar
+        if(is_visible)
+          stripped_message[p_pos++] = p;
+      }
+    }
+    stripped_message[p_pos++] = 0;
     /* Allocate a new buffer if there is none, yet. */
     if(!cli_user(cptr)->lastmsg) {
       cli_user(cptr)->lastmsg = MyMalloc(BUFSIZE + 1);
@@ -4097,17 +4379,74 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg)
     }
     if((chptr->mode.mode & MODE_NOAMSGS) &&
        ((cli_user(cptr)->lastmsg_time + amsg_time) >= CurrentTime) &&
-       (strcmp(cli_user(cptr)->lastmsg, msg) == 0)) {
+       (strcmp(cli_user(cptr)->lastmsg, stripped_message) == 0)) {
       cli_user(cptr)->lastmsg_time = CurrentTime;
       cli_user(cptr)->lastmsg_num++;
+      MyFree(stripped_message);
       if(cli_user(cptr)->lastmsg_num >= feature_int(FEAT_NOAMSG_NUM)) return 1;
       else return 0;
     }
     /* Message did not match so update the data. */
     cli_user(cptr)->lastmsg_time = CurrentTime;
     cli_user(cptr)->lastmsg_num = 0;
-    strcpy(cli_user(cptr)->lastmsg, msg);
+    strcpy(cli_user(cptr)->lastmsg, stripped_message);
+    MyFree(stripped_message);
   }
   return 0;
 }
 
+/** Extended flood check.
+ * The channelmode MODE_NOFLOOD prevents users from flooding the channel.
+ * 
+ * This function returns 0 if the message may pass and 1 if the message should
+ * be blocked.
+ * For this function to work properly it must be called on every PRIVMSG which
+ * is sent by any user.
+ *
+ * --pk910 2011/7/1
+ */
+int ext_noflood_block(struct Client *cptr, struct Channel *chptr) {
+  if(!chptr->mode.noflood) return 0;
+  struct Membership *member = find_member_link(chptr, cptr);
+  if(!member) return 0; //TODO: we've no check for -n channels implemented, yet
+  //check if this user is really affected by +f
+  unsigned int flags = (chptr->mode.noflood_value & 0x00000007);        //0000 0000 0000 0000 0000 0000 0000 0111 = 0x00000007 >> 0
+  unsigned int count = (chptr->mode.noflood_value & 0x00001ff8) >> 3;   //0000 0000 0000 0000 0001 1111 1111 1000 = 0x00001ff8 >> 3
+  int time           = (chptr->mode.noflood_value & 0x07ffe000) >> 13;  //0000 0111 1111 1111 1110 0000 0000 0000 = 0x07ffe000 >> 13
+  if(!(flags & FLFL_NOFLOOD) && HasPriv(cptr, PRIV_FLOOD))
+    return 0;
+  if(!(flags & FLFL_CHANOP) && (member->status & CHFL_CHANOP)) 
+    return 0;
+  if(!(flags & (FLFL_CHANOP | FLFL_VOICE)) && (member->status & CHFL_VOICE)) 
+    return 0;
+  int floodcount = 0;
+  struct MemberFlood *floodnode, *prev_floodnode;
+  for (floodnode = member->flood; floodnode; floodnode = floodnode->next_memberflood) {
+    if(floodnode->time + time > CurrentTime) {
+      if(floodcount == 0 && floodnode != member->flood) {
+        //free all before
+        prev_floodnode->next_memberflood = free_MemberFlood;
+        free_MemberFlood  = prev_floodnode;
+        member->flood = floodnode;
+      }
+      floodcount++;
+    }
+    prev_floodnode = floodnode;
+  }
+  Debug((DEBUG_INFO, "floodcount: %i", floodcount));
+  if(floodcount >= count) return 1; //blocked!
+  //add a new floodnode :)
+  if(free_MemberFlood) {
+    floodnode = free_MemberFlood;
+    free_MemberFlood = floodnode->next_memberflood;
+  } else
+    floodnode = (struct MemberFlood*) MyMalloc(sizeof(struct MemberFlood));
+  floodnode->time = CurrentTime;
+  floodnode->next_memberflood = NULL;
+  if(floodcount > 0)
+    prev_floodnode->next_memberflood = floodnode;
+  else
+    member->flood = floodnode;
+  return 0;
+}
+