/** * rwatch.C: This file contains functionality implementing OperServ RWATCH. * * 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 © 2006 Atheme Development Group * Rights to this code are documented in doc/LICENCE. * * $Id: rwatch.C,v 1.7 2007/09/22 14:27:28 pippijn Exp $ */ #include "atheme.h" #include static char const rcsid[] = "$Id: rwatch.C,v 1.7 2007/09/22 14:27:28 pippijn Exp $"; REGISTER_MODULE ("operserv/rwatch", false, "The Ermyth Team "); static void os_cmd_rwatch (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_rwatch_list (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_rwatch_add (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_rwatch_del (sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_rwatch_set (sourceinfo_t *si, int parc, char *parv[]); static void write_rwatchdb (void); static void load_rwatchdb (void); E cmdvec os_cmdtree; E helpvec os_helptree; cmdvec os_rwatch_cmds; list_t rwatch_list; #define RWACT_SNOOP 1 #define RWACT_KLINE 2 typedef struct rwatch_ rwatch_t; struct rwatch_ { char *regex; int reflags; /* AREGEX_* */ char *reason; int actions; /* RWACT_* */ regex_t *re; }; command_t const os_rwatch = { "RWATCH", N_("Performs actions on connecting clients matching regexes."), PRIV_USER_AUSPEX, 2, os_cmd_rwatch }; command_t const os_rwatch_add = { "ADD", N_("Adds an entry to the regex watch list."), AC_NONE, 1, os_cmd_rwatch_add }; command_t const os_rwatch_del = { "DEL", N_("Removes an entry from the regex watch list."), AC_NONE, 1, os_cmd_rwatch_del }; command_t const os_rwatch_list = { "LIST", N_("Displays the regex watch list."), AC_NONE, 1, os_cmd_rwatch_list }; command_t const os_rwatch_set = { "SET", N_("Changes actions on an entry in the regex watch list"), AC_NONE, 1, os_cmd_rwatch_set }; static void write_rwatchdb (void) { FILE *f; node_t *n; rwatch_t *rw; if (!(f = fopen (DATADIR "/rwatch.db.new", "w"))) { slog (LG_ERROR, "write_rwatchdb(): cannot write rwatch database: %s", strerror (errno)); return; } LIST_FOREACH (n, rwatch_list.head) { rw = static_cast < rwatch_t * >(n->data); fprintf (f, "RW %d %s\n", rw->reflags, rw->regex); fprintf (f, "RR %d %s\n", rw->actions, rw->reason); } fclose (f); if ((rename (DATADIR "/rwatch.db.new", DATADIR "/rwatch.db")) < 0) { slog (LG_ERROR, "write_rwatchdb(): couldn't rename rwatch database."); return; } } static void load_rwatchdb (void) { FILE *f; char *item, rBuf[BUFSIZE * 2]; rwatch_t *rw = NULL; if (!(f = fopen (DATADIR "/rwatch.db", "r"))) { slog (LG_DEBUG, "load_rwatchdb(): cannot open rwatch database: %s", strerror (errno)); return; } while (fgets (rBuf, BUFSIZE * 2, f)) { item = strtok (rBuf, " "); strip (item); if (!strcmp (item, "RW")) { char *reflagsstr = strtok (NULL, " "); char *regex = strtok (NULL, "\n"); if (!reflagsstr || !regex || rw) ; /* erroneous, don't add */ else { rw = new rwatch_t; rw->regex = sstrdup (regex); rw->reflags = atoi (reflagsstr); rw->re = regex_create (rw->regex, rw->reflags); } } else if (!strcmp (item, "RR")) { char *actionstr = strtok (NULL, " "); char *reason = strtok (NULL, "\n"); if (!actionstr || !reason || !rw) ; /* erroneous, don't add */ else { rw->actions = atoi (actionstr); rw->reason = sstrdup (reason); node_add (rw, node_create (), &rwatch_list); rw = NULL; } } } fclose (f); } static void os_cmd_rwatch (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, "RWATCH"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH ADD|DEL|LIST|SET")); return; } c = os_rwatch_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 " : "", si->service->disp); return; } c->exec (si->service, si, parc - 1, parv + 1); } static void os_cmd_rwatch_add (sourceinfo_t *si, int parc, char *parv[]) { node_t *n; char *pattern; char *reason; regex_t *regex; rwatch_t *rw; int flags; char *args = parv[0]; if (args == NULL) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RWATCH ADD"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH ADD //[i] ")); return; } pattern = regex_extract (args, &args, &flags); if (pattern == NULL) { command_fail (si, fault::badparams, STR_INVALID_PARAMS, "RWATCH ADD"); command_fail (si, fault::badparams, _("Syntax: RWATCH ADD //[i] ")); return; } reason = args; while (*reason == ' ') reason++; if (*reason == '\0') { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RWATCH ADD"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH ADD //[i] ")); return; } LIST_FOREACH (n, rwatch_list.head) { rwatch_t *t = static_cast < rwatch_t * >(n->data); if (!strcmp (pattern, t->regex)) { command_fail (si, fault::nochange, _("\2%s\2 already found in regex watch list; not adding."), pattern); return; } } regex = regex_create (pattern, flags); if (regex == NULL) { command_fail (si, fault::badparams, _("The provided regex \2%s\2 is invalid."), pattern); return; } rw = new rwatch_t; rw->regex = sstrdup (pattern); rw->reflags = flags; rw->reason = sstrdup (reason); rw->actions = RWACT_SNOOP; rw->re = regex; node_add (rw, node_create (), &rwatch_list); command_success_nodata (si, _("Added \2%s\2 to regex watch list."), pattern); snoop ("RWATCH:ADD: \2%s\2 by \2%s\2", pattern, get_oper_name (si)); logcommand (si, CMDLOG_ADMIN, "RWATCH ADD %s %s", pattern, reason); write_rwatchdb (); } static void os_cmd_rwatch_del (sourceinfo_t *si, int parc, char *parv[]) { node_t *n, *tn; char *pattern; int flags; char *args = parv[0]; if (args == NULL) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RWATCH DEL"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH DEL //[i]")); return; } pattern = regex_extract (args, &args, &flags); if (pattern == NULL) { command_fail (si, fault::badparams, STR_INVALID_PARAMS, "RWATCH DEL"); command_fail (si, fault::badparams, _("Syntax: RWATCH DEL //[i]")); return; } LIST_FOREACH_SAFE (n, tn, rwatch_list.head) { rwatch_t *rw = static_cast < rwatch_t * >(n->data); if (!strcmp (rw->regex, pattern)) { if (rw->actions & RWACT_KLINE) { if (!has_priv (si, PRIV_MASS_AKILL)) { command_fail (si, fault::noprivs, _("You do not have %s privilege."), PRIV_MASS_AKILL); return; } wallops ("\2%s\2 disabled kline on regex watch pattern \2%s\2", get_oper_name (si), pattern); } sfree (rw->regex); sfree (rw->reason); if (rw->re != NULL) regex_destroy (rw->re); delete rw; node_del (n, &rwatch_list); node_free (n); command_success_nodata (si, _("Removed \2%s\2 from regex watch list."), pattern); snoop ("RWATCH:DEL: \2%s\2 by \2%s\2", pattern, get_oper_name (si)); logcommand (si, CMDLOG_ADMIN, "RWATCH DEL %s", pattern); write_rwatchdb (); return; } } command_fail (si, fault::nochange, _("\2%s\2 not found in regex watch list."), pattern); } static void os_cmd_rwatch_list (sourceinfo_t *si, int parc, char *parv[]) { node_t *n; LIST_FOREACH (n, rwatch_list.head) { rwatch_t *rw = static_cast < rwatch_t * >(n->data); command_success_nodata (si, "%s (%s%s%s) - %s", rw->regex, rw->reflags & AREGEX_ICASE ? "i" : "", rw->actions & RWACT_SNOOP ? "S" : "", rw->actions & RWACT_KLINE ? "\2K\2" : "", rw->reason); } command_success_nodata (si, _("End of RWATCH LIST")); logcommand (si, CMDLOG_GET, "RWATCH LIST"); } static void os_cmd_rwatch_set (sourceinfo_t *si, int parc, char *parv[]) { node_t *n, *tn; char *pattern; char *opts; int addflags = 0, removeflags = 0; int flags; char *args = parv[0]; if (args == NULL) { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RWATCH SET"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH SET //[i] [KLINE] [NOKLINE] [SNOOP] [NOSNOOP]")); return; } pattern = regex_extract (args, &args, &flags); if (pattern == NULL) { command_fail (si, fault::badparams, STR_INVALID_PARAMS, "RWATCH SET"); command_fail (si, fault::badparams, _("Syntax: RWATCH SET //[i] [KLINE] [NOKLINE] [SNOOP] [NOSNOOP]")); return; } while (*args == ' ') args++; if (*args == '\0') { command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RWATCH SET"); command_fail (si, fault::needmoreparams, _("Syntax: RWATCH SET //[i] [KLINE] [NOKLINE] [SNOOP] [NOSNOOP]")); return; } opts = args; while (*args != '\0') { if (!strncasecmp (args, "KLINE", 5)) addflags |= RWACT_KLINE, removeflags &= ~RWACT_KLINE, args += 5; else if (!strncasecmp (args, "NOKLINE", 7)) removeflags |= RWACT_KLINE, addflags &= ~RWACT_KLINE, args += 7; else if (!strncasecmp (args, "SNOOP", 5)) addflags |= RWACT_SNOOP, removeflags &= ~RWACT_SNOOP, args += 5; else if (!strncasecmp (args, "NOSNOOP", 7)) removeflags |= RWACT_SNOOP, addflags &= ~RWACT_SNOOP, args += 7; if (*args != '\0' && *args != ' ') { command_fail (si, fault::badparams, STR_INVALID_PARAMS, "RWATCH SET"); command_fail (si, fault::badparams, _("Syntax: RWATCH SET //[i] [KLINE] [NOKLINE] [SNOOP] [NOSNOOP]")); return; } while (*args == ' ') args++; } if ((addflags | removeflags) & RWACT_KLINE && !has_priv (si, PRIV_MASS_AKILL)) { command_fail (si, fault::noprivs, _("You do not have %s privilege."), PRIV_MASS_AKILL); return; } LIST_FOREACH_SAFE (n, tn, rwatch_list.head) { rwatch_t *rw = static_cast < rwatch_t * >(n->data); if (!strcmp (rw->regex, pattern)) { if (((~rw->actions & addflags) | (rw->actions & removeflags)) == 0) { command_fail (si, fault::nochange, _("Options for \2%s\2 unchanged."), pattern); return; } rw->actions |= addflags; rw->actions &= ~removeflags; command_success_nodata (si, _("Set options \2%s\2 on \2%s\2."), opts, pattern); snoop ("RWATCH:SET: \2%s\2 \2%s\2 by \2%s\2", pattern, opts, get_oper_name (si)); if (addflags & RWACT_KLINE) wallops ("\2%s\2 enabled kline on regex watch pattern \2%s\2", get_oper_name (si), pattern); if (removeflags & RWACT_KLINE) wallops ("\2%s\2 disabled kline on regex watch pattern \2%s\2", get_oper_name (si), pattern); logcommand (si, CMDLOG_ADMIN, "RWATCH SET %s %s", pattern, opts); write_rwatchdb (); return; } } command_fail (si, fault::nosuch_target, _("\2%s\2 not found in regex watch list."), pattern); } static void rwatch_newuser (user_t *u) { char usermask[NICKLEN + USERLEN + HOSTLEN + GECOSLEN]; node_t *n; rwatch_t *rw; if (is_internal_client (u)) return; snprintf (usermask, sizeof usermask, "%s!%s@%s %s", u->nick, u->user, u->host, u->gecos); LIST_FOREACH (n, rwatch_list.head) { rw = static_cast < rwatch_t * >(n->data); if (!rw->re) continue; if (regex_match (rw->re, usermask)) { if (rw->actions & RWACT_SNOOP) { snoop ("RWATCH:%s %s matches \2%s\2 (%s)", rw->actions & RWACT_KLINE ? "KLINE:" : "", usermask, rw->regex, rw->reason); } if (rw->actions & RWACT_KLINE) { slog (LG_INFO, "rwatch_newuser(): klining *@%s (user %s!%s@%s matches %s %s)", u->host, u->nick, u->user, u->host, rw->regex, rw->reason); phandler->kline_sts ("*", "*", u->host, 86400, rw->reason); } } } } bool _modinit (module *m) { os_cmdtree << os_rwatch; os_rwatch_cmds << os_rwatch_add; os_rwatch_cmds << os_rwatch_del; os_rwatch_cmds << os_rwatch_list; os_rwatch_cmds << os_rwatch_set; help_addentry (os_helptree, "RWATCH", "help/operserv/rwatch", NULL); user_t::callback.add.attach (rwatch_newuser); load_rwatchdb (); return true; } void _moddeinit (void) { node_t *n, *tn; LIST_FOREACH_SAFE (n, tn, rwatch_list.head) { rwatch_t *rw = static_cast < rwatch_t * >(n->data); sfree (rw->regex); sfree (rw->reason); if (rw->re != NULL) regex_destroy (rw->re); delete rw; node_del (n, &rwatch_list); node_free (n); } os_cmdtree >> os_rwatch; os_rwatch_cmds >> os_rwatch_add; os_rwatch_cmds >> os_rwatch_del; os_rwatch_cmds >> os_rwatch_list; os_rwatch_cmds >> os_rwatch_set; help_delentry (os_helptree, "RWATCH"); user_t::callback.add.detach (rwatch_newuser); }