Allow !trim (for users) to work for helpers.
[srvx.git] / src / chanserv.c
index f61faa91657744373c44240c5512b4fd8dfd25a8..30e77236f10ec44de351b5717cae9358c7a82b2a 100644 (file)
@@ -404,7 +404,7 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
     { "CSMSG_PEEK_TOPIC", "$bTopic:          $b%s" },
     { "CSMSG_PEEK_MODES", "$bModes:          $b%s" },
-    { "CSMSG_PEEK_USERS", "$bTotal users:    $b%d" },
+    { "CSMSG_PEEK_USERS", "$bTotal users:    $b%d (%d ops, %d voices, %d regulars)" },
     { "CSMSG_PEEK_OPS", "$bOps:$b" },
     { "CSMSG_PEEK_NO_OPS", "$bOps:            $bNone present" },
 
@@ -1362,7 +1362,7 @@ unregister_channel(struct chanData *channel, const char *reason)
 }
 
 static void
-expire_channels(UNUSED_ARG(void *data))
+expire_channels(void *data)
 {
     struct chanData *channel, *next;
     struct userData *user;
@@ -1382,7 +1382,7 @@ expire_channels(UNUSED_ARG(void *data))
 
         /* Make sure there are no high-ranking users still in the channel. */
         for(user=channel->users; user; user=user->next)
-            if(user->present && (user->access >= UL_PRESENT))
+            if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
                 break;
         if(user)
             continue;
@@ -1392,7 +1392,7 @@ expire_channels(UNUSED_ARG(void *data))
         unregister_channel(channel, "registration expired.");
     }
 
-    if(chanserv_conf.channel_expire_frequency)
+    if(chanserv_conf.channel_expire_frequency && !data)
         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 }
 
@@ -2265,6 +2265,10 @@ static CHANSERV_FUNC(cmd_move)
     target->channel_info->channel = target;
     channel->channel_info = NULL;
 
+    /* Check whether users are present in the new channel. */
+    for(uData = target->channel_info->users; uData; uData = uData->next)
+        scan_user_presence(uData, NULL);
+
     reply("CSMSG_MOVE_SUCCESS", target->name);
 
     sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
@@ -2315,6 +2319,11 @@ merge_users(struct chanData *source, struct chanData *target)
             choice = (suData->seen > tuData->seen) ? suData : tuData;
         else /* Otherwise, keep the higher access level. */
             choice = (suData->access > tuData->access) ? suData : tuData;
+        /* Use the later seen time. */
+        if(suData->seen < tuData->seen)
+            suData->seen = tuData->seen;
+        else
+            tuData->seen = suData->seen;
 
         /* Remove the user that wasn't picked. */
         if(choice == tuData)
@@ -2343,6 +2352,9 @@ merge_users(struct chanData *source, struct chanData *target)
         /* Update the user counts for the target channel; the
            source counts are left alone. */
         target->userCount++;
+
+        /* Check whether the user is in the target channel. */
+        scan_user_presence(suData, NULL);
     }
 
     /* Possible to assert (source->users == NULL) here. */
@@ -2727,7 +2739,7 @@ cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short mi
         return 0;
     }
 
-    if((actor->access <= max_access) && !IsHelping(user))
+    if(actor->access <= max_access)
     {
         reply("CSMSG_NO_ACCESS");
         return 0;
@@ -2809,7 +2821,7 @@ cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short m
     unsigned int count;
     unsigned long limit;
 
-    actor = GetChannelAccess(channel->channel_info, user->handle_info);
+    actor = GetChannelUser(channel->channel_info, user->handle_info);
     if(min_access > max_access)
     {
         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
@@ -3139,6 +3151,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
     else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
     {
         struct handle_info *hi;
+        extern const char *titlehost_suffix;
         char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
         const char *accountname = argv[1] + 1;
 
@@ -3148,7 +3161,7 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
             return 0;
         }
 
-        snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
+        snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
         victims = alloca(sizeof(victims[0]) * channel->members.used);
 
         if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
@@ -3803,56 +3816,6 @@ static CHANSERV_FUNC(cmd_access)
     return 1;
 }
 
-static void
-zoot_list(struct listData *list)
-{
-    struct userData *uData;
-    unsigned int start, curr, highest, lowest;
-    struct helpfile_table tmp_table;
-    const char **temp, *msg;
-
-    if(list->table.length == 1)
-    {
-        if(list->search)
-            send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
-        else
-            send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
-        msg = user_find_message(list->user, "MSG_NONE");
-        send_message_type(4, list->user, list->bot, "  %s", msg);
-    }
-    tmp_table.width = list->table.width;
-    tmp_table.flags = list->table.flags;
-    list->table.contents[0][0] = " ";
-    highest = list->highest;
-    if(list->lowest != 0)
-        lowest = list->lowest;
-    else if(highest < 100)
-        lowest = 1;
-    else
-        lowest = highest - 100;
-    for(start = curr = 1; curr < list->table.length; )
-    {
-        uData = list->users[curr-1];
-        list->table.contents[curr++][0] = " ";
-        if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
-        {
-            if(list->search)
-                send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
-            else
-                send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
-            temp = list->table.contents[--start];
-            list->table.contents[start] = list->table.contents[0];
-            tmp_table.contents = list->table.contents + start;
-            tmp_table.length = curr - start;
-            table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
-            list->table.contents[start] = temp;
-            start = curr;
-            highest = lowest - 1;
-            lowest = (highest < 100) ? 0 : (highest - 99);
-        }
-    }
-}
-
 static void
 def_list(struct listData *list)
 {
@@ -3898,7 +3861,6 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
     lData.highest = highest;
     lData.search = (argc > 1) ? argv[1] : NULL;
     send_list = def_list;
-    (void)zoot_list; /* since it doesn't show user levels */
 
     if(user->handle_info)
     {
@@ -3951,6 +3913,8 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
             ary[3] = "Suspended";
         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
             ary[3] = "Vacation";
+        else if(HANDLE_FLAGGED(uData->handle, BOT))
+            ary[3] = "Bot";
         else
             ary[3] = "Normal";
     }
@@ -4196,6 +4160,7 @@ static CHANSERV_FUNC(cmd_mode)
     struct userData *uData;
     struct mod_chanmode *change;
     short base_oplevel;
+    char fmt[MAXLEN];
 
     if(argc < 2)
     {
@@ -4215,7 +4180,7 @@ static CHANSERV_FUNC(cmd_mode)
         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);
+    change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
     if(!change)
     {
         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
@@ -4232,18 +4197,16 @@ static CHANSERV_FUNC(cmd_mode)
     }
 
     modcmd_chanmode_announce(change);
+    mod_chanmode_format(change, fmt);
     mod_chanmode_free(change);
-    reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
+    reply("CSMSG_MODES_SET", fmt);
     return 1;
 }
 
 static CHANSERV_FUNC(cmd_invite)
 {
-    struct userData *uData;
     struct userNode *invite;
 
-    uData = GetChannelUser(channel->channel_info, user->handle_info);
-
     if(argc > 1)
     {
         if(!(invite = GetUserH(argv[1])))
@@ -4494,13 +4457,13 @@ static CHANSERV_FUNC(cmd_peek)
     char modes[MODELEN];
     unsigned int n;
     struct helpfile_table table;
+    int opcount = 0, voicecount = 0, srvcount = 0;
 
     irc_make_chanmode(channel, modes);
 
     reply("CSMSG_PEEK_INFO", channel->name);
     reply("CSMSG_PEEK_TOPIC", channel->topic);
     reply("CSMSG_PEEK_MODES", modes);
-    reply("CSMSG_PEEK_USERS", channel->members.used);
 
     table.length = 0;
     table.width = 1;
@@ -4509,12 +4472,23 @@ static CHANSERV_FUNC(cmd_peek)
     for(n = 0; n < channel->members.used; n++)
     {
         mn = channel->members.list[n];
+        if(IsLocal(mn->user))
+            srvcount++;
+        else if(mn->modes & MODE_CHANOP)
+            opcount++;
+        else if(mn->modes & MODE_VOICE)
+            voicecount++;
+
         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
             continue;
         table.contents[table.length] = alloca(sizeof(**table.contents));
         table.contents[table.length][0] = mn->user->nick;
         table.length++;
     }
+
+    reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
+          (channel->members.used - opcount - voicecount - srvcount));
+
     if(table.length)
     {
         reply("CSMSG_PEEK_OPS");
@@ -4932,7 +4906,7 @@ chanserv_support_channels(void)
 static CHANSERV_FUNC(cmd_expire)
 {
     int channel_count = registered_channels;
-    expire_channels(NULL);
+    expire_channels(chanserv);
     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
     return 1;
 }
@@ -5381,7 +5355,7 @@ static MODCMD_FUNC(chan_opt_usergreeting)
 static MODCMD_FUNC(chan_opt_modes)
 {
     struct mod_chanmode *new_modes;
-    char modes[MODELEN];
+    char modes[MAXLEN];
 
     if(argc > 1)
     {
@@ -5394,7 +5368,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, 0)))
+        else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
         {
             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
             return 0;
@@ -6498,11 +6472,9 @@ handle_join(struct modeNode *mNode)
         }
     }
 
-    /* ChanServ will not modify the limits in join-flooded channels.
-       It will also skip DynLimit processing when the user (or srvx)
-       is bursting in, because there are likely more incoming. */
+    /* ChanServ will not modify the limits in join-flooded channels,
+       or when there are enough slots left below the limit. */
     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
-       && !user->uplink->burst
        && !channel->join_flooded
        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
     {
@@ -6553,7 +6525,7 @@ handle_join(struct modeNode *mNode)
                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
                     modes |= MODE_VOICE;
             }
-            if(uData->access >= UL_PRESENT)
+            if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
                 cData->visited = now;
             if(cData->user_greeting)
                 greeting = cData->user_greeting;
@@ -6582,7 +6554,7 @@ handle_join(struct modeNode *mNode)
         }
         if(greeting)
             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
-        if(uData && info)
+        if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
     }
     return 0;
@@ -6621,7 +6593,7 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
             continue;
         }
 
-        if(channel->access >= UL_PRESENT)
+        if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
             channel->channel->visited = now;
 
         if(IsUserAutoOp(channel))
@@ -6708,7 +6680,7 @@ handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
     {
         scan_user_presence(uData, mn->user);
         uData->seen = now;
-        if (uData->access >= UL_PRESENT)
+        if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
             cData->visited = now;
     }
 
@@ -7033,7 +7005,7 @@ 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, 0))
+    if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
        && (change->argc < 2))
     {
         chanserv_conf.default_modes = *change;
@@ -7189,7 +7161,6 @@ user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
 static void
 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
 {
-    struct banData *bData;
     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
     unsigned long set_time, triggered_time, expires_time;
 
@@ -7220,7 +7191,7 @@ ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
     if(!reason || (expires_time && (expires_time < now)))
         return;
 
-    bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
+    add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
 }
 
 static struct suspended *
@@ -7400,7 +7371,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, 0))) {
+       && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
         cData->modes = *modes;
         if(off_channel > 0)
           cData->modes.modes_set |= MODE_REGISTERED;
@@ -7516,7 +7487,7 @@ chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
     saxdb_start_record(ctx, KEY_USERS, 1);
     for(; uData; uData = uData->next)
     {
-        if((uData->access >= UL_PRESENT) && uData->present)
+        if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
             high_present = 1;
         saxdb_start_record(ctx, uData->handle->handle, 0);
         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
@@ -7882,8 +7853,8 @@ init_chanserv(const char *nick)
     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
-    DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
-    DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
+    DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
 
     /* Channel options */