unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
rmtkl.c (8617B)
1 /* 2 * Easily remove *-Lines in bulk 3 * (C) Copyright 2019 Gottem and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #include "unrealircd.h" 21 22 ModuleHeader MOD_HEADER = { 23 "rmtkl", 24 "1.4", 25 "Adds /rmtkl command to easily remove *-Lines in bulk", 26 "Gottem and the UnrealIRCd Team", 27 "unrealircd-6", 28 }; 29 30 #define IsParam(x) (parc > (x) && !BadPtr(parv[(x)])) 31 #define IsNotParam(x) (parc <= (x) || BadPtr(parv[(x)])) 32 33 typedef struct { 34 int type; 35 char flag; 36 char *txt; 37 char *operpriv; 38 } TKLType; 39 40 static void dump_str(Client *client, const char **buf); 41 static TKLType *find_TKLType_by_flag(char flag); 42 void rmtkl_check_options(const char *param, int *skipperm, int *silent); 43 int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent); 44 CMD_FUNC(rmtkl); 45 46 TKLType tkl_types[] = { 47 { TKL_KILL, 'k', "K-Line", "server-ban:kline:remove" }, 48 { TKL_ZAP, 'z', "Z-Line", "server-ban:zline:local:remove" }, 49 { TKL_KILL | TKL_GLOBAL, 'G', "G-Line", "server-ban:gline:remove" }, 50 { TKL_ZAP | TKL_GLOBAL, 'Z', "Global Z-Line", "server-ban:zline:global:remove" }, 51 { TKL_SHUN | TKL_GLOBAL, 's', "Shun", "server-ban:shun:remove" }, 52 // { TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" }, TODO: re-add spamfilter support 53 { 0, 0, "Unknown *-Line", 0 }, 54 }; 55 56 static const char *rmtkl_help[] = { 57 "*** \002Help on /rmtkl\002 *** ", 58 "Removes all TKLs matching the given conditions from the local server, or the entire", 59 "network if it's a global-type ban.", 60 "Syntax:", 61 " \002/rmtkl\002 \037user@host\037 \037type\037 [\037comment\037] [\037-skipperm\037] [\037-silent\037]", 62 "The \037user@host\037 field is a wildcard mask to match the target of a ban.", 63 "The \037type\037 field may contain any number of the following characters:", 64 " k, z, G, Z, s, F and *", 65 " These correspond to (local) K-Line, (local) Z-Line, G-Line, Global Z-Line, (global) Shun and (global) Spamfilter", 66 " (asterisk includes every type besides F)", 67 "The \037comment\037 field is also a wildcard mask to match the reason text of a ban. If specified, it must always", 68 "come \037before\037 the options starting with \002-\002.", 69 "Examples:", 70 " - \002/rmtkl * *\002", 71 " [remove \037all\037 supported TKLs except spamfilters]", 72 " - \002/rmtkl *@*.mx GZ\002 * -skipperm", 73 " [remove all Mexican G/Z-Lines while skipping over permanent ones]", 74 /* " - \002/rmtkl * * *Zombie*\002", 75 " [remove all non-spamfilter bans having \037Zombie\037 in the reason field]", TODO: re-add spamfilter support */ 76 "*** \002End of help\002 ***", 77 NULL 78 }; 79 80 MOD_INIT() 81 { 82 MARK_AS_OFFICIAL_MODULE(modinfo); 83 if (CommandExists("RMTKL")) 84 { 85 config_error("Command RMTKL already exists"); 86 return MOD_FAILED; 87 } 88 CommandAdd(modinfo->handle, "RMTKL", rmtkl, 5, CMD_USER); 89 return MOD_SUCCESS; 90 } 91 92 MOD_LOAD() 93 { 94 if (ModuleGetError(modinfo->handle) != MODERR_NOERROR) 95 { 96 config_error("A critical error occurred when loading module %s: %s", MOD_HEADER.name, ModuleGetErrorStr(modinfo->handle)); 97 return MOD_FAILED; 98 } 99 return MOD_SUCCESS; 100 } 101 102 MOD_UNLOAD() 103 { 104 return MOD_SUCCESS; 105 } 106 107 static void dump_str(Client *client, const char **buf) 108 { 109 if (!MyUser(client)) 110 return; 111 112 // Using sendto_one() instead of sendnumericfmt() because the latter strips indentation and stuff ;] 113 for (; *buf != NULL; buf++) 114 sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *buf); 115 116 // Let user take 8 seconds to read it 117 add_fake_lag(client, 8000); 118 } 119 120 static TKLType *find_TKLType_by_flag(char flag) 121 { 122 TKLType *t; 123 for (t = tkl_types; t->type; t++) 124 if (t->flag == flag) 125 break; 126 return t; 127 } 128 129 void rmtkl_check_options(const char *param, int *skipperm, int *silent) { 130 if (!strcasecmp("-skipperm", param)) 131 *skipperm = 1; 132 if (!strcasecmp("-silent", param)) 133 *silent = 1; 134 } 135 136 int rmtkl_tryremove(Client *client, TKLType *tkltype, TKL *tkl, const char *uhmask, const char *commentmask, int skipperm, int silent) 137 { 138 if (tkl->type != tkltype->type) 139 return 0; 140 141 // Let's not touch Q-Lines 142 if (tkl->type & TKL_NAME) 143 return 0; 144 145 /* Don't touch TKL's that were added through config */ 146 if (tkl->flags & TKL_FLAG_CONFIG) 147 return 0; 148 149 if (TKLIsSpamfilter(tkl)) 150 { 151 #if 0 152 //FIXME: re-add spamfilter support 153 // Is a spamfilter added through IRC, we can remove this if the "user" mask matches the reason 154 if (!match_simple(uhmask, tkl->reason)) 155 return 0; 156 #endif 157 } else 158 if (TKLIsServerBan(tkl)) 159 { 160 if (!match_simple(uhmask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) 161 return 0; 162 163 if (commentmask && !match_simple(commentmask, tkl->ptr.serverban->reason)) 164 return 0; 165 } else 166 return 0; 167 168 if (skipperm && tkl->expire_at == 0) 169 return 0; 170 171 if (!silent) 172 sendnotice_tkl_del(client->name, tkl); 173 174 RunHook(HOOKTYPE_TKL_DEL, client, tkl); 175 176 if (tkl->type & TKL_SHUN) 177 tkl_check_local_remove_shun(tkl); 178 tkl_del_line(tkl); 179 return 1; 180 } 181 182 CMD_FUNC(rmtkl) 183 { 184 TKL *tkl, *next; 185 TKLType *tkltype; 186 const char *types, *uhmask, *commentmask, *p; 187 char tklchar; 188 int tklindex, tklindex2, skipperm, silent; 189 unsigned int count; 190 char broadcast[BUFSIZE]; 191 192 if (!IsULine(client) && !IsOper(client)) 193 { 194 sendnumeric(client, ERR_NOPRIVILEGES); 195 return; 196 } 197 198 if (IsNotParam(1)) 199 { 200 dump_str(client, rmtkl_help); 201 return; 202 } 203 204 if (IsNotParam(2)) 205 { 206 sendnotice(client, "Not enough parameters. Type /RMTKL for help."); 207 return; 208 } 209 210 uhmask = parv[1]; 211 types = parv[2]; 212 commentmask = NULL; 213 skipperm = 0; 214 silent = 0; 215 count = 0; 216 snprintf(broadcast, sizeof(broadcast), ":%s RMTKL %s %s", client->name, types, uhmask); 217 218 // Check for optionals 219 if (IsParam(3)) 220 { 221 // Comment mask, if specified, always goes third 222 if (*parv[3] != '-') 223 commentmask = parv[3]; 224 else 225 rmtkl_check_options(parv[3], &skipperm, &silent); 226 ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[3]); 227 } 228 if (IsParam(4)) 229 { 230 rmtkl_check_options(parv[4], &skipperm, &silent); 231 ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[4]); 232 } 233 if (IsParam(5)) 234 { 235 rmtkl_check_options(parv[5], &skipperm, &silent); 236 ircsnprintf(broadcast, sizeof(broadcast), "%s %s", broadcast, parv[5]); 237 } 238 239 // Wildcard resolves to everything but 'F', since spamfilters are a bit special 240 if (strchr(types, '*')) 241 types = "kzGZs"; 242 243 // Make sure the oper actually has the privileges to remove the *-Lines he wants 244 if (!IsULine(client)) 245 { 246 for (p = types; *p; p++) 247 { 248 tkltype = find_TKLType_by_flag(*p); 249 if (!tkltype->type) 250 continue; 251 252 if (!ValidatePermissionsForPath(tkltype->operpriv, client, NULL, NULL, NULL)) 253 { 254 sendnumeric(client, ERR_NOPRIVILEGES); 255 return; 256 } 257 } 258 } 259 260 // Broadcast the command to other servers *before* we proceed with removal 261 sendto_server(NULL, 0, 0, NULL, "%s", broadcast); 262 263 // Loop over all supported types 264 for (tkltype = tkl_types; tkltype->type; tkltype++) { 265 if (!strchr(types, tkltype->flag)) 266 continue; 267 268 // Loop over all TKL entries, first try the ones in the hash table 269 tklchar = tkl_typetochar(tkltype->type); 270 tklindex = tkl_ip_hash_type(tklchar); 271 if (tklindex >= 0) 272 { 273 for (tklindex2 = 0; tklindex2 < TKLIPHASHLEN2; tklindex2++) 274 { 275 for (tkl = tklines_ip_hash[tklindex][tklindex2]; tkl; tkl = next) 276 { 277 next = tkl->next; 278 count += rmtkl_tryremove(client, tkltype, tkl, uhmask, commentmask, skipperm, silent); 279 } 280 } 281 } 282 283 // Then the regular *-Lines (not an else because certain TKLs might have a hash as well as a plain linked list) 284 tklindex = tkl_hash(tklchar); 285 for (tkl = tklines[tklindex]; tkl; tkl = next) 286 { 287 next = tkl->next; 288 count += rmtkl_tryremove(client, tkltype, tkl, uhmask, commentmask, skipperm, silent); 289 } 290 } 291 292 unreal_log(ULOG_INFO, "tkl", "RMTKL_COMMAND", client, 293 "[rmtkl] $client removed $tkl_removed_count TKLine(s) using /RMTKL", 294 log_data_integer("tkl_removed_count", count)); 295 }