Add the Tools > Ban List window

Reimplements the official client's ban list: a searchable table of the
server's bans with Add / Remove / Edit, an own-ban filter and highlight,
choosable columns, and the Add/Edit dialog whose IP and name patterns
can be read as IPv4/IPv6 wildcards, fixed strings or regular expressions
(translated to the regexes banadd takes exactly as the official client
does). Editing is add-then-delete, since the protocol has no banedit.

The ban list is read from the notifybanlist burst that answers banlist,
like channelpermlist. The menu item is only shown where the server grants
b_client_ban_list.

Also fixes ServerModel numbering permissions from 0 instead of 1, which
had every selfPermissionValue() lookup reading the neighbouring
permission — including the group-assignment menus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 23:00:18 +00:00
parent 636090d075
commit 22e0304f25
20 changed files with 1705 additions and 44 deletions

View File

@@ -3,9 +3,11 @@ package com.ts3client.ui;
import com.ts3client.audio.AudioBackend;
import com.ts3client.config.IdentityEntry;
import com.ts3client.config.IdentityStore;
import com.ts3client.config.BanReasonPresets;
import com.ts3client.config.Settings;
import com.ts3client.net.ChannelNode;
import com.ts3client.net.ClientEntry;
import com.ts3client.net.ServerModel;
import com.ts3client.net.TeamspeakConnection;
import com.ts3client.sound.SoundNotifier;
import com.ts3client.text.TsLink;
@@ -58,6 +60,9 @@ final class ServerTab implements ServerTabConnectionEvents.Listener {
/** Identity used for the current connection, so it can be saved into a bookmark. */
private String identityId = "";
/** The one open ban list window for this server, if any. */
private BanListDialog banList;
ServerTab(MainFrame host, Settings settings, IdentityStore identities, AudioBackend audio,
SoundNotifier sounds) {
this.host = host;
@@ -266,6 +271,33 @@ final class ServerTab implements ServerTabConnectionEvents.Listener {
if (conn.isConnected()) conn.disconnect();
}
/**
* Whether the server grants us {@code b_client_ban_list}. Until it has named its
* permissions nothing can be resolved, so the list is offered and the server left to refuse.
*/
boolean canViewBanList() {
if (!conn.isConnected()) return false;
ServerModel model = conn.getModel();
return !model.hasPermissionNames() || model.selfPermissionValue("b_client_ban_list") > 0;
}
/** Opens the server's ban list, or brings the already open one forward. */
void showBanList(BanReasonPresets presets) {
if (!conn.isConnected()) return;
if (banList == null || !banList.isDisplayable()) {
banList = new BanListDialog(host, conn, settings, presets);
}
banList.setVisible(true);
banList.toFront();
}
/** The server's name changed or the connection went; the ban list window follows. */
void banListServerChanged(boolean connected) {
if (banList == null) return;
if (connected) banList.serverNameChanged();
else banList.connectionLost();
}
/** Releases the background resources of a tab that is being thrown away. */
void dispose() {
conn.getIcons().shutdown();