2020-03-29 02:16:53 -07:00
|
|
|
/*
|
|
|
|
* Easily remove *-Lines in bulk
|
|
|
|
* (C) Copyright 2019 Gottem and the UnrealIRCd team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "unrealircd.h"
|
|
|
|
|
|
|
|
ModuleHeader MOD_HEADER = {
|
|
|
|
"rmtkl",
|
|
|
|
"1.4",
|
|
|
|
"Adds /rmtkl command to easily remove *-Lines in bulk",
|
|
|
|
"Gottem and the UnrealIRCd Team",
|
2022-01-14 21:16:34 -08:00
|
|
|
"unrealircd-6",
|
2020-03-29 02:16:53 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
#define IsParam(x) (parc > (x) && !BadPtr(parv[(x)]))
|
|
|
|
#define IsNotParam(x) (parc <= (x) || BadPtr(parv[(x)]))
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int type;
|
|
|
|
char flag;
|
|
|
|
char *txt;
|
|
|
|
char *operpriv;
|
|
|
|
} TKLType;
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
static void dump_str(Client *client, const char **buf);
|
2020-03-29 02:16:53 -07:00
|
|
|
static TKLType *find_TKLType_by_flag(char flag);
|
2022-01-14 21:16:34 -08:00
|
|
|
void rmtkl_check_options(const char *param, int *skipperm, int *silent);
|
|
|
|
int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent);
|
2020-03-29 02:16:53 -07:00
|
|
|
CMD_FUNC(rmtkl);
|
|
|
|
|
|
|
|
TKLType tkl_types[] = {
|
|
|
|
{ TKL_KILL, 'k', "K-Line", "server-ban:kline:remove" },
|
|
|
|
{ TKL_ZAP, 'z', "Z-Line", "server-ban:zline:local:remove" },
|
|
|
|
{ TKL_KILL | TKL_GLOBAL, 'G', "G-Line", "server-ban:gline:remove" },
|
|
|
|
{ TKL_ZAP | TKL_GLOBAL, 'Z', "Global Z-Line", "server-ban:zline:global:remove" },
|
|
|
|
{ TKL_SHUN | TKL_GLOBAL, 's', "Shun", "server-ban:shun:remove" },
|
|
|
|
// { TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" }, TODO: re-add spamfilter support
|
|
|
|
{ 0, 0, "Unknown *-Line", 0 },
|
|
|
|
};
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
static const char *rmtkl_help[] = {
|
2020-03-29 02:16:53 -07:00
|
|
|
"*** \002Help on /rmtkl\002 *** ",
|
|
|
|
"Removes all TKLs matching the given conditions from the local server, or the entire",
|
|
|
|
"network if it's a global-type ban.",
|
|
|
|
"Syntax:",
|
|
|
|
" \002/rmtkl\002 \037user@host\037 \037type\037 [\037comment\037] [\037-skipperm\037] [\037-silent\037]",
|
|
|
|
"The \037user@host\037 field is a wildcard mask to match the target of a ban.",
|
|
|
|
"The \037type\037 field may contain any number of the following characters:",
|
|
|
|
" k, z, G, Z, s, F and *",
|
|
|
|
" These correspond to (local) K-Line, (local) Z-Line, G-Line, Global Z-Line, (global) Shun and (global) Spamfilter",
|
|
|
|
" (asterisk includes every type besides F)",
|
|
|
|
"The \037comment\037 field is also a wildcard mask to match the reason text of a ban. If specified, it must always",
|
|
|
|
"come \037before\037 the options starting with \002-\002.",
|
|
|
|
"Examples:",
|
|
|
|
" - \002/rmtkl * *\002",
|
|
|
|
" [remove \037all\037 supported TKLs except spamfilters]",
|
|
|
|
" - \002/rmtkl *@*.mx GZ\002 * -skipperm",
|
|
|
|
" [remove all Mexican G/Z-Lines while skipping over permanent ones]",
|
|
|
|
/* " - \002/rmtkl * * *Zombie*\002",
|
|
|
|
" [remove all non-spamfilter bans having \037Zombie\037 in the reason field]", TODO: re-add spamfilter support */
|
|
|
|
"*** \002End of help\002 ***",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
MOD_INIT()
|
|
|
|
{
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
if (CommandExists("RMTKL"))
|
|
|
|
{
|
|
|
|
config_error("Command RMTKL already exists");
|
|
|
|
return MOD_FAILED;
|
|
|
|
}
|
|
|
|
CommandAdd(modinfo->handle, "RMTKL", rmtkl, 5, CMD_USER);
|
|
|
|
return MOD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOD_LOAD()
|
|
|
|
{
|
|
|
|
if (ModuleGetError(modinfo->handle) != MODERR_NOERROR)
|
|
|
|
{
|
|
|
|
config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle));
|
|
|
|
return MOD_FAILED;
|
|
|
|
}
|
|
|
|
return MOD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOD_UNLOAD()
|
|
|
|
{
|
|
|
|
return MOD_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
static void dump_str(Client *client, const char **buf)
|
2020-03-29 02:16:53 -07:00
|
|
|
{
|
|
|
|
if (!MyUser(client))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Using sendto_one() instead of sendnumericfmt() because the latter strips indentation and stuff ;]
|
|
|
|
for (; *buf != NULL; buf++)
|
|
|
|
sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *buf);
|
|
|
|
|
|
|
|
// Let user take 8 seconds to read it
|
2022-01-14 21:16:34 -08:00
|
|
|
add_fake_lag(client, 8000);
|
2020-03-29 02:16:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static TKLType *find_TKLType_by_flag(char flag)
|
|
|
|
{
|
|
|
|
TKLType *t;
|
|
|
|
for (t = tkl_types; t->type; t++)
|
|
|
|
if (t->flag == flag)
|
|
|
|
break;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
void rmtkl_check_options(const char *param, int *skipperm, int *silent) {
|
2020-03-29 02:16:53 -07:00
|
|
|
if (!strcasecmp("-skipperm", param))
|
|
|
|
*skipperm = 1;
|
|
|
|
if (!strcasecmp("-silent", param))
|
|
|
|
*silent = 1;
|
|
|
|
}
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent)
|
2020-03-29 02:16:53 -07:00
|
|
|
{
|
|
|
|
if (tkl->type != tkltype->type)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Let's not touch Q-Lines
|
|
|
|
if (tkl->type & TKL_NAME)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Don't touch TKL's that were added through config */
|
|
|
|
if (tkl->flags & TKL_FLAG_CONFIG)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (TKLIsSpamfilter(tkl))
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
//FIXME: re-add spamfilter support
|
|
|
|
// Is a spamfilter added through IRC, we can remove this if the "user" mask matches the reason
|
|
|
|
if (!match_simple(uhmask, tkl->reason))
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
} else
|
|
|
|
if (TKLIsServerBan(tkl))
|
|
|
|
{
|
|
|
|
if (!match_simple(uhmask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (commentmask && !match_simple(commentmask, tkl->ptr.serverban->reason))
|
|
|
|
return 0;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (skipperm && tkl->expire_at == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!silent)
|
|
|
|
sendnotice_tkl_del(client->name, tkl);
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
RunHook(HOOKTYPE_TKL_DEL, client, tkl);
|
2020-03-29 02:16:53 -07:00
|
|
|
|
|
|
|
if (tkl->type & TKL_SHUN)
|
|
|
|
tkl_check_local_remove_shun(tkl);
|
|
|
|
tkl_del_line(tkl);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CMD_FUNC(rmtkl)
|
|
|
|
{
|
|
|
|
TKL *tkl, *next;
|
|
|
|
TKLType *tkltype;
|
2022-01-14 21:16:34 -08:00
|
|
|
const char *types, *uhmask, *commentmask, *p;
|
2020-03-29 02:16:53 -07:00
|
|
|
char tklchar;
|
|
|
|
int tklindex, tklindex2, skipperm, silent;
|
|
|
|
unsigned int count;
|
|
|
|
char broadcast[BUFSIZE];
|
|
|
|
|
|
|
|
if (!IsULine(client) && !IsOper(client))
|
|
|
|
{
|
|
|
|
sendnumeric(client, ERR_NOPRIVILEGES);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsNotParam(1))
|
|
|
|
{
|
|
|
|
dump_str(client, rmtkl_help);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsNotParam(2))
|
|
|
|
{
|
|
|
|
sendnotice(client, "Not enough parameters. Type /RMTKL for help.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uhmask = parv[1];
|
|
|
|
types = parv[2];
|
|
|
|
commentmask = NULL;
|
|
|
|
skipperm = 0;
|
|
|
|
silent = 0;
|
|
|
|
count = 0;
|
|
|
|
snprintf(broadcast, sizeof(broadcast), ":%s RMTKL %s %s", client->name, types, uhmask);
|
|
|
|
|
|
|
|
// Check for optionals
|
|
|
|
if (IsParam(3))
|
|
|
|
{
|
|
|
|
// Comment mask, if specified, always goes third
|
|
|
|
if (*parv[3] != '-')
|
|
|
|
commentmask = parv[3];
|
|
|
|
else
|
|
|
|
rmtkl_check_options(parv[3], &skipperm, &silent);
|
|
|
|
ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[3]);
|
|
|
|
}
|
|
|
|
if (IsParam(4))
|
|
|
|
{
|
|
|
|
rmtkl_check_options(parv[4], &skipperm, &silent);
|
|
|
|
ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[4]);
|
|
|
|
}
|
|
|
|
if (IsParam(5))
|
|
|
|
{
|
|
|
|
rmtkl_check_options(parv[5], &skipperm, &silent);
|
|
|
|
ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[5]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wildcard resolves to everything but 'F', since spamfilters are a bit special
|
|
|
|
if (strchr(types, '*'))
|
|
|
|
types = "kzGZs";
|
|
|
|
|
|
|
|
// Make sure the oper actually has the privileges to remove the *-Lines he wants
|
|
|
|
if (!IsULine(client))
|
|
|
|
{
|
|
|
|
for (p = types; *p; p++)
|
|
|
|
{
|
|
|
|
tkltype = find_TKLType_by_flag(*p);
|
|
|
|
if (!tkltype->type)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!ValidatePermissionsForPath(tkltype->operpriv, client, NULL, NULL, NULL))
|
|
|
|
{
|
|
|
|
sendnumeric(client, ERR_NOPRIVILEGES);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Broadcast the command to other servers *before* we proceed with removal
|
|
|
|
sendto_server(NULL, 0, 0, NULL, "%s", broadcast);
|
|
|
|
|
|
|
|
// Loop over all supported types
|
|
|
|
for (tkltype = tkl_types; tkltype->type; tkltype++) {
|
|
|
|
if (!strchr(types, tkltype->flag))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Loop over all TKL entries, first try the ones in the hash table
|
|
|
|
tklchar = tkl_typetochar(tkltype->type);
|
|
|
|
tklindex = tkl_ip_hash_type(tklchar);
|
|
|
|
if (tklindex >= 0)
|
|
|
|
{
|
|
|
|
for (tklindex2 = 0; tklindex2 < TKLIPHASHLEN2; tklindex2++)
|
|
|
|
{
|
|
|
|
for (tkl = tklines_ip_hash[tklindex][tklindex2]; tkl; tkl = next)
|
|
|
|
{
|
|
|
|
next = tkl->next;
|
|
|
|
count += rmtkl_tryremove(client, tkltype, tkl, uhmask, commentmask, skipperm, silent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then the regular *-Lines (not an else because certain TKLs might have a hash as well as a plain linked list)
|
|
|
|
tklindex = tkl_hash(tklchar);
|
|
|
|
for (tkl = tklines[tklindex]; tkl; tkl = next)
|
|
|
|
{
|
|
|
|
next = tkl->next;
|
|
|
|
count += rmtkl_tryremove(client, tkltype, tkl, uhmask, commentmask, skipperm, silent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-14 21:16:34 -08:00
|
|
|
unreal_log(ULOG_INFO, "tkl", "RMTKL_COMMAND", client,
|
|
|
|
"[rmtkl] $client removed $tkl_removed_count TKLine(s) using /RMTKL",
|
|
|
|
log_data_integer("tkl_removed_count", count));
|
2020-03-29 02:16:53 -07:00
|
|
|
}
|