/* * Copyright (c) 2005-2007 Atheme Development Group * Rights to this code are as documented in doc/LICENSE. * * Autokline channels. * */ #include "atheme-compat.h" DECLARE_MODULE_V1 ( "contrib/os_klinechan", false, _modinit, _moddeinit, PACKAGE_STRING, "Jilles Tjoelker " ); static void os_cmd_klinechan(sourceinfo_t *si, int parc, char *parv[]); static void os_cmd_listklinechans(sourceinfo_t *si, int parc, char *parv[]); command_t os_klinechan = { "KLINECHAN", "Klines all users joining a channel.", PRIV_MASS_AKILL, 3, os_cmd_klinechan, { .path = "contrib/klinechan" } }; command_t os_listklinechans = { "LISTKLINECHAN", "Lists active K:line channels.", PRIV_MASS_AKILL, 1, os_cmd_listklinechans, { .path = "contrib/listklinechans" } }; static void klinechan_check_join(hook_channel_joinpart_t *hdata); static void klinechan_show_info(hook_channel_req_t *hdata); void _modinit(module_t *m) { service_named_bind_command("operserv", &os_klinechan); service_named_bind_command("operserv", &os_listklinechans); hook_add_event("channel_join"); hook_add_first_channel_join(klinechan_check_join); hook_add_event("channel_info"); hook_add_channel_info(klinechan_show_info); } void _moddeinit(module_unload_intent_t intent) { service_named_unbind_command("operserv", &os_klinechan); service_named_unbind_command("operserv", &os_listklinechans); hook_del_channel_join(klinechan_check_join); hook_del_channel_info(klinechan_show_info); } static void klinechan_check_join(hook_channel_joinpart_t *hdata) { mychan_t *mc; chanuser_t *cu = hdata->cu; service_t *svs; char reason[256]; const char *khost; svs = service_find("operserv"); if (svs == NULL) return; if (cu == NULL || is_internal_client(cu->user)) return; if (!(mc = MYCHAN_FROM(cu->chan))) return; if (metadata_find(mc, "private:klinechan:closer")) { khost = cu->user->ip ? cu->user->ip : cu->user->host; if (has_priv_user(cu->user, PRIV_JOIN_STAFFONLY)) notice(svs->me->nick, cu->user->nick, "Warning: %s klines normal users", cu->chan->name); else if (is_autokline_exempt(cu->user)) { char buf[BUFSIZE]; snprintf(buf, sizeof(buf), "Not klining *@%s due to klinechan %s (user %s!%s@%s is exempt)", khost, cu->chan->name, cu->user->nick, cu->user->user, cu->user->host); wallops_sts(buf); } else { snprintf(reason, sizeof reason, "Joining %s", cu->chan->name); slog(LG_INFO, "klinechan_check_join(): klining \2*@%s\2 (user \2%s!%s@%s\2 joined \2%s\2)", khost, cu->user->nick, cu->user->user, cu->user->host, cu->chan->name); kline_sts("*", "*", khost, 86400, reason); } } } static void klinechan_show_info(hook_channel_req_t *hdata) { metadata_t *md; const char *setter, *reason; time_t ts; struct tm tm; char strfbuf[BUFSIZE]; if (!has_priv(hdata->si, PRIV_CHAN_AUSPEX)) return; md = metadata_find(hdata->mc, "private:klinechan:closer"); if (md == NULL) return; setter = md->value; md = metadata_find(hdata->mc, "private:klinechan:reason"); reason = md != NULL ? md->value : "unknown"; md = metadata_find(hdata->mc, "private:klinechan:timestamp"); ts = md != NULL ? atoi(md->value) : 0; tm = *localtime(&ts); strftime(strfbuf, sizeof strfbuf, TIME_FORMAT, &tm); command_success_nodata(hdata->si, "%s had \2automatic klines\2 enabled on it by %s on %s (%s)", hdata->mc->name, setter, strfbuf, reason); } static void os_cmd_klinechan(sourceinfo_t *si, int parc, char *parv[]) { char *target = parv[0]; char *action = parv[1]; char *reason = parv[2]; mychan_t *mc; if (!target || !action) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "KLINECHAN"); command_fail(si, fault_needmoreparams, "Usage: KLINECHAN <#channel> [reason]"); return; } if (!(mc = mychan_find(target))) { command_fail(si, fault_nosuch_target, "\2%s\2 is not registered.", target); return; } if (!strcasecmp(action, "ON")) { if (!reason) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "KLINECHAN"); command_fail(si, fault_needmoreparams, "Usage: KLINECHAN <#channel> ON "); return; } if (mc->flags & CHAN_LOG) { command_fail(si, fault_noprivs, "\2%s\2 cannot be closed.", target); return; } if (metadata_find(mc, "private:klinechan:closer")) { command_fail(si, fault_nochange, "\2%s\2 is already on autokline.", target); return; } metadata_add(mc, "private:klinechan:closer", si->su->nick); metadata_add(mc, "private:klinechan:reason", reason); metadata_add(mc, "private:klinechan:timestamp", number_to_string(CURRTIME)); wallops("%s enabled automatic klines on the channel \2%s\2 (%s).", get_oper_name(si), target, reason); logcommand(si, CMDLOG_ADMIN, "KLINECHAN:ON: \2%s\2 (reason: \2%s\2)", target, reason); command_success_nodata(si, "Klining all users joining \2%s\2.", target); } else if (!strcasecmp(action, "OFF")) { if (!metadata_find(mc, "private:klinechan:closer")) { command_fail(si, fault_nochange, "\2%s\2 is not closed.", target); return; } metadata_delete(mc, "private:klinechan:closer"); metadata_delete(mc, "private:klinechan:reason"); metadata_delete(mc, "private:klinechan:timestamp"); wallops("%s disabled automatic klines on the channel \2%s\2.", get_oper_name(si), target); logcommand(si, CMDLOG_ADMIN, "KLINECHAN:OFF: \2%s\2", target); command_success_nodata(si, "No longer klining users joining \2%s\2.", target); } else { command_fail(si, fault_badparams, STR_INVALID_PARAMS, "KLINECHAN"); command_fail(si, fault_badparams, "Usage: KLINECHAN <#channel> [reason]"); } } static void os_cmd_listklinechans(sourceinfo_t *si, int parc, char *parv[]) { const char *pattern; mowgli_patricia_iteration_state_t state; mychan_t *mc; metadata_t *md; int matches = 0; pattern = parc >= 1 ? parv[0] : "*"; MOWGLI_PATRICIA_FOREACH(mc, &state, mclist) { md = metadata_find(mc, "private:klinechan:closer"); if (md == NULL) continue; if (!match(pattern, mc->name)) { command_success_nodata(si, "- %-30s", mc->name); matches++; } } logcommand(si, CMDLOG_ADMIN, "LISTKLINECHANS: \2%s\2 (\2%d\2 matches)", pattern, matches); if (matches == 0) command_success_nodata(si, _("No K:line channels matched pattern \2%s\2"), pattern); else command_success_nodata(si, ngettext(N_("\2%d\2 match for pattern \2%s\2"), N_("\2%d\2 matches for pattern \2%s\2"), matches), matches, pattern); }