]> jfr.im git - irc/quakenet/snircd.git/blobdiff - ircd/channel.c
add sbounce from asuka into snircd tree
[irc/quakenet/snircd.git] / ircd / channel.c
index 7015b57deafb515704babe7e4b5262d718564137..b268b632d37f5f26abdf45728a44eb01062b2245 100644 (file)
@@ -19,7 +19,7 @@
  */
 /** @file
  * @brief Channel management and maintenance
- * @version $Id: channel.c,v 1.155.2.12 2006/01/07 01:08:29 entrope Exp $
+ * @version $Id: channel.c,v 1.155.2.15 2006/04/04 23:09:47 entrope Exp $
  */
 #include "config.h"
 
@@ -383,7 +383,7 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
   ircd_ntoa_r(iphost, &cli_ip(cptr));
   if (!IsAccount(cptr))
     sr = NULL;
-  else if (HasHiddenHost(cptr))
+  else if (HasHiddenHost(cptr) || HasSetHost(cptr))
     sr = cli_user(cptr)->realhost;
   else
   {
@@ -743,7 +743,7 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int r
   /*
    * Servers can always speak on channels.
    */
-  if (IsServer(cptr))
+  if (IsServer(cptr) || IsXtraOp(cptr))
     return 1;
 
   member = find_channel_member(cptr, chptr);
@@ -828,6 +828,14 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'n';
   if (chptr->mode.mode & MODE_REGONLY)
     *mbuf++ = 'r';
+  if (chptr->mode.mode & MODE_NOCOLOUR)
+    *mbuf++ = 'c';
+  if (chptr->mode.mode & MODE_NOCTCP)
+    *mbuf++ = 'C';
+  if (chptr->mode.mode & MODE_NONOTICE)
+    *mbuf++ = 'N';
+  if (chptr->mode.mode & MODE_NOQUITPARTS)
+    *mbuf++ = 'u';
   if (chptr->mode.mode & MODE_DELJOINS)
     *mbuf++ = 'D';
   else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS))
@@ -842,7 +850,7 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'k';
     if (previous_parameter)
       strcat(pbuf, " ");
-    if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
+    if (is_chan_op(cptr, chptr) || IsServer(cptr) || IsOper(cptr)) {
       strcat(pbuf, chptr->mode.key);
     } else
       strcat(pbuf, "*");
@@ -1293,6 +1301,53 @@ struct Channel *get_channel(struct Client *cptr, char *chname, ChannelGetType fl
   return chptr;
 }
 
+int SetAutoChanModes(struct Channel *chptr)
+{
+  static int chan_flags[] = {
+    MODE_PRIVATE,       'p',
+    MODE_SECRET,        's',
+    MODE_MODERATED,     'm',
+    MODE_TOPICLIMIT,    't',
+    MODE_INVITEONLY,    'i',
+    MODE_NOPRIVMSGS,    'n',
+    MODE_REGONLY,       'r',
+    MODE_NOCOLOUR,      'c',
+    MODE_NOCTCP,        'C',
+    MODE_NONOTICE,      'N',
+    MODE_DELJOINS,      'D',
+    MODE_NOQUITPARTS,   'u'
+  };
+
+  unsigned int *flag_p;
+  unsigned int t_mode;
+  const char *modestr;
+
+  t_mode = 0;
+
+  assert(0 != chptr);
+
+  if (!feature_bool(FEAT_AUTOCHANMODES) || !feature_str(FEAT_AUTOCHANMODES_LIST) || strlen(feature_str(FEAT_AUTOCHANMODES_LIST)) <= 1)
+    return(-1);
+
+  modestr = feature_str(FEAT_AUTOCHANMODES_LIST);
+
+  for (; *modestr; modestr++) {
+    for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
+      if (flag_p[1] == *modestr)
+        break;
+
+    if (!flag_p[0]) /* didn't find it */
+      continue;
+
+    t_mode |= flag_p[0]; 
+
+  } /* for (; *modestr; modestr++) { */
+
+  if (t_mode != 0)
+    chptr->mode.mode = t_mode;
+  return(0);
+}
+
 /** invite a user to a channel.
  *
  * Adds an invite for a user to a channel.  Limits the number of invites
@@ -1542,6 +1597,10 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_LIMIT,                'l',
 /*  MODE_APASS,                'A', */
 /*  MODE_UPASS,                'U', */
+    MODE_NOQUITPARTS,   'u',
+    MODE_NOCOLOUR,      'c',
+    MODE_NOCTCP,        'C',
+    MODE_NONOTICE,     'N',
     0x0, 0x0
   };
   static int local_flags[] = {
@@ -1805,7 +1864,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        strptr_i = &remstr_i;
       }
 
-      /* if we're changing oplevels we know the oplevel, pass it on */
+      /* if we're changing oplevels and we know the oplevel, pass it on */
       if (mbuf->mb_channel->mode.apass[0]
           && (MB_TYPE(mbuf, i) & MODE_CHANOP)
           && MB_OPLEVEL(mbuf, i) < MAXOPLEVEL)
@@ -1849,9 +1908,9 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
                            addbuf, remstr, addstr);
     } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
       /*
-       * If HACK2 was set, we're bouncing; we send the MODE back to the
-       * connection we got it from with the senses reversed and a TS of 0;
-       * origin is us
+       * If HACK2 was set, we're bouncing; we send the MODE back to
+       * the connection we got it from with the senses reversed and
+       * the proper TS; origin is us
        */
       sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
                    mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
@@ -1859,21 +1918,14 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
                    mbuf->mb_channel->creationtime);
     } else {
       /*
-       * We're propagating a normal MODE command to the rest of the network;
-       * we send the actual channel TS unless this is a HACK3 or a HACK4
+       * We're propagating a normal (or HACK3 or HACK4) MODE command
+       * to the rest of the network.  We send the actual channel TS.
        */
-      if (IsServer(mbuf->mb_source) || IsMe(mbuf->mb_source))
-       sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
-                             "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
-                             rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
-                             addbuf, remstr, addstr,
-                             (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
-                             mbuf->mb_channel->creationtime);
-      else
-       sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
-                             "%H %s%s%s%s%s%s", mbuf->mb_channel,
-                             rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
-                             addbuf, remstr, addstr);
+      sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
+                            "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
+                            rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
+                            addbuf, remstr, addstr,
+                            mbuf->mb_channel->creationtime);
     }
   }
 
@@ -1957,8 +2009,9 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
 
   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
-          MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
-           MODE_DELJOINS | MODE_WASDELJOINS);
+           MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
+           MODE_DELJOINS | MODE_WASDELJOINS | MODE_NOQUITPARTS  | MODE_NOCOLOUR |
+           MODE_NOCTCP | MODE_NONOTICE);
 
   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
     return;
@@ -2105,6 +2158,10 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
     MODE_DELJOINS,      'D',
+    MODE_NOQUITPARTS,   'u',
+    MODE_NOCOLOUR,      'c',
+    MODE_NOCTCP,        'C',
+    MODE_NONOTICE,      'N',
     0x0, 0x0
   };
   unsigned int add;
@@ -2397,8 +2454,7 @@ mode_parse_key(struct ParseState *state, int *flag_p)
   if (state->flags & MODE_PARSE_SET) {
     if (state->dir == MODE_DEL) /* remove the old key */
       *state->chptr->mode.key = '\0';
-    else if (!state->chptr->mode.key[0]
-             || ircd_strcmp(t_str, state->chptr->mode.key) < 0)
+    else
       ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
   }
 }
@@ -2492,6 +2548,13 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
       !ircd_strcmp(state->chptr->mode.upass, t_str))
     return; /* no upass change */
 
+  /* Skip if this is a burst, we have a Upass already and the new Upass is
+   * after the old one alphabetically */
+  if ((state->flags & MODE_PARSE_BURST) &&
+      *(state->chptr->mode.upass) &&
+      ircd_strcmp(state->chptr->mode.upass, t_str) <= 0)
+    return;
+
   if (state->flags & MODE_PARSE_BOUNCE) {
     if (*state->chptr->mode.upass) /* reset old upass */
       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
@@ -2504,8 +2567,7 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
   if (state->flags & MODE_PARSE_SET) {
     if (state->dir == MODE_DEL) /* remove the old upass */
       *state->chptr->mode.upass = '\0';
-    else if (state->chptr->mode.upass[0] == '\0'
-             || ircd_strcmp(t_str, state->chptr->mode.upass) < 0)
+    else
       ircd_strncpy(state->chptr->mode.upass, t_str, KEYLEN);
   }
 }
@@ -2603,6 +2665,13 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
       !ircd_strcmp(state->chptr->mode.apass, t_str))
     return; /* no apass change */
 
+  /* Skip if this is a burst, we have an Apass already and the new Apass is
+   * after the old one alphabetically */
+  if ((state->flags & MODE_PARSE_BURST) &&
+      *(state->chptr->mode.apass) &&
+      ircd_strcmp(state->chptr->mode.apass, t_str) <= 0)
+    return;
+
   if (state->flags & MODE_PARSE_BOUNCE) {
     if (*state->chptr->mode.apass) /* reset old apass */
       modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
@@ -2614,12 +2683,10 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
 
   if (state->flags & MODE_PARSE_SET) {
     if (state->dir == MODE_ADD) { /* set the new apass */
-      /* Only accept the new apass if there is no current apass
-       * (e.g. when a user sets it) or the new one is "less" than the
-       * old (for resolving conflicts during burst).
-       */
-      if (state->chptr->mode.apass[0] == '\0'
-          || ircd_strcmp(t_str, state->chptr->mode.apass) < 0)
+      /* Only accept the new apass if there is no current apass or
+       * this is a BURST. */
+      if (state->chptr->mode.apass[0] == '\0' ||
+          (state->flags & MODE_PARSE_BURST))
         ircd_strncpy(state->chptr->mode.apass, t_str, KEYLEN);
       /* Make it VERY clear to the user that this is a one-time password */
       if (MyUser(state->sptr)) {
@@ -2682,8 +2749,12 @@ bmatch(struct Ban *old_ban, struct Ban *new_ban)
   old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '@';
   if (res)
     return res;
-  /* Compare the addresses. */
-  return !ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits);
+  /* If the old ban's mask mismatches, cannot be a superset. */
+  if (!ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits))
+    return 1;
+  /* Otherwise it depends on whether the old ban's text is a superset
+   * of the new. */
+  return mmatch(old_ban->banstr, new_ban->banstr);
 }
 
 /** Add a ban from a ban list and mark bans that should be removed
@@ -3016,17 +3087,29 @@ mode_process_clients(struct ParseState *state)
     if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
        (MODE_DEL | MODE_CHANOP)) {
       /* prevent +k users from being deopped */
-      if (IsChannelService(state->cli_change[i].client)) {
+      /*
+       * ASUKA_X:
+       * Allow +X'ed users to mess with +k'ed.
+       * --Bigfoot
+       */
+      if ((IsChannelService(state->cli_change[i].client) && IsService(cli_user(state->cli_change[i].client)->server)) || (IsChannelService(state->cli_change[i].client) && !IsXtraOp(state->sptr))) {
        if (state->flags & MODE_PARSE_FORCE) /* it was forced */
          sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
                               state->chptr,
                               (IsServer(state->sptr) ? cli_name(state->sptr) :
                                cli_name((cli_user(state->sptr))->server)));
 
-       else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
-         send_reply(state->sptr, ERR_ISCHANSERVICE,
-                    cli_name(state->cli_change[i].client),
-                    state->chptr->chname);
+        else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET && (state->sptr != state->cli_change[i].client)) {
+          if(IsService(cli_user(state->cli_change[i].client)->server) && IsChannelService(state->cli_change[i].client)){
+            send_reply(state->sptr, ERR_ISREALSERVICE,
+                     cli_name(state->cli_change[i].client),
+                     state->chptr->chname);
+          }else{
+            send_reply(state->sptr, ERR_ISCHANSERVICE,
+                     cli_name(state->cli_change[i].client),
+                     state->chptr->chname);
+          }
+
          continue;
        }
       }
@@ -3169,6 +3252,10 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
     MODE_DELJOINS,      'D',
+    MODE_NOQUITPARTS,   'u',
+    MODE_NOCOLOUR,      'c',
+    MODE_NOCTCP,        'C',
+    MODE_NONOTICE,      'N',
     MODE_ADD,          '+',
     MODE_DEL,          '-',
     0x0, 0x0
@@ -3272,7 +3359,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
       state.parc--;
 
       /* is it a TS? */
-      if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
+      if (IsServer(state.cptr) && !state.parc && IsDigit(*modestr)) {
        time_t recv_ts;
 
        if (!(state.flags & MODE_PARSE_SET))      /* don't set earlier TS if */
@@ -3282,6 +3369,35 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
 
        if (recv_ts && recv_ts < state.chptr->creationtime)
          state.chptr->creationtime = recv_ts; /* respect earlier TS */
+        else if (recv_ts > state.chptr->creationtime) {
+          struct Client *sserv;
+
+          /* Check whether the originating server has fully processed
+           * the burst to it. */
+          sserv = state.cptr;
+          if (!IsServer(sserv))
+              sserv = cli_user(sserv)->server;
+          if (IsBurstOrBurstAck(sserv)) {
+            /* This is a legal but unusual case; the source server
+             * probably just has not processed the BURST for this
+             * channel.  It SHOULD wipe out all its modes soon, so
+             * silently ignore the mode change rather than send a
+             * bounce that could desync modes from our side (that
+             * have already been sent).
+             */
+            state.mbuf->mb_add = 0;
+            state.mbuf->mb_rem = 0;
+            state.mbuf->mb_count = 0;
+            return state.args_used;
+          } else {
+            /* Server is desynced; bounce the mode and deop the source
+             * to fix it. */
+            state.mbuf->mb_dest &= ~MODEBUF_DEST_CHANNEL;
+            state.mbuf->mb_dest |= MODEBUF_DEST_BOUNCE | MODEBUF_DEST_HACK2;
+            if (!IsServer(state.cptr))
+              state.mbuf->mb_dest |= MODEBUF_DEST_DEOP;
+          }
+        }
 
        break; /* break out of while loop */
       } else if (state.flags & MODE_PARSE_STRICT ||
@@ -3404,11 +3520,13 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
     /* Send notification to channel */
     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);
+                   ((flags & CHFL_BANNED) || ((chan->mode.mode & MODE_NOQUITPARTS)
+                    && !IsChannelService(member->user)) || !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) ?
+                   ((flags & CHFL_BANNED) || (chan->mode.mode & MODE_NOQUITPARTS)
+                    || !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
@@ -3501,6 +3619,15 @@ joinbuf_flush(struct JoinBuf *jbuf)
   case JOINBUF_TYPE_CREATE:
     sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
                          "%s %Tu", chanlist, jbuf->jb_create);
+    if (MyUser(jbuf->jb_source) && (feature_bool(FEAT_AUTOCHANMODES) && 
+                 feature_str(FEAT_AUTOCHANMODES_LIST) && (strlen(feature_str(FEAT_AUTOCHANMODES_LIST)) > 0))) {
+      char *name;
+      char *p = 0;
+      for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) {
+        sendcmdto_serv_butone(jbuf->jb_source, CMD_MODE, jbuf->jb_connect,
+                         "%s %s%s", name, "+", feature_str(FEAT_AUTOCHANMODES_LIST));
+      }
+    }
     break;
 
   case JOINBUF_TYPE_PART: