/** * akill.C: This file contains functionality which implements the OperServ AKILL command. * * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team * Rights to this code are as documented in COPYING. * * * Portions of this file were derived from sources bearing the following license: * Copyright © 2003-2004 E. Will et al. * Copyright © 2005-2006 Atheme Development Group * Rights to this code are documented in doc/pod/license.pod. * * $Id: akill.C,v 1.9 2007/09/22 14:27:28 pippijn Exp $ */ #include "atheme.h" #include #include #include #include "servers.h" #include #include static char const rcsid[] = "$Id: akill.C,v 1.9 2007/09/22 14:27:28 pippijn Exp $"; REGISTER_MODULE ("operserv/akill", false, "The Ermyth Team "); static void os_cmd_akill (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_akill_add (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_akill_del (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_akill_list (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_akill_sync (sourceinfo_t *si, int parc, char *parv[]); command_t const os_akill = { "AKILL", N_("Manages network bans."), PRIV_AKILL, 3, os_cmd_akill }; command_t const os_akill_add = { "ADD", N_("Adds a network ban"), AC_NONE, 2, os_cmd_akill_add }; command_t const os_akill_del = { "DEL", N_("Deletes a network ban"), AC_NONE, 1, os_cmd_akill_del }; command_t const os_akill_list = { "LIST", N_("Lists all network bans"), AC_NONE, 1, os_cmd_akill_list }; command_t const os_akill_sync = { "SYNC", N_("Synchronises network bans to servers"), AC_NONE, 0, os_cmd_akill_sync }; E cmdvec os_cmdtree; E helpvec os_helptree; cmdvec os_akill_cmds; static void os_akill_newuser (user_t *u) { kline_t *k; if (is_internal_client (u)) return; k = kline_t::find (u); if (k != NULL) { /* Server didn't have that kline, send it again. * To ensure kline exempt works on akills too, do * not send a KILL. -- jilles */ phandler->kline_sts (u->server->name, k->user, k->host, k->duration ? k->expires - NOW : 0, k->reason); } } static void os_cmd_akill (sourceinfo_t *si, int parc, char *parv[]) { /* Grab args */ char *cmd = parv[0]; command_t const *c; /* Bad/missing arg */ if (!cmd) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "AKILL"); command_fail (si, fault::needmoreparams, _("Syntax: AKILL ADD|DEL|LIST")); return; } c = os_akill_cmds.find (cmd); if (c == NULL) { command_fail (si, fault::badparams, _("Invalid command. Use \2/%s%s help\2 for a command listing."), (ircd->uses_rcommand == false) ? "msg " : "", opersvs.me->disp); return; } c->exec (si->service, si, parc - 1, parv + 1); } static void os_cmd_akill_add (sourceinfo_t *si, int parc, char *parv[]) { user_t *u; char *target = parv[0]; char *token = strtok (parv[1], " "); char star[] = "*"; char *kuser, *khost; char *treason, reason[BUFSIZE]; long duration; char *s; kline_t *k; if (!target || !token) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "AKILL ADD"); command_fail (si, fault::needmoreparams, _("Syntax: AKILL ADD [!P|!T ] ")); return; } if (!strcasecmp (token, "!P")) { duration = 0; treason = strtok (NULL, ""); if (treason) strlcpy (reason, treason, BUFSIZE); else strlcpy (reason, "No reason given", BUFSIZE); } else if (!strcasecmp (token, "!T")) { s = strtok (NULL, " "); treason = strtok (NULL, ""); if (treason) strlcpy (reason, treason, BUFSIZE); else strlcpy (reason, "No reason given", BUFSIZE); if (s) { duration = (atol (s) * 60); while (isdigit (*s)) s++; if (*s == 'h' || *s == 'H') duration *= 60; else if (*s == 'd' || *s == 'D') duration *= 1440; else if (*s == 'w' || *s == 'W') duration *= 10080; else if (*s == '\0') ; else duration = 0; if (duration == 0) { command_fail (si, fault::badparams, _("Invalid duration given.")); command_fail (si, fault::badparams, _("Syntax: AKILL ADD [!P|!T ] ")); return; } } else { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "AKILL ADD"); command_fail (si, fault::needmoreparams, _("Syntax: AKILL ADD [!P|!T ] ")); return; } } else { duration = config_options.kline_time; strlcpy (reason, token, BUFSIZE); treason = strtok (NULL, ""); if (treason) { strlcat (reason, " ", BUFSIZE); strlcat (reason, treason, BUFSIZE); } } if (strchr (target, '!')) { command_fail (si, fault::badparams, _("Invalid character '%c' in user@host."), '!'); return; } if (!(strchr (target, '@'))) { if (!(u = user_find_named (target))) { command_fail (si, fault::nosuch_target, _("\2%s\2 is not on IRC."), target); return; } if (is_internal_client (u) || u == si->su) return; kuser = star; khost = u->host; } else { char *p; int i = 0; kuser = collapse (strtok (target, "@")); khost = collapse (strtok (NULL, "")); if (!kuser || !khost) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "AKILL ADD"); command_fail (si, fault::needmoreparams, _("Syntax: AKILL ADD @ [options] ")); return; } if (strchr (khost, '@')) { command_fail (si, fault::badparams, _("Too many '%c' in user@host."), '@'); command_fail (si, fault::badparams, _("Syntax: AKILL ADD @ [options] ")); return; } /* make sure there's at least 4 non-wildcards */ /* except if the user has no wildcards */ for (p = kuser; *p; p++) { if (*p != '*' && *p != '?' && *p != '.') i++; } for (p = khost; *p; p++) { if (*p != '*' && *p != '?' && *p != '.') i++; } if (i < 4 && (strchr (kuser, '*') || strchr (kuser, '?')) && !has_priv (si, PRIV_AKILL_ANYMASK)) { command_fail (si, fault::badparams, _("Invalid user@host: \2%s@%s\2. At least four non-wildcard characters are required."), kuser, khost); return; } } if (!strcmp (kuser, "*")) { bool unsafe = false; char *p; if (!match (khost, "127.0.0.1") || !match_ips (khost, "127.0.0.1")) unsafe = true; else if (me.vhost != NULL && (!match (khost, me.vhost) || !match_ips (khost, me.vhost))) unsafe = true; else if ((p = strrchr (khost, '/')) != NULL && IsDigit (p[1]) && atoi (p + 1) < 4) unsafe = true; if (unsafe) { command_fail (si, fault::badparams, _("Invalid user@host: \2%s@%s\2. This mask is unsafe."), kuser, khost); logcommand (si, CMDLOG_ADMIN, "failed AKILL ADD %s@%s (unsafe mask)", kuser, khost); return; } } if ((k = kline_t::find (kuser, khost))) { command_fail (si, fault::nochange, _("AKILL \2%s@%s\2 is already matched in the database."), kuser, khost); return; } k = kline_t::create (kuser, khost, reason, duration); k->setby = sstrdup (get_oper_name (si)); if (duration) command_success_nodata (si, _("Timed AKILL on \2%s@%s\2 was successfully added and will expire in %s."), k->user, k->host, timediff (duration)); else command_success_nodata (si, _("AKILL on \2%s@%s\2 was successfully added."), k->user, k->host); snoop ("AKILL:ADD: \2%s@%s\2 by \2%s\2 for \2%s\2", k->user, k->host, get_oper_name (si), k->reason); verbose_wallops ("\2%s\2 is \2adding\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); logcommand (si, CMDLOG_SET, "AKILL ADD %s@%s %s", k->user, k->host, k->reason); } static void os_cmd_akill_del (sourceinfo_t *si, int parc, char *parv[]) { char *target = parv[0]; char *userbuf, *hostbuf; unsigned int number; char *s; kline_t *k; if (!target) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "AKILL DEL"); command_fail (si, fault::needmoreparams, _("Syntax: AKILL DEL ")); return; } if (strchr (target, ',')) { unsigned int start = 0, end = 0, i; char t[16]; s = strtok (target, ","); do { if (strchr (s, ':')) { for (i = 0; *s != ':'; s++, i++) t[i] = *s; t[++i] = '\0'; start = atoi (t); s++; /* skip past the : */ for (i = 0; *s != '\0'; s++, i++) t[i] = *s; t[++i] = '\0'; end = atoi (t); for (i = start; i <= end; i++) { if (!(k = kline_t::find (i))) { command_fail (si, fault::nosuch_target, _("No such AKILL with number \2%d\2."), i); continue; } command_success_nodata (si, _("AKILL on \2%s@%s\2 has been successfully removed."), k->user, k->host); verbose_wallops ("\2%s\2 is \2removing\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); snoop ("AKILL:DEL: \2%s@%s\2 by \2%s\2", k->user, k->host, get_oper_name (si)); logcommand (si, CMDLOG_SET, "AKILL DEL %s@%s", k->user, k->host); kline_t::destroy (k->user, k->host); } continue; } number = atoi (s); if (!(k = kline_t::find (number))) { command_fail (si, fault::nosuch_target, _("No such AKILL with number \2%d\2."), number); return; } command_success_nodata (si, _("AKILL on \2%s@%s\2 has been successfully removed."), k->user, k->host); verbose_wallops ("\2%s\2 is \2removing\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); snoop ("AKILL:DEL: \2%s@%s\2 by \2%s\2", k->user, k->host, get_oper_name (si)); logcommand (si, CMDLOG_SET, "AKILL DEL %s@%s", k->user, k->host); kline_t::destroy (k->user, k->host); } while ((s = strtok (NULL, ","))); return; } if (!strchr (target, '@')) { unsigned int start = 0, end = 0, i; char t[16]; if (strchr (target, ':')) { for (i = 0; *target != ':'; target++, i++) t[i] = *target; t[++i] = '\0'; start = atoi (t); target++; /* skip past the : */ for (i = 0; *target != '\0'; target++, i++) t[i] = *target; t[++i] = '\0'; end = atoi (t); for (i = start; i <= end; i++) { if (!(k = kline_t::find (i))) { command_fail (si, fault::nosuch_target, _("No such AKILL with number \2%d\2."), i); continue; } command_success_nodata (si, _("AKILL on \2%s@%s\2 has been successfully removed."), k->user, k->host); verbose_wallops ("\2%s\2 is \2removing\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); snoop ("AKILL:DEL: \2%s@%s\2 by \2%s\2", k->user, k->host, get_oper_name (si)); kline_t::destroy (k->user, k->host); } return; } number = atoi (target); if (!(k = kline_t::find (number))) { command_fail (si, fault::nosuch_target, _("No such AKILL with number \2%d\2."), number); return; } command_success_nodata (si, _("AKILL on \2%s@%s\2 has been successfully removed."), k->user, k->host); verbose_wallops ("\2%s\2 is \2removing\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); snoop ("AKILL:DEL: \2%s@%s\2 by \2%s\2", k->user, k->host, get_oper_name (si)); logcommand (si, CMDLOG_SET, "AKILL DEL %s@%s", k->user, k->host); kline_t::destroy (k->user, k->host); return; } userbuf = strtok (target, "@"); hostbuf = strtok (NULL, ""); if (!(k = kline_t::find (userbuf, hostbuf))) { command_fail (si, fault::nosuch_target, _("No such AKILL: \2%s@%s\2."), userbuf, hostbuf); return; } command_success_nodata (si, _("AKILL on \2%s@%s\2 has been successfully removed."), userbuf, hostbuf); verbose_wallops ("\2%s\2 is \2removing\2 an \2AKILL\2 for \2%s@%s\2 -- reason: \2%s\2", get_oper_name (si), k->user, k->host, k->reason); snoop ("AKILL:DEL: \2%s@%s\2 by \2%s\2", k->user, k->host, get_oper_name (si)); logcommand (si, CMDLOG_SET, "AKILL DEL %s@%s", k->user, k->host); kline_t::destroy (userbuf, hostbuf); } static void os_cmd_akill_list (sourceinfo_t *si, int parc, char *parv[]) { char *param = parv[0]; bool full = false; kline_t::list_type::iterator klit, klit_end; kline_t *k; if (param != NULL && !strcasecmp (param, "FULL")) full = true; if (full) command_success_nodata (si, _("AKILL list (with reasons):")); else command_success_nodata (si, _("AKILL list:")); for (klit = kline_t::list.begin (), klit_end = kline_t::list.end (); klit != klit_end; ++klit) { k = *klit; if (k->duration && full) command_success_nodata (si, _("%d: %s@%s - by \2%s\2 - expires in \2%s\2 - (%s)"), k->number, k->user, k->host, k->setby, timediff (k->expires > NOW ? k->expires - NOW : 0), k->reason); else if (k->duration && !full) command_success_nodata (si, _("%d: %s@%s - by \2%s\2 - expires in \2%s\2"), k->number, k->user, k->host, k->setby, timediff (k->expires > NOW ? k->expires - NOW : 0)); else if (!k->duration && full) command_success_nodata (si, _("%d: %s@%s - by \2%s\2 - \2permanent\2 - (%s)"), k->number, k->user, k->host, k->setby, k->reason); else command_success_nodata (si, _("%d: %s@%s - by \2%s\2 - \2permanent\2"), k->number, k->user, k->host, k->setby); } command_success_nodata (si, _("Total of \2%d\2 %s in AKILL list."), kline_t::list.size (), (kline_t::list.size () == 1) ? "entry" : "entries"); logcommand (si, CMDLOG_GET, "AKILL LIST%s", full ? " FULL" : ""); } static void os_cmd_akill_sync (sourceinfo_t *si, int parc, char *parv[]) { kline_t::list_type::iterator klit, klit_end; kline_t *k; logcommand (si, CMDLOG_DO, "AKILL SYNC"); snoop ("AKILL:SYNC: \2%s\2", get_oper_name (si)); for (klit = kline_t::list.begin (), klit_end = kline_t::list.end (); klit != klit_end; ++klit) { k = *klit; if (k->duration == 0) phandler->kline_sts ("*", k->user, k->host, 0, k->reason); else if (k->expires > NOW) phandler->kline_sts ("*", k->user, k->host, k->expires - NOW, k->reason); } command_success_nodata (si, _("AKILL list synchronized to servers.")); } bool _modinit (module *m) { os_cmdtree << os_akill; /* Add sub-commands */ os_akill_cmds << os_akill_add; os_akill_cmds << os_akill_del; os_akill_cmds << os_akill_list; os_akill_cmds << os_akill_sync; help_addentry (os_helptree, "AKILL", "help/operserv/akill", NULL); user_t::callback.add.attach (os_akill_newuser); return true; } void _moddeinit () { os_cmdtree >> os_akill; /* Delete sub-commands */ os_akill_cmds >> os_akill_add; os_akill_cmds >> os_akill_del; os_akill_cmds >> os_akill_list; os_akill_cmds >> os_akill_sync; help_delentry (os_helptree, "AKILL"); user_t::callback.add.detach (os_akill_newuser); }