Make references to channel password lengths consistent.
[ircu2.10.12-pk.git] / ircd / channel.c
index ff12cc1cae844197758d43e402ced3a381d9f869..ea2d72812673a27510307cef6e9e6e5a6a7918fe 100644 (file)
@@ -258,24 +258,15 @@ int sub1_from_channel(struct Channel* chptr)
 
   chptr->users = 0;
 
-  /*
-   * Also channels without Apass set need to be kept alive,
-   * otherwise Bad Guys(tm) would be able to takeover
-   * existing channels too easily, and then set an Apass!
-   * However, if a channel without Apass becomes empty
-   * then we try to be kind to them and remove possible
-   * limiting modes.
-   */
-  chptr->mode.mode &= ~MODE_INVITEONLY;
-  chptr->mode.limit = 0;
-  /*
-   * We do NOT reset a possible key or bans because when
-   * the 'channel owners' can't get in because of a key
-   * or ban then apparently there was a fight/takeover
-   * on the channel and we want them to contact IRC opers
-   * who then will educate them on the use of Apass/upass.
+  /* There is a semantics problem here: Assuming no fragments across a
+   * split, a channel without Apass could be maliciously destroyed and
+   * recreated, and someone could set apass on the new instance.
+   *
+   * This could be fixed by preserving the empty non-Apass channel for
+   * the same time as if it had an Apass (but removing +i and +l), and
+   * reopping the first user to rejoin.  However, preventing net rides
+   * requires a backwards-incompatible protocol change..
    */
-
   if (!chptr->mode.apass[0])         /* If no Apass, destroy now. */
     destruct_channel(chptr);
   else if (TStime() - chptr->creationtime < 172800)    /* Channel younger than 48 hours? */
@@ -355,12 +346,15 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
 {
   char        nu[NICKLEN + USERLEN + 2];
   char        tmphost[HOSTLEN + 1];
+  char        iphost[SOCKIPLEN + 1];
+  char       *hostmask;
   char       *sr;
   struct Ban *found;
 
   /* Build nick!user and alternate host names. */
   ircd_snprintf(0, nu, sizeof(nu), "%s!%s",
                 cli_name(cptr), cli_user(cptr)->username);
+  ircd_ntoa_r(iphost, &cli_ip(cptr));
   if (!IsAccount(cptr))
     sr = NULL;
   else if (HasHiddenHost(cptr))
@@ -385,10 +379,12 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
     if (res)
       continue;
     /* Compare host portion of ban. */
-    if (!((banlist->flags & BAN_IPMASK)
-         && ipmask_check(&cli_ip(cptr), &banlist->address, banlist->addrbits))
-        && match(banlist->banstr + banlist->nu_len + 1, cli_user(cptr)->host)
-        && !(sr && match(banlist->banstr + banlist->nu_len + 1, sr) == 0))
+    hostmask = banlist->banstr + banlist->nu_len + 1;
+    if (((banlist->flags & BAN_IPMASK)
+         ? !ipmask_check(&cli_ip(cptr), &banlist->address, banlist->addrbits)
+         : match(hostmask, iphost))
+        && match(hostmask, cli_user(cptr)->host)
+        && !(sr && match(hostmask, sr) == 0))
       continue;
     /* If an exception matches, no ban can match. */
     if (banlist->flags & BAN_EXCEPTION)
@@ -451,7 +447,7 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who,
     member->user         = who;
     member->channel      = chptr;
     member->status       = flags;
-    member->oplevel      = oplevel;
+    SetOpLevel(member, oplevel);
 
     member->next_member  = chptr->members;
     if (member->next_member)
@@ -661,14 +657,15 @@ int member_can_send_to_channel(struct Membership* member, int reveal)
   assert(0 != member);
 
   /* Discourage using the Apass to get op.  They should use the upass. */
-  if (IsChannelManager(member) && *member->channel->mode.upass)
+  if (IsChannelManager(member) && member->channel->mode.apass[0])
     return 0;
 
   if (IsVoicedOrOpped(member))
     return 1;
+
   /*
    * If it's moderated, and you aren't a privileged user, you can't
-   * speak.  
+   * speak.
    */
   if (member->channel->mode.mode & MODE_MODERATED)
     return 0;
@@ -1113,9 +1110,10 @@ char *pretty_mask(char *mask)
       user = mask;
       host = ++ptr;
     }
-    else if (*ptr == '.')
+    else if (*ptr == '.' || *ptr == ':')
     {
-      /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
+      /* Case 2: Found character specific to IP or hostname (without
+       * finding a '!' or '@' yet) */
       last_dot = ptr;
       continue;
     }
@@ -1288,7 +1286,8 @@ void clean_channelname(char *cn)
   int i;
 
   for (i = 0; cn[i]; i++) {
-    if (i >= CHANNELLEN || !IsChannelChar(cn[i])) {
+    if (i >= IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))
+        || !IsChannelChar(cn[i])) {
       cn[i] = '\0';
       return;
     }
@@ -1638,8 +1637,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
 
   /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
    */
-  if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source))
-    app_source = &me;
+  if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source) || IsMe(mbuf->mb_source))
+    app_source = &his;
   else
     app_source = mbuf->mb_source;
 
@@ -2338,14 +2337,24 @@ mode_parse_limit(struct ParseState *state, int *flag_p)
   }
 }
 
+/** Helper function to clean key-like parameters. */
+static void
+clean_key(char *s)
+{
+  int t_len = KEYLEN;
+
+  while (*s > ' ' && *s != ':' && *s != ',' && t_len--)
+    s++;
+  *s = '\0';
+}
+
 /*
  * Helper function to convert keys
  */
 static void
 mode_parse_key(struct ParseState *state, int *flag_p)
 {
-  char *t_str, *s;
-  int t_len;
+  char *t_str;
 
   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
     return;
@@ -2371,15 +2380,9 @@ mode_parse_key(struct ParseState *state, int *flag_p)
     return;
   state->done |= DONE_KEY;
 
-  t_len = KEYLEN;
-
   /* clean up the key string */
-  s = t_str;
-  while (*s > ' ' && *s != ':' && *s != ',' && t_len--)
-    s++;
-  *s = '\0';
-
-  if (!*t_str) { /* warn if empty */
+  clean_key(t_str);
+  if (!*t_str || *t_str == ':') { /* warn if empty */
     if (MyUser(state->sptr))
       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
                       "MODE -k");
@@ -2432,8 +2435,7 @@ mode_parse_key(struct ParseState *state, int *flag_p)
 static void
 mode_parse_upass(struct ParseState *state, int *flag_p)
 {
-  char *t_str, *s;
-  int t_len;
+  char *t_str;
 
   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
     return;
@@ -2468,10 +2470,8 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
     if (*state->chptr->mode.apass) {
       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
                  state->chptr->chname);
-    } else if (TStime() - state->chptr->creationtime >= 171000) {
-      send_reply(state->sptr, ERR_NOMANAGER_LONG, state->chptr->chname);
     } else {
-      send_reply(state->sptr, ERR_NOMANAGER_SHORT, state->chptr->chname);
+      send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname);
     }
     return;
   }
@@ -2480,15 +2480,9 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
     return;
   state->done |= DONE_UPASS;
 
-  t_len = PASSLEN + 1;
-
   /* clean up the upass string */
-  s = t_str;
-  while (*++s > ' ' && *s != ':' && --t_len)
-    ;
-  *s = '\0';
-
-  if (!*t_str) { /* warn if empty */
+  clean_key(t_str);
+  if (!*t_str || *t_str == ':') { /* warn if empty */
     if (MyUser(state->sptr))
       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
                       "MODE -U");
@@ -2533,7 +2527,7 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
 
   if (state->flags & MODE_PARSE_SET) {
     if (state->dir == MODE_ADD) /* set the new upass */
-      ircd_strncpy(state->chptr->mode.upass, t_str, PASSLEN);
+      ircd_strncpy(state->chptr->mode.upass, t_str, KEYLEN);
     else /* remove the old upass */
       *state->chptr->mode.upass = '\0';
   }
@@ -2545,8 +2539,8 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
 static void
 mode_parse_apass(struct ParseState *state, int *flag_p)
 {
-  char *t_str, *s;
-  int t_len;
+  struct Membership *memb;
+  char *t_str;
 
   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
     return;
@@ -2577,7 +2571,9 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
   }
 
   /* Don't allow to change the Apass if the channel is older than 48 hours. */
-  if (TStime() - state->chptr->creationtime >= 172800 && !IsAnOper(state->sptr)) {
+  if (MyUser(state->sptr)
+      && TStime() - state->chptr->creationtime >= 172800
+      && !IsAnOper(state->sptr)) {
     send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
     return;
   }
@@ -2587,10 +2583,8 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
     if (*state->chptr->mode.apass) {
       send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
                  state->chptr->chname);
-    } else if (TStime() - state->chptr->creationtime >= 171000) {
-      send_reply(state->sptr, ERR_NOMANAGER_LONG, state->chptr->chname);
     } else {
-      send_reply(state->sptr, ERR_NOMANAGER_SHORT, state->chptr->chname);
+      send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname);
     }
     return;
   }
@@ -2599,15 +2593,9 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
     return;
   state->done |= DONE_APASS;
 
-  t_len = PASSLEN + 1;
-
   /* clean up the apass string */
-  s = t_str;
-  while (*++s > ' ' && *s != ':' && --t_len)
-    ;
-  *s = '\0';
-
-  if (!*t_str) { /* warn if empty */
+  clean_key(t_str);
+  if (!*t_str || *t_str == ':') { /* warn if empty */
     if (MyUser(state->sptr))
       need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
                       "MODE -A");
@@ -2647,16 +2635,24 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
   if (state->flags & MODE_PARSE_SET) {
     if (state->dir == MODE_ADD) { /* set the new apass */
       /* Make it VERY clear to the user that this is a one-time password */
-      ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
+      ircd_strncpy(state->chptr->mode.apass, t_str, KEYLEN);
       if (MyUser(state->sptr)) {
        send_reply(state->sptr, RPL_APASSWARN_SET, state->chptr->mode.apass);
        send_reply(state->sptr, RPL_APASSWARN_SECRET, state->chptr->chname,
                    state->chptr->mode.apass);
       }
+      /* Give the channel manager level 0 ops. */
+      if (!(state->flags & MODE_PARSE_FORCE) && IsChannelManager(state->member))
+        SetOpLevel(state->member, 0);
     } else { /* remove the old apass */
       *state->chptr->mode.apass = '\0';
       if (MyUser(state->sptr))
         send_reply(state->sptr, RPL_APASSWARN_CLEAR);
+      /* Revert everyone to MAXOPLEVEL. */
+      for (memb = state->chptr->members; memb; memb = memb->next_member) {
+        if (memb->status & MODE_CHANOP)
+          SetOpLevel(memb, MAXOPLEVEL);
+      }
     }
   }
 }
@@ -2812,7 +2808,7 @@ mode_parse_ban(struct ParseState *state, int *flag_p)
   newban->flags = ((state->dir == MODE_ADD) ? BAN_ADD : BAN_DEL)
       | (*flag_p == MODE_BAN ? 0 : BAN_EXCEPTION);
   set_ban_mask(newban, collapse(pretty_mask(t_str)));
-  ircd_strncpy(newban->who, cli_name(state->sptr), HOSTLEN);
+  ircd_strncpy(newban->who, IsUser(state->sptr) ? cli_name(state->sptr) : "*", NICKLEN);
   newban->when = TStime();
   apply_ban(&state->chptr->banlist, newban, 0);
 }
@@ -3037,14 +3033,14 @@ mode_process_clients(struct ParseState *state)
     /* set op-level of member being opped */
     if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
        (MODE_ADD | MODE_CHANOP)) {
-      /* If being opped by an outsider, get oplevel 0 for an apass
+      /* If being opped by an outsider, get oplevel 1 for an apass
        *   channel, else MAXOPLEVEL.
        * Otherwise, if not an apass channel, or state->member has
        *   MAXOPLEVEL, get oplevel MAXOPLEVEL.
        * Otherwise, get state->member's oplevel+1.
        */
       if (!state->member)
-        SetOpLevel(member, state->chptr->mode.apass[0] ? 0 : MAXOPLEVEL);
+        SetOpLevel(member, state->chptr->mode.apass[0] ? 1 : MAXOPLEVEL);
       else if (!state->chptr->mode.apass[0] || OpLevel(state->member) == MAXOPLEVEL)
         SetOpLevel(member, MAXOPLEVEL);
       else
@@ -3208,12 +3204,12 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
        break;
 
       case 'A': /* deal with Admin passes */
-        if (feature_bool(FEAT_OPLEVELS))
+        if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS))
        mode_parse_apass(&state, flag_p);
        break;
 
       case 'U': /* deal with user passes */
-        if (feature_bool(FEAT_OPLEVELS))
+        if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS))
        mode_parse_upass(&state, flag_p);
        break;
 
@@ -3403,10 +3399,11 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
     /* send notification to all servers */
     if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
     {
-      if (flags & CHFL_CHANOP)
+      if (flags & CHFL_CHANOP) {
+        assert(oplevel == 0 || oplevel == 1);
         sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
                               "%u:%H %Tu", oplevel, chan, chan->creationtime);
-      else
+      else
         sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
                               "%H %Tu", chan, chan->creationtime);
     }
@@ -3417,7 +3414,7 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
 
       /* send an op, too, if needed */
       if (flags & CHFL_CHANOP && (oplevel < MAXOPLEVEL || !MyUser(jbuf->jb_source)))
-       sendcmdto_channel_butserv_butone((chan->mode.apass[0] ? &me : jbuf->jb_source),
+       sendcmdto_channel_butserv_butone((chan->mode.apass[0] ? &his : jbuf->jb_source),
                                          CMD_MODE, chan, NULL, 0, "%H +o %C",
                                         chan, jbuf->jb_source);
     } else if (MyUser(jbuf->jb_source))
@@ -3524,7 +3521,7 @@ void CheckDelayedJoins(struct Channel *chan)
     if (!memb2) {
       /* clear +d */
       chan->mode.mode &= ~MODE_WASDELJOINS;
-      sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan, NULL, 0,
+      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan, NULL, 0,
                                        "%H -d", chan);
     }
   }