Make some ChanServ ban checks see through hidden hosts.
[srvx.git] / src / chanserv.c
index abba31fa3717cfa974ea4014edba4a7f25c531b8..99e9ba3deba3de044a232c65440b059b4cc1cf65 100644 (file)
@@ -1202,7 +1202,7 @@ add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t
     safestrncpy(bd->mask, mask, sizeof(bd->mask));
     if(owner)
         safestrncpy(bd->owner, owner, sizeof(bd->owner));
-    bd->reason = reason ? strdup(reason) : NULL;
+    bd->reason = strdup(reason);
 
     if(expires)
        timeq_add(expires, expire_ban, bd);
@@ -1662,7 +1662,6 @@ chanserv_get_owned_count(struct handle_info *hi)
 
 static CHANSERV_FUNC(cmd_register)
 {
-    struct mod_chanmode *change;
     struct handle_info *handle;
     struct chanData *cData;
     struct modeNode *mn;
@@ -1685,7 +1684,8 @@ static CHANSERV_FUNC(cmd_register)
             return 0;
         }
 
-        if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
+        if(!IsHelping(user)
+           && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
         {
             reply("CSMSG_MUST_BE_OPPED", channel->name);
             return 0;
@@ -1752,13 +1752,20 @@ static CHANSERV_FUNC(cmd_register)
     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
     cData->modes = chanserv_conf.default_modes;
     if(off_channel > 0)
-      cData->modes.modes_set |= MODE_REGISTERED;
-    change = mod_chanmode_dup(&cData->modes, 1);
-    change->args[change->argc].mode = MODE_CHANOP;
-    change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
-    change->argc++;
-    mod_chanmode_announce(chanserv, channel, change);
-    mod_chanmode_free(change);
+        cData->modes.modes_set |= MODE_REGISTERED;
+    if (IsOffChannel(cData))
+    {
+        mod_chanmode_announce(chanserv, channel, &cData->modes);
+    }
+    else
+    {
+        struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
+        change->args[change->argc].mode = MODE_CHANOP;
+        change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
+        change->argc++;
+        mod_chanmode_announce(chanserv, channel, change);
+        mod_chanmode_free(change);
+    }
 
     /* Initialize the channel's max user record. */
     cData->max = channel->members.used;
@@ -2101,8 +2108,20 @@ merge_bans(struct chanData *source, struct chanData *target)
 static void
 merge_data(struct chanData *source, struct chanData *target)
 {
+    /* Use more recent visited and owner-transfer time; use older
+     * registered time.  Bitwise or may_opchan.  Use higher max.
+     * Do not touch last_refresh, ban count or user counts.
+     */
     if(source->visited > target->visited)
        target->visited = source->visited;
+    if(source->registered < target->registered)
+        target->registered = source->registered;
+    if(source->ownerTransfer > target->ownerTransfer)
+        target->ownerTransfer = source->ownerTransfer;
+    if(source->may_opchan)
+        target->may_opchan = 1;
+    if(source->max > target->max)
+        target->max = source->max;
 }
 
 static void
@@ -2420,7 +2439,7 @@ cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long dur
 }
 
 static int
-cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
+cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
 {
     struct userData *actor, *uData, *next;
     char interval[INTERVALLEN];
@@ -2446,7 +2465,9 @@ cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short m
     {
        next = uData->next;
 
-       if((uData->seen > limit) || uData->present)
+       if((uData->seen > limit)
+           || uData->present
+           || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
            continue;
 
        if(((uData->access >= min_access) && (uData->access <= max_access))
@@ -2470,9 +2491,11 @@ static CHANSERV_FUNC(cmd_trim)
 {
     unsigned long duration;
     unsigned short min_level, max_level;
+    int vacation;
 
     REQUIRE_PARAMS(3);
 
+    vacation = argc > 3 && !strcmp(argv[3], "vacation");
     duration = ParseInterval(argv[2]);
     if(duration < 60)
     {
@@ -2487,17 +2510,17 @@ static CHANSERV_FUNC(cmd_trim)
     }
     else if(!irccasecmp(argv[1], "users"))
     {
-       cmd_trim_users(user, channel, 0, 0, duration);
+       cmd_trim_users(user, channel, 0, 0, duration, vacation);
        return 1;
     }
     else if(parse_level_range(&min_level, &max_level, argv[1]))
     {
-       cmd_trim_users(user, channel, min_level, max_level, duration);
+       cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
        return 1;
     }
     else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
     {
-       cmd_trim_users(user, channel, min_level, min_level, duration);
+       cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
        return 1;
     }
     else
@@ -2670,7 +2693,7 @@ static CHANSERV_FUNC(cmd_devoice)
 }
 
 static int
-bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
+bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
 {
     unsigned int ii;
 
@@ -2683,7 +2706,7 @@ bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban
         if(IsService(mn->user))
             continue;
 
-        if(!user_matches_glob(mn->user, ban, 1))
+        if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
             continue;
 
         if(protect_user(mn->user, user, channel->channel_info))
@@ -3024,7 +3047,8 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas
     {
         for(ii = count = 0; ii < bans->used; ++ii)
         {
-            match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
+            match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
+                                          MATCH_USENICK | MATCH_VISIBLE);
             if(match[ii])
                 count++;
         }
@@ -3046,8 +3070,9 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas
         if(!match[ii])
             continue;
         change->args[count].mode = MODE_REMOVE | MODE_BAN;
-        change->args[count++].u.hostmask = bans->list[ii]->ban;
+        change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
     }
+    assert(count == change->argc);
     return change;
 }
 
@@ -3080,7 +3105,11 @@ unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
         change = find_matching_bans(&channel->banlist, actee, mask);
         if(change)
         {
+            unsigned int ii;
+
             modcmd_chanmode_announce(change);
+            for(ii = 0; ii < change->argc; ++ii)
+                free((char*)change->args[ii].u.hostmask);
             mod_chanmode_free(change);
             acted = 1;
         }
@@ -3094,7 +3123,8 @@ unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
        while(ban)
        {
            if(actee)
-               for( ; ban && !user_matches_glob(actee, ban->mask, 1);
+               for( ; ban && !user_matches_glob(actee, ban->mask,
+                                                 MATCH_USENICK | MATCH_VISIBLE);
                     ban = ban->next);
            else
                for( ; ban && !match_ircglobs(mask, ban->mask);
@@ -3157,9 +3187,11 @@ static CHANSERV_FUNC(cmd_unbanall)
     for(ii=0; ii<channel->banlist.used; ii++)
     {
         change->args[ii].mode = MODE_REMOVE | MODE_BAN;
-        change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
+        change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
     }
     modcmd_chanmode_announce(change);
+    for(ii = 0; ii < change->argc; ++ii)
+        free((char*)change->args[ii].u.hostmask);
     mod_chanmode_free(change);
     reply("CSMSG_BANS_REMOVED", channel->name);
     return 1;
@@ -3168,6 +3200,7 @@ static CHANSERV_FUNC(cmd_unbanall)
 static CHANSERV_FUNC(cmd_open)
 {
     struct mod_chanmode *change;
+    unsigned int ii;
 
     change = find_matching_bans(&channel->banlist, user, NULL);
     if(!change)
@@ -3178,6 +3211,8 @@ static CHANSERV_FUNC(cmd_open)
         change->modes_clear &= ~channel->channel_info->modes.modes_set;
     modcmd_chanmode_announce(change);
     reply("CSMSG_CHANNEL_OPENED", channel->name);
+    for(ii = 0; ii < change->argc; ++ii)
+        free((char*)change->args[ii].u.hostmask);
     mod_chanmode_free(change);
     return 1;
 }
@@ -3739,8 +3774,10 @@ static CHANSERV_FUNC(cmd_topic)
 
 static CHANSERV_FUNC(cmd_mode)
 {
+    struct userData *uData;
     struct mod_chanmode *change;
-    
+    short base_oplevel;
+
     if(argc < 2)
     {
         change = &channel->channel_info->modes;
@@ -3752,7 +3789,14 @@ static CHANSERV_FUNC(cmd_mode)
        return 1;
     }
 
-    change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
+    uData = GetChannelUser(channel->channel_info, user->handle_info);
+    if (!uData)
+        base_oplevel = MAXOPLEVEL;
+    else if (uData->access >= UL_OWNER)
+        base_oplevel = 1;
+    else
+        base_oplevel = 1 + UL_OWNER - uData->access;
+    change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
     if(!change)
     {
        reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
@@ -3927,9 +3971,10 @@ static CHANSERV_FUNC(cmd_info)
     reply("CSMSG_CHANNEL_USERS", cData->userCount);
     reply("CSMSG_CHANNEL_BANS", cData->banCount);
     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
-    reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
 
     privileged = IsStaff(user);
+    if(privileged)
+        reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
 
@@ -4440,18 +4485,21 @@ chanserv_expire_suspension(void *data)
 {
     struct suspended *suspended = data;
     struct chanNode *channel;
-    struct mod_chanmode change;
 
     if(!suspended->expires || (now < suspended->expires))
         suspended->revoked = now;
     channel = suspended->cData->channel;
     suspended->cData->channel = channel;
     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
-    mod_chanmode_init(&change);
-    change.argc = 1;
-    change.args[0].mode = MODE_CHANOP;
-    change.args[0].u.member = AddChannelUser(chanserv, channel);
-    mod_chanmode_announce(chanserv, channel, &change);
+    if(!IsOffChannel(suspended->cData))
+    {
+        struct mod_chanmode change;
+        mod_chanmode_init(&change);
+        change.argc = 1;
+        change.args[0].mode = MODE_CHANOP;
+        change.args[0].u.member = AddChannelUser(chanserv, channel);
+        mod_chanmode_announce(chanserv, channel, &change);
+    }
 }
 
 static CHANSERV_FUNC(cmd_csuspend)
@@ -4872,7 +4920,7 @@ static MODCMD_FUNC(chan_opt_modes)
        {
             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
        }
-       else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
+       else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
        {
             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
             return 0;
@@ -5478,8 +5526,15 @@ static CHANSERV_FUNC(cmd_giveownership)
     new_owner = GetChannelAccess(cData, new_owner_hi);
     if(!new_owner)
     {
-        reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
-        return 0;
+        if(force)
+        {
+            new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
+        }
+        else
+        {
+            reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
+            return 0;
+        }
     }
     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
     {
@@ -5839,7 +5894,7 @@ handle_join(struct modeNode *mNode)
         unsigned int ii;
         for(ii = 0; ii < channel->banlist.used; ii++)
         {
-            if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
+            if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
             {
                 /* Riding a netburst.  Naughty. */
                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
@@ -5854,8 +5909,8 @@ handle_join(struct modeNode *mNode)
     {
         /* Not joining through a ban. */
         for(bData = cData->bans;
-                bData && !user_matches_glob(user, bData->mask, 1);
-                bData = bData->next);
+            bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+            bData = bData->next);
 
         if(bData)
         {
@@ -5955,6 +6010,10 @@ handle_join(struct modeNode *mNode)
             uData->present = 1;
         }
     }
+
+    /* If user joining normally (not during burst), apply op or voice,
+     * and send greeting/userinfo as appropriate.
+     */
     if(!user->uplink->burst)
     {
         if(modes)
@@ -5965,7 +6024,7 @@ handle_join(struct modeNode *mNode)
             change.args[0].u.member = mNode;
             mod_chanmode_announce(chanserv, channel, &change);
         }
-        if(greeting && !user->uplink->burst)
+        if(greeting)
             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
         if(uData && info)
             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
@@ -6036,14 +6095,14 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
            || IsSuspended(channel->channel_info))
             continue;
         for(jj = 0; jj < channel->banlist.used; ++jj)
-            if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+            if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
                 break;
         if(jj < channel->banlist.used)
             continue;
         for(ban = channel->channel_info->bans; ban; ban = ban->next)
         {
             char kick_reason[MAXLEN];
-            if(!user_matches_glob(user, ban->mask, 1))
+            if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
                 continue;
             change.args[0].mode = MODE_BAN;
             change.args[0].u.hostmask = ban->mask;
@@ -6217,7 +6276,7 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch
             if(!bounce)
                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
-            bounce->args[bnc].u.hostmask = ban;
+            bounce->args[bnc].u.hostmask = strdup(ban);
             bnc++;
             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
         }
@@ -6226,6 +6285,9 @@ handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_ch
     {
         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
             mod_chanmode_announce(chanserv, channel, bounce);
+        for(ii = 0; ii < change->argc; ++ii)
+            if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
+                free((char*)bounce->args[ii].u.hostmask);
         mod_chanmode_free(bounce);
     }
 }
@@ -6253,7 +6315,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
             continue;
         /* Look for a matching ban already on the channel. */
         for(jj = 0; jj < channel->banlist.used; ++jj)
-            if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+            if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
                 break;
         /* Need not act if we found one. */
         if(jj < channel->banlist.used)
@@ -6261,7 +6323,7 @@ handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
         /* Look for a matching ban in this channel. */
         for(bData = channel->channel_info->bans; bData; bData = bData->next)
         {
-            if(!user_matches_glob(user, bData->mask, 1))
+            if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
                 continue;
             change.args[0].u.hostmask = bData->mask;
             mod_chanmode_announce(chanserv, channel, &change);
@@ -6407,7 +6469,8 @@ chanserv_conf_read(void)
         str = "+nt";
     safestrncpy(mode_line, str, sizeof(mode_line));
     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
-    if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
+    if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
+       && (change->argc < 2))
     {
         chanserv_conf.default_modes = *change;
         mod_chanmode_free(change);
@@ -6463,8 +6526,6 @@ chanserv_conf_read(void)
     else
         strlist = alloc_string_list(2);
     chanserv_conf.old_ban_names = strlist;
-    /* the variable itself is actually declared in proto-common.c; this is equally 
-     * parse issue. */
     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
     off_channel = str ? atoi(str) : 0;
 }
@@ -6581,6 +6642,8 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
+    if (!reason || !owner)
+        return;
 
     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
@@ -6591,7 +6654,7 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
     else
         expires_time = 0;
 
-    if(expires_time && (expires_time < now))
+    if(!reason || (expires_time && (expires_time < now)))
         return;
 
     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
@@ -6774,7 +6837,7 @@ chanserv_channel_read(const char *key, struct record_data *hir)
     if(!IsSuspended(cData)
        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
        && (argc = split_line(str, 0, ArrayLength(argv), argv))
-       && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
+       && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
         cData->modes = *modes;
        if(off_channel > 0)
           cData->modes.modes_set |= MODE_REGISTERED;