Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / channel.c
index 4517ecec71b1884de5129a8a303e276b2ca3283a..6c974c62eaee51ed27e8b3988a19b3b132c97811 100644 (file)
@@ -25,6 +25,7 @@
 #include "ircd.h"
 #include "ircd_alloc.h"
 #include "ircd_chattr.h"
+#include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_snprintf.h"
 #include "ircd_string.h"
@@ -53,6 +54,9 @@
 
 struct Channel* GlobalChannelList = 0;
 
+static unsigned int membershipAllocCount;
+static struct Membership* membershipFreeList;
+
 static struct SLink *next_overlapped_ban(void);
 static int del_banid(struct Channel *, char *, int);
 void del_invite(struct Client *, struct Channel *);
@@ -94,7 +98,7 @@ struct Membership* find_member_link(struct Channel* chptr, const struct Client*
   assert(0 != chptr);
   
   /* Servers don't have member links */
-  if (IsServer(cptr))
+  if (IsServer(cptr)||IsMe(cptr))
      return 0;
   
   /* +k users are typically on a LOT of channels.  So we iterate over who
@@ -227,7 +231,7 @@ int sub1_from_channel(struct Channel* chptr)
     int i;
     for (i = 0; i <= HighestFd; i++)
     {
-      struct Client *acptr;
+      struct Client *acptr = 0;
       if ((acptr = LocalClientArray[i]) && acptr->listing &&
           acptr->listing->chptr == chptr)
       {
@@ -530,8 +534,15 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who,
   assert(0 != who);
 
   if (who->user) {
-    struct Membership* member = 
-            (struct Membership*) MyMalloc(sizeof(struct Membership));
+   
+    struct Membership* member = membershipFreeList;
+    if (member)
+      membershipFreeList = member->next_member;
+    else {
+      member = (struct Membership*) MyMalloc(sizeof(struct Membership));
+      ++membershipAllocCount;
+    }
+
     assert(0 != member);
     member->user         = who;
     member->channel      = chptr;
@@ -580,7 +591,9 @@ static int remove_member_from_channel(struct Membership* member)
     member->user->user->channel = member->next_channel;
 
   --member->user->user->joined;
-  MyFree(member);
+
+  member->next_member = membershipFreeList;
+  membershipFreeList = member;
 
   return sub1_from_channel(chptr);
 }
@@ -2816,13 +2829,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
                           addbuf, remstr, addstr,
                           mbuf->mb_channel->creationtime);
 
-#ifdef OPATH
-    if (mbuf->mb_dest & MODEBUF_DEST_LOG) {
-      write_log(OPATH, "%Tu %#C OPMODE %H %s%s%s%s%s%s\n", TStime(),
-               mbuf->mb_source, mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
+    if (mbuf->mb_dest & MODEBUF_DEST_LOG)
+      log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
+               "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
+               mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
                addbuf_i ? "+" : "", addbuf, remstr, addstr);
-    }
-#endif
 
     if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
       sendcmdto_channel_butserv(app_source, CMD_MODE, mbuf->mb_channel,
@@ -3174,6 +3185,8 @@ struct ParseState {
   unsigned int flags;
   unsigned int dir;
   unsigned int done;
+  unsigned int add;
+  unsigned int del;
   int args_used;
   int max_args;
   int numbans;
@@ -3248,7 +3261,7 @@ mode_parse_limit(struct ParseState *state, int *flag_p)
       state->chptr->mode.mode |= flag_p[0];
       state->chptr->mode.limit = t_limit;
     } else {
-      state->chptr->mode.mode &= flag_p[0];
+      state->chptr->mode.mode &= ~flag_p[0];
       state->chptr->mode.limit = 0;
     }
   }
@@ -3564,13 +3577,8 @@ mode_parse_client(struct ParseState *state, int *flag_p)
   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
     return;
 
-  if (state->parc <= 0) { /* warn if not enough args */
-    if (MyUser(state->sptr))
-      need_more_params(state->sptr, state->dir == MODE_ADD ?
-                      (flag_p[0] == MODE_CHANOP ? "MODE +o" : "MODE +v") :
-                      (flag_p[0] == MODE_CHANOP ? "MODE -o" : "MODE -v"));
+  if (state->parc <= 0) /* return if not enough args */
     return;
-  }
 
   t_str = state->parv[state->args_used++]; /* grab arg */
   state->parc--;
@@ -3682,10 +3690,6 @@ mode_process_clients(struct ParseState *state)
 static void
 mode_parse_mode(struct ParseState *state, int *flag_p)
 {
-  if ((state->dir == MODE_ADD &&  (flag_p[0] & state->chptr->mode.mode)) ||
-      (state->dir == MODE_DEL && !(flag_p[0] & state->chptr->mode.mode)))
-    return; /* no change */
-
   /* If they're not an oper, they can't change modes */
   if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
     send_notoper(state);
@@ -3695,36 +3699,25 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
   if (!state->mbuf)
     return;
 
-  modebuf_mode(state->mbuf, state->dir | flag_p[0]);
-
-  /* make +p and +s mutually exclusive */
-  if (state->dir == MODE_ADD && flag_p[0] & (MODE_SECRET | MODE_PRIVATE)) {
-    if (flag_p[0] == MODE_SECRET && (state->chptr->mode.mode & MODE_PRIVATE))
-      modebuf_mode(state->mbuf, MODE_DEL | MODE_PRIVATE);
-    else if (flag_p[0] == MODE_PRIVATE &&
-            (state->chptr->mode.mode & MODE_SECRET))
-      modebuf_mode(state->mbuf, MODE_DEL | MODE_SECRET);
-  }
-
-  if (state->flags & MODE_PARSE_SET) { /* set the flags */
-    if (state->dir == MODE_ADD) { /* add the mode to the channel */
-      state->chptr->mode.mode |= flag_p[0];
-
-      /* make +p and +s mutually exclusive */
-      if (state->dir == MODE_ADD && flag_p[0] & (MODE_SECRET | MODE_PRIVATE)) {
-       if (flag_p[0] == MODE_PRIVATE)
-         state->chptr->mode.mode &= ~MODE_SECRET;
-       else
-         state->chptr->mode.mode &= ~MODE_PRIVATE;
-      }
-    } else /* remove the mode from the channel */
-      state->chptr->mode.mode &= ~flag_p[0];
+  if (state->dir == MODE_ADD) {
+    state->add |= flag_p[0];
+    state->del &= ~flag_p[0];
+
+    if (flag_p[0] & MODE_SECRET) {
+      state->add &= ~MODE_PRIVATE;
+      state->del |= MODE_PRIVATE;
+    } else if (flag_p[0] & MODE_PRIVATE) {
+      state->add &= ~MODE_SECRET;
+      state->del |= MODE_SECRET;
+    }
+  } else {
+    state->add &= ~flag_p[0];
+    state->del |= flag_p[0];
   }
 
-  /* Clear out invite structures if we're removing invites */
-  if (state->flags & MODE_PARSE_SET && state->dir == MODE_DEL &&
-      flag_p[0] == MODE_INVITEONLY)
-    mode_invite_clear(state->chptr);
+  assert(0 == (state->add & state->del));
+  assert((MODE_SECRET | MODE_PRIVATE) !=
+        (state->add & (MODE_SECRET | MODE_PRIVATE)));
 }
 
 /*
@@ -3754,6 +3747,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
   };
   int i;
   int *flag_p;
+  unsigned int t_mode;
   char *modestr;
   struct ParseState state;
 
@@ -3772,6 +3766,8 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
   state.flags = flags;
   state.dir = MODE_ADD;
   state.done = 0;
+  state.add = 0;
+  state.del = 0;
   state.args_used = 0;
   state.max_args = MAXMODEPARAMS;
   state.numbans = 0;
@@ -3866,6 +3862,27 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
   if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
     return state.args_used; /* tell our parent how many args we gobbled */
 
+  t_mode = state.chptr->mode.mode;
+
+  if (state.del & t_mode) { /* delete any modes to be deleted... */
+    modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
+
+    t_mode &= ~state.del;
+  }
+  if (state.add & ~t_mode) { /* add any modes to be added... */
+    modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
+
+    t_mode |= state.add;
+  }
+
+  if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
+    if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
+       !(t_mode & MODE_INVITEONLY))
+      mode_invite_clear(state.chptr);
+
+    state.chptr->mode.mode = t_mode;
+  }
+
   if (state.flags & MODE_PARSE_WIPEOUT) {
     if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
       modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
@@ -3925,46 +3942,56 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
 
   assert(0 != jbuf);
 
-  if (chan) {
-    if (jbuf->jb_type == JOINBUF_TYPE_PART ||
-       jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
-      /* Send notification to channel */
-      if (!(flags & CHFL_ZOMBIE))
-       sendcmdto_channel_butserv(jbuf->jb_source, CMD_PART, chan,
-                                 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
-                                 ":%H" : "%H :%s", chan, jbuf->jb_comment);
-      else if (MyUser(jbuf->jb_source))
-       sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
-                     (flags & CHFL_BANNED || !jbuf->jb_comment) ?
-                     ":%H" : "%H :%s", chan, jbuf->jb_comment);
-      /* XXX: Shouldn't we send a PART here anyway? */
-      /* to users on the channel?  Why?  From their POV, the user isn't on
-       * the channel anymore anyway.  We don't send to servers until below,
-       * when we gang all the channel parts together.  Note that this is
-       * exactly the same logic, albeit somewhat more concise, as was in
-       * the original m_part.c */
-
-      if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
-         IsLocalChannel(chan->chname)) /* got to remove user here */
-       remove_user_from_channel(jbuf->jb_source, chan);
-    } else {
-      /* Add user to channel */
-      add_user_to_channel(chan, jbuf->jb_source, flags);
+  if (!chan) {
+    if (jbuf->jb_type == JOINBUF_TYPE_JOIN)
+      sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
 
-      /* Send the notification to the channel */
-      sendcmdto_channel_butserv(jbuf->jb_source, CMD_JOIN, chan, ":%H", chan);
+    return;
+  }
 
-      /* send an op, too, if needed */
-      if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE &&
-         !IsModelessChannel(chan->chname))
-       sendcmdto_channel_butserv(jbuf->jb_source, CMD_MODE, chan, "%H +o %C",
-                                 chan, jbuf->jb_source);
-    }
+  if (jbuf->jb_type == JOINBUF_TYPE_PART ||
+      jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
+    /* Send notification to channel */
+    if (!(flags & CHFL_ZOMBIE))
+      sendcmdto_channel_butserv(jbuf->jb_source, CMD_PART, chan,
+                               (flags & CHFL_BANNED || !jbuf->jb_comment) ?
+                               ":%H" : "%H :%s", chan, jbuf->jb_comment);
+    else if (MyUser(jbuf->jb_source))
+      sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
+                   (flags & CHFL_BANNED || !jbuf->jb_comment) ?
+                   ":%H" : "%H :%s", chan, jbuf->jb_comment);
+    /* XXX: Shouldn't we send a PART here anyway? */
+    /* to users on the channel?  Why?  From their POV, the user isn't on
+     * the channel anymore anyway.  We don't send to servers until below,
+     * when we gang all the channel parts together.  Note that this is
+     * exactly the same logic, albeit somewhat more concise, as was in
+     * the original m_part.c */
+
+    if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
+       IsLocalChannel(chan->chname)) /* got to remove user here */
+      remove_user_from_channel(jbuf->jb_source, chan);
+  } else {
+    /* Add user to channel */
+    add_user_to_channel(chan, jbuf->jb_source, flags);
 
-    if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || IsLocalChannel(chan->chname))
-      return; /* don't send to remote */
+    /* send notification to all servers */
+    if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !IsLocalChannel(chan->chname))
+      sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
+                           "%H %Tu", chan, chan->creationtime);
+
+    /* Send the notification to the channel */
+    sendcmdto_channel_butserv(jbuf->jb_source, CMD_JOIN, chan, ":%H", chan);
+
+    /* send an op, too, if needed */
+    if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE &&
+       !IsModelessChannel(chan->chname))
+      sendcmdto_channel_butserv(jbuf->jb_source, CMD_MODE, chan, "%H +o %C",
+                               chan, jbuf->jb_source);
   }
 
+  if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || IsLocalChannel(chan->chname))
+    return; /* don't send to remote */
+
   /* figure out if channel name will cause buffer to be overflowed */
   len = chan ? strlen(chan->chname) + 1 : 2;
   if (jbuf->jb_strlen + len > IRC_BUFSIZE)
@@ -3989,7 +4016,8 @@ joinbuf_flush(struct JoinBuf *jbuf)
   int chanlist_i = 0;
   int i;
 
-  if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL)
+  if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
+      jbuf->jb_type == JOINBUF_TYPE_JOIN)
     return 0; /* no joins to process */
 
   for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
@@ -4004,18 +4032,12 @@ joinbuf_flush(struct JoinBuf *jbuf)
   }
 
   jbuf->jb_count = 0; /* reset base counters */
-  jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_JOIN ||
-                     jbuf->jb_type == JOINBUF_TYPE_PART ?
+  jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
                      STARTJOINLEN : STARTCREATELEN) +
                     (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
 
   /* and send the appropriate command */
   switch (jbuf->jb_type) {
-  case JOINBUF_TYPE_JOIN:
-    sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
-                         "%s", chanlist);
-    break;
-
   case JOINBUF_TYPE_CREATE:
     sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
                          "%s %Tu", chanlist, jbuf->jb_create);