Initial commit.

This commit is contained in:
William Pitcock 2012-04-17 15:09:50 -05:00
commit 740371b4e2
57 changed files with 15884 additions and 0 deletions

6382
.deps Normal file

File diff suppressed because it is too large Load Diff

65
Makefile Normal file
View File

@ -0,0 +1,65 @@
# Copyright (c) 2003-2004 E. Will et al.
# Rights to this code are documented in doc/LICENSE.
#
# This file contains build instructions.
#
# $Id: Makefile.in 8375 2007-06-03 20:03:26Z pippijn $
#
MODULE = contrib
SRCS = \
cs_access_alias.c \
cs_badwords.c \
cs_fregister.c \
cs_kickdots.c \
cs_ping.c \
cs_regmode.c \
cs_regnotice.c \
cs_updown.c \
cs_userinfo.c \
dnsbl.c \
gen_echoserver.c \
gen_listenerdemo.c \
gen_vhostonreg.c \
graphtastical.c \
gs_roulette.c \
ircd_announceserv.c \
ircd_catserv.c \
ms_fsend.c \
ns_cleannick.c \
ns_fenforce.c \
ns_fregister.c \
ns_forbid.c \
ns_generatehash.c \
ns_generatepass.c \
ns_guestnoreg.c \
ns_listlogins.c \
ns_mxcheck.c \
ns_mxcheck_async.c \
ns_regnotice.c \
ns_waitreg.c \
on_db_save.c \
os_akillnicklist.c \
os_defcon.c \
os_joinmon.c \
os_kill.c \
os_klinechan.c \
os_modeall.c \
os_pingspam.c \
os_resolve.c \
os_savechanmodes.c \
os_tabletest.c \
os_testcmd.c \
os_testproc.c \
os_trace.c \
wumpus.c
include ../../extra.mk
include ../../buildsys.mk
include ../../buildsys.module.mk
CPPFLAGS += -I../../include
CFLAGS += ${PLUGIN_CFLAGS}
LIBS += -L../../libathemecore -lathemecore ${LDFLAGS_RPATH}

153
README Normal file
View File

@ -0,0 +1,153 @@
This directory contains modules that are not included in the main modules
folders for one reason or another. Either their code is a bit ugly, their
functionality is frowned upon, they only work with a limited number of IRCd's
or similar cases.
Modules
=======
cs_babbler.c - Repeats what others users in a channel say back to a specific
user. Useful for users that claim they have entire channels on
ignore. NOT COMPILED BY DEFAULT.
cs_badwords.c - Takes actions against users (KICK, BAN, KICKBAN or QUIET) for
using badwords in channel, specified on a per-channel basis
with the BADWORDS command. Be aware it will check every message
sent to channels that BLOCKBADWORDS is set on so it can be a bit
CPU-heavy.
cs_kickdots.c - Kicks users from a channel when kickdots metadata is set on
that channel and users send a line containing only "...".
Deprecated by cs_badwords (/cs badwords #channel add ... kick).
cs_ping.c - Responds to users that ping ChanServ with "Pong!".
cs_regmode.c - Sets the stupid, pointless DALNet-style +/-r mode when a channel
is registered or dropped. NOT RECOMMENDED TO USE.
cs_regnotice.c - Sends a user a notice with some information specified in a
regnotice {} block inside the chanserv {} block of your
atheme.conf when the user registers a channel.
cs_updown.c - Either gives or removes all your channel status modes at once.
cs_userinfo.c - Display a message when a user joins a channel. You must be able
to edit the channel access list to add or remove a userinfo entry.
gen_echoserver.c - NOT RECOMMENDED TO USE.
gen_httpd.c - A small sample httpd for serving files. It is highly recommended
to use misc/httpd.c instead.
gen_listenerdemo.c - NOT RECOMMENDED TO USE.
gen_vhostonreg.c - Assigns a $account.hidehostsuffix vhost to all users upon
account registration. $account will be replaced by the users'
accountname and hidehostsuffix is that config option from the
serverinfo {} block of your atheme.conf.
graphtastical.c - Graphs user->channel relationships. Not recommended to use if
there are privacy concerns.
gs_roulette.c - A nice GameServ game of Russian Roulette.
ircd_catserv.c - Little module showing a CatServ Services client.
ircd_announceserv.c - A services bot which allows users to request network
announcements that will then (when approved by a soper)
be sent to all users on the network. This is seperate
from InfoServ so that users can easily ignore users'
announcements but won't miss any important announcements
from network staff.
ircd_loveserv.c - A services bot for sending love-related items to other users.
NOT COMPILED BY DEFAULT.
ircd_crypto_trans.c - A encryption module for IRCServices weird password
encryption scheme. NOT COMPILED BY DEFAULT.
mlocktweaker.c - Sets the mlock to all new channels to something specified in
the source code of the module. See line 16 of the module's
code for what to edit. NOT COMPILED BY DEFAULT.
ns_ajoin.c - Allows users to set a AJOIN/autojoin list of channels that Atheme
will automatically join them to upon identify. Only works on
ShadowIRCd, InspIRCd and UnrealIRCd. NOT COMPILED BY DEFAULT.
ns_cleannick.c - Detects and cleans 'lame' nicknames using case normalization.
ns_fenforce.c - Allows opers to force the ENFORCE flag on/off on other users'
accounts.
ns_forbid.c - Allows opers to forbid the registration and use of a nickname.
ns_fregister.c - Allows opers to register an account on behalf of another user.
A oper must have the user:fregister priv to use this command.
ns_generatehash.c - Generates a password hash from the password given as part
of the command. Extremely useful if your passwords are
encrypted and you want to set SOPER passwords.
ns_generatepass.c - Generates a random password.
ns_guestnoreg.c - Disallows the registration of nicks beginning with a string
specified in the guestnicks {} block inside the nickserv {}
block of your atheme.conf.
ns_listlogins.c - Allows users to list the other clients currently logged in
to the same account as them.
ns_mxcheck.c - Checks if a email address provided by a user upon registration
is valid and fails registration if it is not.
ns_mxcheck_async.c - Same as ns_mxcheck.c, but asynchronous.
ns_regnotice.c - Sends a user a notice with some information specified in a
regnotice {} block inside the nickserv {} block of your
atheme.conf when the user registers an account.
ns_waitreg.c - Requires a user to have been connected for waitreg_time (in
the nickserv {} block of your atheme.conf) seconds before
they are allowed to register their nick.
on_db_save.c - Allows you to specify a command that is run every time the
Atheme database is saved.
os_akillnicklist.c - AKILLs users matched in a nicklist specified in your
atheme.conf. See the comment at the top of the module's
source code for details.
os_defcon.c - Allows you to use DEFCON-based security on your network.
*HIGHLY* NOT RECOMMENDED AND NOT COMPILED BY DEFAULT.
os_helpme.c - Sets usermode +h on all users listed with the general:helper
soper priv upon recieving soper status. Requires an IRCd that has
+h (helper) usermode support. NOT COMPILED BY DEFAULT.
os_joinmon.c - Allows adding nick patterns to a joinmon list and when a user
matching one of the patterns joins a channel, a message will
be sent to the logchan (with the info loglevel).
os_kill.c - Allows opers to KILL users via services.
os_klinechan.c - KLINEs all users who join a KLINECHAN.
os_pingspam.c - Spam a user with pings and various messages either on-demand or
spams all users a bit upon connect.
os_procwatch.c - Watch a specified process and log a message when it finishes
running. Requires kqueue (FreeBSD). NOT COMPILED BY DEFAULT.
os_savechanmodes.c - Allows you to dump and restore channelmodes of all channels
on the network.
os_tabletest.c - NOT RECOMMENDED TO USE.
os_testcmd.c - Run a test command.
os_testproc.c - Runs a test of child processes.
os_trace.c - Looks up users by certain criteria and allows you to perform
various actions on them.
wumpus.c - Allows users to play a game of Hunt the Wumpus!

42
backtrace.c Normal file
View File

@ -0,0 +1,42 @@
#include <execinfo.h>
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/backtrace", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void __segv_hdl(int whocares)
{
void *array[256];
char **strings;
size_t sz, i;
sz = backtrace(array, 256);
strings = backtrace_symbols(array, sz);
slog(LG_INFO, "---------------- [ CRASH ] -----------------");
slog(LG_INFO, "%zu stack frames, flags %s", sz, get_conf_opts());
for (i = 0; i < sz; i++)
slog(LG_INFO, "#%zu --> %p (%s)", i, array[i], strings[i]);
slog(LG_INFO, "Report to http://jira.atheme.org/");
slog(LG_INFO, "--------------------------------------------");
}
void _modinit(module_t *m)
{
signal(SIGSEGV, __segv_hdl);
}
void _moddeinit(module_unload_intent_t intent)
{
signal(SIGSEGV, NULL);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

BIN
backtrace.so Executable file

Binary file not shown.

215
cs_access_alias.c Normal file
View File

@ -0,0 +1,215 @@
/*
* Copyright (c) 2007 Jilles Tjoelker, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* ChanServ ACCESS command
*
* $Id$
*/
#include "atheme.h"
#include "template.h"
DECLARE_MODULE_V1
(
"contrib/cs_access_alias", FALSE, _modinit, _moddeinit,
"$Id$",
"freenode <http://www.freenode.net>"
);
static void cs_cmd_access(sourceinfo_t *si, int parc, char *parv[]);
command_t cs_access = { "ACCESS", "Manipulates channel access lists.",
AC_NONE, 4, cs_cmd_access, { .path = "contrib/access" } };
void _modinit(module_t *m)
{
service_named_bind_command("chanserv", &cs_access);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("chanserv", &cs_access);
}
static void compat_cmd(sourceinfo_t *si, const char *cmdname, char *channel, char *arg1, char *arg2, char *arg3)
{
int newparc;
char *newparv[5];
command_t *cmd;
newparv[0] = channel;
newparv[1] = arg1;
newparv[2] = arg2;
newparv[3] = arg3;
newparv[4] = NULL;
/* this assumes arg3!=NULL implies arg2!=NULL implies arg1!=NULL */
newparc = 1 + (arg1 != NULL) + (arg2 != NULL) + (arg3 != NULL);
cmd = command_find(si->service->commands, cmdname);
if (cmd != NULL)
command_exec(si->service, si, cmd, newparc, newparv);
else
command_fail(si, fault_unimplemented, _("Command \2%s\2 not loaded?"), cmdname);
}
typedef struct {
const char *res;
unsigned int level;
} template_iter_t;
static int global_template_search(const char *key, void *data, void *privdata)
{
template_iter_t *iter = privdata;
default_template_t *def_t = data;
if (def_t->flags == iter->level)
iter->res = key;
return 0;
}
static const char *get_template_name(mychan_t *mc, unsigned int level)
{
metadata_t *md;
const char *p, *q, *r;
char *s;
char ss[40];
static char flagname[400];
template_iter_t iter;
md = metadata_find(mc, "private:templates");
if (md != NULL)
{
p = md->value;
while (p != NULL)
{
while (*p == ' ')
p++;
q = strchr(p, '=');
if (q == NULL)
break;
r = strchr(q, ' ');
if (r != NULL && r < q)
break;
mowgli_strlcpy(ss, q, sizeof ss);
if (r != NULL && r - q < (int)(sizeof ss - 1))
{
ss[r - q] = '\0';
}
if (level == flags_to_bitmask(ss, 0))
{
mowgli_strlcpy(flagname, p, sizeof flagname);
s = strchr(flagname, '=');
if (s != NULL)
*s = '\0';
return flagname;
}
p = r;
}
}
iter.res = NULL;
iter.level = level;
mowgli_patricia_foreach(global_template_dict, global_template_search, &iter);
return iter.res;
}
static void access_list(sourceinfo_t *si, mychan_t *mc, int parc, char *parv[])
{
mowgli_node_t *n;
chanacs_t *ca;
const char *str1, *str2;
int i = 1;
bool operoverride = false;
/* Copied from modules/chanserv/flags.c */
/* Note: This overrides the normal need of +A access unless private */
if (use_channel_private && mc->flags & MC_PRIVATE &&
!chanacs_source_has_flag(mc, si, CA_ACLVIEW))
{
if (has_priv(si, PRIV_CHAN_AUSPEX))
operoverride = true;
else
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
return;
}
}
command_success_nodata(si, _("Entry Nickname/Host Flags"));
command_success_nodata(si, "----- ---------------------- -----");
MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
{
ca = n->data;
/* Change: don't show akicks */
if (ca->level == CA_AKICK)
continue;
str1 = get_template_name(mc, ca->level);
str2 = ca->tmodified ? time_ago(ca->tmodified) : "?";
if (str1 != NULL)
command_success_nodata(si, _("%-5d %-22s %s (%s) [modified %s ago]"), i, ca->entity ? ca->entity->name : ca->host, bitmask_to_flags(ca->level), str1,
str2);
else
command_success_nodata(si, _("%-5d %-22s %s [modified %s ago]"), i, ca->entity ? ca->entity->name : ca->host, bitmask_to_flags(ca->level),
str2);
i++;
}
command_success_nodata(si, "----- ---------------------- -----");
command_success_nodata(si, _("End of \2%s\2 FLAGS listing."), mc->name);
if (operoverride)
logcommand(si, CMDLOG_ADMIN, "%s ACCESS LIST (oper override)", mc->name);
else
logcommand(si, CMDLOG_GET, "%s ACCESS LIST", mc->name);
}
static void cs_cmd_access(sourceinfo_t *si, int parc, char *parv[])
{
char *chan, *cmd;
mychan_t *mc;
char killit[] = "-*";
char deftemplate[] = "OP";
char defaccess[] = "=votirA";
if (parc < 2)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS");
command_fail(si, fault_needmoreparams, _("Syntax: ACCESS <#channel> ADD|DEL|LIST [nick] [level]"));
return;
}
if (parv[0][0] == '#')
chan = parv[0], cmd = parv[1];
else if (parv[1][0] == '#')
cmd = parv[0], chan = parv[1];
else
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "ACCESS");
command_fail(si, fault_badparams, _("Syntax: ACCESS <#channel> ADD|DEL|LIST [nick] [level]"));
return;
}
mc = mychan_find(chan);
if (mc == NULL)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), chan);
return;
}
if (!strcasecmp(cmd, "LIST"))
access_list(si, mc, parc - 2, parv + 2);
else if (parc < 3)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS");
command_fail(si, fault_needmoreparams, _("Syntax: ACCESS <#channel> ADD|DEL <nick> [level]"));
return;
}
else if (!strcasecmp(cmd, "ADD"))
compat_cmd(si, "FLAGS", chan, parv[2], parc > 3 ? parv[3] : (get_template_flags(mc, deftemplate) ? deftemplate : defaccess), NULL);
else if (!strcasecmp(cmd, "DEL"))
compat_cmd(si, "FLAGS", chan, parv[2], killit, NULL);
else
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);
}

100
cs_babbler.c Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2008 William Pitcock
* Rights to this code are as documented in doc/LICENSE.
*
* Because sometimes premadonna assholes with large ignore lists
* piss entire channels the hell off...
*
* So what does this do?
* =====================
*
* It repeats everything someone says and to be extra annoying, highlights
* the person who has public ignore notification spam.
*
* It was written for the purpose of mockery of someone on #atheme-project
* who makes claims like "I have the whole channel on ignore", etc.
*
* Pro tip: we don't care about your ignore list.
*
* How do I use it? I have an asshole on my channel too!
* =====================================================
*
* Load the module, set these options:
*
* - babbler:enable to actually enable babbler
* - babbler:nicks, the actual ignore list of the asshole
* - babbler:target, the nick of the person who needs to be pwnt
* - babbler:source, the nick of a psuedoclient to send the message
* from.
*
* Will you make it PM them instead?
* =================================
*
* Absolutely not. Then it could be used for spambots, etc. That's a really
* bad idea.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_babbler", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod -at- nenolod.net>"
);
static void
on_channel_message(void *p)
{
hook_cmessage_data_t *data = p;
if (data != NULL && data->msg != NULL)
{
mychan_t *mc = MYCHAN_FROM(data->c);
metadata_t *md;
if (!mc)
return;
if (!metadata_find(mc, "babbler:enable"))
return;
if (!(md = metadata_find(mc, "babbler:nicks")))
return;
if (strstr(md->value, data->u->nick))
{
char *source = NULL;
char *target;
if (!(md = metadata_find(mc, "babbler:target")))
return;
target = md->value;
if (!(md = metadata_find(mc, "babbler:source")))
source = chansvs.nick;
else
source = md->value;
msg(source, data->c->name, "%s: <%s> %s", target, data->u->nick, data->msg);
}
}
}
void _modinit(module_t *m)
{
hook_add_event("channel_message");
hook_add_channel_message(on_channel_message);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_message(on_channel_message);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

447
cs_badwords.c Normal file
View File

@ -0,0 +1,447 @@
/*
* Copyright (c) 2005 William Pitcock <nenolod -at- nenolod.net>
* Rights to this code are as documented in doc/LICENSE.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_badwords", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.net>"
);
static void on_channel_message(hook_cmessage_data_t *data);
static void cs_cmd_badwords(sourceinfo_t *si, int parc, char *parv[]);
static void cs_set_cmd_blockbadwords(sourceinfo_t *si, int parc, char *parv[]);
static void write_badword_db(database_handle_t *db);
static void db_h_bw(database_handle_t *db, const char *type);
command_t cs_badwords = { "BADWORDS", N_("Manage the list of channel bad words."), AC_AUTHENTICATED, 4, cs_cmd_badwords, { .path = "contrib/badwords" } };
command_t cs_set_blockbadwords = { "BLOCKBADWORDS", N_("Set whether users can say badwords in channel or not."), AC_NONE, 2, cs_set_cmd_blockbadwords, { .path = "contrib/set_blockbadwords" } };
struct badword_ {
char *badword;
time_t add_ts;
char *creator;
char *channel;
char *action;
mowgli_node_t node;
};
typedef struct badword_ badword_t;
mowgli_patricia_t **cs_set_cmdtree;
void _modinit(module_t *m)
{
MODULE_TRY_REQUEST_SYMBOL(m, cs_set_cmdtree, "chanserv/set_core", "cs_set_cmdtree");
if (!module_find_published("backend/opensex"))
{
slog(LG_INFO, "Module %s requires use of the OpenSEX database backend, refusing to load.", m->name);
m->mflags = MODTYPE_FAIL;
return;
}
hook_add_event("channel_message");
hook_add_channel_message(on_channel_message);
hook_add_db_write(write_badword_db);
db_register_type_handler("BW", db_h_bw);
service_named_bind_command("chanserv", &cs_badwords);
command_add(&cs_set_blockbadwords, *cs_set_cmdtree);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_message(on_channel_message);
hook_del_db_write(write_badword_db);
db_unregister_type_handler("BW");
service_named_unbind_command("chanserv", &cs_badwords);
command_delete(&cs_set_blockbadwords, *cs_set_cmdtree);
}
static inline mowgli_list_t *badwords_list_of(mychan_t *mc)
{
mowgli_list_t *l;
return_val_if_fail(mc != NULL, NULL);
l = privatedata_get(mc, "badword:list");
if (l != NULL)
return l;
l = mowgli_list_create();
privatedata_set(mc, "badword:list", l);
return l;
}
static void write_badword_db(database_handle_t *db)
{
mowgli_node_t *n;
mychan_t *mc;
mowgli_patricia_iteration_state_t state;
mowgli_list_t *l;
MOWGLI_PATRICIA_FOREACH(mc, &state, mclist)
{
l = badwords_list_of(mc);
if (l == NULL)
return;
MOWGLI_ITER_FOREACH(n, l->head)
{
badword_t *bw = n->data;
db_start_row(db, "BW");
db_write_word(db, bw->badword);
db_write_time(db, bw->add_ts);
db_write_word(db, bw->creator);
db_write_word(db, bw->channel);
db_write_word(db, bw->action);
db_commit_row(db);
}
}
}
static void db_h_bw(database_handle_t *db, const char *type)
{
mychan_t *mc;
mowgli_patricia_iteration_state_t state;
mowgli_list_t *l;
const char *badword = db_sread_word(db);
time_t add_ts = db_sread_time(db);
const char *creator = db_sread_word(db);
const char *channel = db_sread_word(db);
const char *action = db_sread_word(db);
MOWGLI_PATRICIA_FOREACH(mc, &state, mclist)
{
if (irccasecmp(mc->name, channel))
continue;
l = badwords_list_of(mc);
badword_t *bw = smalloc(sizeof(badword_t));
bw->badword = sstrdup(badword);
bw->add_ts = add_ts;
bw->creator = sstrdup(creator);
bw->channel = sstrdup(channel);
bw->action = sstrdup(action);
mowgli_node_add(bw, &bw->node, l);
}
}
static void on_channel_message(hook_cmessage_data_t *data)
{
badword_t *bw;
mowgli_node_t *n;
mowgli_list_t *l;
mychan_t *mc = MYCHAN_FROM(data->c);
if (mc == NULL)
return;
if (metadata_find(mc, "blockbadwords") == NULL)
return;
l = badwords_list_of(mc);
if (MOWGLI_LIST_LENGTH(l) == 0)
return;
char *kickstring = "Foul language is prohibited here.";
if (data != NULL && data->msg != NULL)
{
MOWGLI_ITER_FOREACH(n, l->head)
{
bw = n->data;
if (!match(bw->badword, data->msg))
{
if (!strcasecmp("KICKBAN", bw->action))
{
char hostbuf[BUFSIZE];
hostbuf[0] = '\0';
mowgli_strlcat(hostbuf, "*!*@", BUFSIZE);
mowgli_strlcat(hostbuf, data->u->vhost, BUFSIZE);
modestack_mode_param(chansvs.nick, data->c, MTYPE_ADD, 'b', hostbuf);
chanban_add(data->c, hostbuf, 'b');
kick(chansvs.me->me, data->c, data->u, kickstring);
return;
}
else if (!strcasecmp("KICK", bw->action))
{
kick(chansvs.me->me, data->c, data->u, kickstring);
return;
}
else if (!strcasecmp("QUIET", bw->action))
{
char hostbuf[BUFSIZE];
hostbuf[0] = '\0';
mowgli_strlcat(hostbuf, "*!*@", BUFSIZE);
mowgli_strlcat(hostbuf, data->u->vhost, BUFSIZE);
modestack_mode_param(chansvs.nick, data->c, MTYPE_ADD, 'q', hostbuf);
chanban_add(data->c, hostbuf, 'q');
return;
}
else if (!strcasecmp("BAN", bw->action))
{
char hostbuf[BUFSIZE];
hostbuf[0] = '\0';
mowgli_strlcat(hostbuf, "*!*@", BUFSIZE);
mowgli_strlcat(hostbuf, data->u->vhost, BUFSIZE);
modestack_mode_param(chansvs.nick, data->c, MTYPE_ADD, 'b', hostbuf);
chanban_add(data->c, hostbuf, 'b');
return;
}
}
}
}
}
/* SET BADWORD */
static void cs_cmd_badwords(sourceinfo_t *si, int parc, char *parv[])
{
char *channel = parv[0];
char *command = parv[1];
char *word = parv[2];
char *action = parv[3];
mychan_t *mc;
mowgli_node_t *n, *tn;
badword_t *bw;
mowgli_list_t *l;
if (!channel || !command)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SET BADWORDS");
command_fail(si, fault_needmoreparams, _("Syntax: BADWORDS <#channel> ADD|DEL|LIST [badword] [action]"));
return;
}
if (!(mc = mychan_find(channel)))
{
command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."), channel);
return;
}
l = badwords_list_of(mc);
if (!strcasecmp("ADD", command))
{
if (!word || !action)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "BADWORDS");
command_fail(si, fault_needmoreparams, _("Syntax: BADWORDS <#channel> ADD <badword> <action>"));
return;
}
if (!chanacs_source_has_flag(mc, si, CA_SET))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this command."));
return;
}
if(!strcasecmp("KICK", action) || !strcasecmp("KICKBAN", action) || !strcasecmp("BAN", action) || (!strcasecmp("QUIET", action) && ircd != NULL && strchr(ircd->ban_like_modes, 'q')))
{
if (l != NULL)
{
MOWGLI_ITER_FOREACH(n, l->head)
{
bw = n->data;
if (!irccasecmp(bw->badword, word))
{
command_success_nodata(si, _("\2%s\2 has already been entered into the bad word list."), word);
return;
}
}
}
bw = smalloc(sizeof(badword_t));
bw->add_ts = CURRTIME;;
bw->creator = sstrdup(get_source_name(si));
bw->channel = sstrdup(mc->name);
bw->badword = sstrdup(word);
bw->action = sstrdup(action);
mowgli_node_add(bw, &bw->node, l);
command_success_nodata(si, _("You have added \2%s\2 as a bad word."), word);
logcommand(si, CMDLOG_SET, "BADWORDS:ADD: \2%s\2 \2%s\2 \2%s\2", channel, word, action);
}
else
{
command_fail(si, fault_badparams, _("Invalid action given."));
return;
}
}
else if (!strcasecmp("DEL", command))
{
if (!word)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "BADWORDS");
command_fail(si, fault_needmoreparams, _("Syntax: BADWORDS <#channel> DEL <badword>"));
return;
}
if (!chanacs_source_has_flag(mc, si, CA_SET))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this command."));
return;
}
if (l == NULL)
{
command_fail(si, fault_nosuch_target, _("There are no badwords set in this channel."));
return;
}
MOWGLI_ITER_FOREACH_SAFE(n, tn, l->head)
{
bw = n->data;
if (!irccasecmp(bw->badword, word))
{
logcommand(si, CMDLOG_SET, "BADWORDS:DEL: \2%s\2 \2%s\2", mc->name, bw->badword);
command_success_nodata(si, _("Bad word \2%s\2 has been deleted."), bw->badword);
mowgli_node_delete(&bw->node, l);
free(bw->creator);
free(bw->channel);
free(bw->badword);
free(bw->action);
free(bw);
return;
}
}
command_success_nodata(si, _("Word \2%s\2 not found in bad word database."), word);
}
else if (!strcasecmp("LIST", command))
{
char buf[BUFSIZE];
struct tm tm;
if (!chanacs_source_has_flag(mc, si, CA_ACLVIEW))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this command."));
return;
}
if (l == NULL)
{
command_fail(si, fault_nosuch_target, _("There are no badwords set in this channel."));
return;
}
MOWGLI_ITER_FOREACH(n, l->head)
{
bw = n->data;
tm = *localtime(&bw->add_ts);
strftime(buf, BUFSIZE, TIME_FORMAT, &tm);
command_success_nodata(si, "Word: \2%s\2 Action: \2%s\2 (%s - %s)",
bw->badword, bw->action, bw->creator, buf);
}
command_success_nodata(si, "End of list.");
logcommand(si, CMDLOG_GET, "BADWORDS:LIST");
}
else
{
command_fail(si, fault_needmoreparams, STR_INVALID_PARAMS, "BADWORDS");
command_fail(si, fault_needmoreparams, _("Syntax: BADWORDS <#channel> ADD|DEL|LIST [badword] [action]"));
return;
}
}
static void cs_set_cmd_blockbadwords(sourceinfo_t *si, int parc, char *parv[])
{
mychan_t *mc;
if (!(mc = mychan_find(parv[0])))
{
command_fail(si, fault_nosuch_target, _("Channel \2%s\2 is not registered."), parv[0]);
return;
}
if (!parv[1])
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SET BLOCKBADWORDS");
return;
}
if (!chanacs_source_has_flag(mc, si, CA_SET))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this command."));
return;
}
if (!strcasecmp("ON", parv[1]))
{
metadata_t *md = metadata_find(mc, "blockbadwords");
if (md)
{
command_fail(si, fault_nochange, _("The \2%s\2 flag is already set for channel \2%s\2."), "BLOCKBADWORDS", mc->name);
return;
}
metadata_add(mc, "blockbadwords", "on");
logcommand(si, CMDLOG_SET, "SET:BLOCKBADWORDS:ON: \2%s\2", mc->name);
command_success_nodata(si, _("The \2%s\2 flag has been set for channel \2%s\2."), "BLOCKBADWORDS", mc->name);
return;
}
else if (!strcasecmp("OFF", parv[1]))
{
metadata_t *md = metadata_find(mc, "blockbadwords");
if (!md)
{
command_fail(si, fault_nochange, _("The \2%s\2 flag is not set for channel \2%s\2."), "BLOCKBADWORDS", mc->name);
return;
}
metadata_delete(mc, "blockbadwords");
logcommand(si, CMDLOG_SET, "SET:BLOCKBADWORDS:OFF: \2%s\2", mc->name);
command_success_nodata(si, _("The \2%s\2 flag has been removed for channel \2%s\2."), "BLOCKBADWORDS", mc->name);
return;
}
else
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "BLOCKBADWORDS");
return;
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

158
cs_fregister.c Normal file
View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2005 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the CService FREGISTER function.
*
*/
#include "atheme.h"
#include "../chanserv/chanserv.h"
DECLARE_MODULE_V1
(
"contrib/cs_fregister", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void cs_cmd_fregister(sourceinfo_t *si, int parc, char *parv[]);
command_t cs_fregister = { "FREGISTER", N_("Forcibly registers a channel."),
PRIV_CHAN_ADMIN, 3, cs_cmd_fregister, { .path = "contrib/cs_fregister" } };
void _modinit(module_t *m)
{
service_named_bind_command("chanserv", &cs_fregister);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("chanserv", &cs_fregister);
}
static void cs_cmd_fregister(sourceinfo_t *si, int parc, char *parv[])
{
channel_t *c;
chanuser_t *cu;
mychan_t *mc;
char *name = parv[0];
char str[21];
hook_channel_register_check_t hdatac;
hook_channel_req_t hdata;
unsigned int fl;
/* This command is not useful on registered channels, ignore it if
* it is a fantasy command so users can program bots to react on
* it without interference from ChanServ.
*/
if (si->c != NULL)
return;
if (!name)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FREGISTER");
command_fail(si, fault_needmoreparams, _("To forcibly register a channel: FREGISTER <#channel>"));
return;
}
if (*name != '#')
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "FREGISTER");
command_fail(si, fault_badparams, _("Syntax: FREGISTER <#channel>"));
return;
}
/* make sure they're logged in */
if (!si->smu)
{
command_fail(si, fault_noprivs, _("You are not logged in."));
return;
}
if (si->smu->flags & MU_WAITAUTH)
{
command_fail(si, fault_notverified, _("You need to verify your email address before you may register channels."));
return;
}
/* make sure it isn't already registered */
if ((mc = mychan_find(name)))
{
if (!use_channel_private || !(mc->flags & MC_PRIVATE))
command_fail(si, fault_alreadyexists, _("\2%s\2 is already registered to \2%s\2."), mc->name, mychan_founder_names(mc));
else
command_fail(si, fault_alreadyexists, _("\2%s\2 is already registered."), mc->name);
return;
}
/* make sure the channel exists */
if (!(c = channel_find(name)))
{
command_fail(si, fault_nosuch_target, _("The channel \2%s\2 must exist in order to register it."), name);
return;
}
hdatac.si = si;
hdatac.name = name;
hdatac.chan = c;
hdatac.approved = 0;
hook_call_channel_can_register(&hdatac);
if (hdatac.approved != 0)
return;
logcommand(si, CMDLOG_REGISTER | CMDLOG_ADMIN, "FREGISTER: \2%s\2", name);
mc = mychan_add(name);
mc->registered = CURRTIME;
mc->used = CURRTIME;
mc->mlock_on |= (CMODE_NOEXT | CMODE_TOPIC);
if (c->limit == 0)
mc->mlock_off |= CMODE_LIMIT;
if (c->key == NULL)
mc->mlock_off |= CMODE_KEY;
mc->flags |= config_options.defcflags;
chanacs_add(mc, entity(si->smu), custom_founder_check(), CURRTIME, entity(si->smu));
if (c->ts > 0)
{
snprintf(str, sizeof str, "%lu", (unsigned long)c->ts);
metadata_add(mc, "private:channelts", str);
}
if (chansvs.deftemplates != NULL && *chansvs.deftemplates != '\0')
metadata_add(mc, "private:templates",
chansvs.deftemplates);
command_success_nodata(si, _("\2%s\2 is now registered to \2%s\2."), mc->name, entity(si->smu)->name);
hdata.si = si;
hdata.mc = mc;
hook_call_channel_register(&hdata);
/* Allow the hook to override this. */
fl = chanacs_source_flags(mc, si);
cu = chanuser_find(mc->chan, si->su);
if (cu == NULL)
;
else if (ircd->uses_owner && fl & CA_USEOWNER && fl & CA_AUTOOP &&
!(cu->modes & CSTATUS_OWNER))
{
modestack_mode_param(si->service->nick, mc->chan, MTYPE_ADD,
ircd->owner_mchar[1], CLIENT_NAME(si->su));
cu->modes |= CSTATUS_OWNER;
}
else if (ircd->uses_protect && fl & CA_USEPROTECT && fl & CA_AUTOOP &&
!(cu->modes & CSTATUS_PROTECT))
{
modestack_mode_param(si->service->nick, mc->chan, MTYPE_ADD,
ircd->protect_mchar[1], CLIENT_NAME(si->su));
cu->modes |= CSTATUS_PROTECT;
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

50
cs_kickdots.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2006 William Pitcock
* Rights to this code are as documented in doc/LICENSE.
*
* Kicks people saying "..." on channels with "kickdots" metadata set.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_kickdots", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod -at- nenolod.net>"
);
static void
on_channel_message(hook_cmessage_data_t *data)
{
if (data != NULL && data->msg != NULL && !strncmp(data->msg, "...", 3))
{
mychan_t *mc = MYCHAN_FROM(data->c);
if (mc == NULL)
return;
if (metadata_find(mc, "kickdots"))
{
kick(chansvs.me->me, data->c, data->u, data->msg);
}
}
}
void _modinit(module_t *m)
{
hook_add_event("channel_message");
hook_add_channel_message(on_channel_message);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_message(on_channel_message);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

35
cs_ping.c Normal file
View File

@ -0,0 +1,35 @@
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_ping", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void cs_cmd_ping(sourceinfo_t *si, int parc, char *parv[]);
command_t cs_ping = { "PING", "Verifies network connectivity by responding with pong.",
AC_NONE, 0, cs_cmd_ping, { .path = "contrib/cs_ping" } };
void _modinit(module_t *m)
{
service_named_bind_command("chanserv", &cs_ping);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("chanserv", &cs_ping);
}
static void cs_cmd_ping(sourceinfo_t *si, int parc, char *parv[])
{
command_success_nodata(si, "Pong!");
return;
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

50
cs_regmode.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2011 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Set/unset DALnet channel mode +r on registration/deregistration.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_regmode", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void register_hook(hook_channel_req_t *hdata)
{
mychan_t *mc = hdata->mc;
if (mc == NULL || mc->chan == NULL)
return;
modestack_mode_simple(chansvs.nick, mc->chan, MTYPE_ADD, CMODE_CHANREG);
}
static void drop_hook(mychan_t *mc)
{
if (mc == NULL || mc->chan == NULL)
return;
modestack_mode_simple(chansvs.nick, mc->chan, MTYPE_DEL, CMODE_CHANREG);
}
void
_modinit(module_t *m)
{
hook_add_event("channel_register");
hook_add_channel_register(register_hook);
hook_add_event("channel_drop");
hook_add_channel_drop(drop_hook);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_channel_register(register_hook);
hook_del_channel_drop(drop_hook);
}

83
cs_regnotice.c Normal file
View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2010 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Sends a customized welcome message on channel registration.
*/
#include "atheme.h"
#include "conf.h"
DECLARE_MODULE_V1
(
"contrib/cs_regnotice", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static mowgli_list_t regnotices = { NULL, NULL, 0 };
static void regnotice_hook(hook_channel_req_t *hdata)
{
mowgli_node_t *n;
sourceinfo_t *si = hdata->si;
mychan_t *mc = hdata->mc;
if (si == NULL || mc == NULL)
return;
MOWGLI_ITER_FOREACH(n, regnotices.head)
{
char *line = n->data;
command_success_nodata(si, "%s", line);
}
}
static int regnotice_config_handler(mowgli_config_file_entry_t *ce)
{
mowgli_config_file_entry_t *cce;
MOWGLI_ITER_FOREACH(cce, ce->entries)
{
char *line = sstrdup(cce->varname);
mowgli_node_add(line, mowgli_node_create(), &regnotices);
}
return 0;
}
static void regnotice_config_purge(void *unused)
{
mowgli_node_t *n, *tn;
MOWGLI_ITER_FOREACH_SAFE(n, tn, regnotices.head)
{
char *line = n->data;
free(line);
mowgli_node_delete(n, &regnotices);
mowgli_node_free(n);
}
}
void
_modinit(module_t *m)
{
hook_add_event("config_purge");
hook_add_config_purge(regnotice_config_purge);
hook_add_event("channel_register");
hook_add_channel_register(regnotice_hook);
add_conf_item("REGNOTICE", &chansvs.me->conf_table, regnotice_config_handler);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_channel_register(regnotice_hook);
hook_del_config_purge(regnotice_config_purge);
del_conf_item("REGNOTICE", &chansvs.me->conf_table);
}

232
cs_updown.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Copyright (c) 2008 Atheme Development Group
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the CService UP/DOWN functions.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_updown", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void cs_cmd_up(sourceinfo_t *si, int parc, char *parv[]);
static void cs_cmd_down(sourceinfo_t *si, int parc, char *parv[]);
command_t cs_up = { "UP", "Grants all access you have permission to on a given channel.", AC_NONE, 1, cs_cmd_up, { .path = "contrib/up" } };
command_t cs_down = { "DOWN", "Removes all current access you posess on a given channel.", AC_NONE, 1, cs_cmd_down, { .path = "contrib/down" } };
void _modinit(module_t *m)
{
service_named_bind_command("chanserv", &cs_up);
service_named_bind_command("chanserv", &cs_down);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("chanserv", &cs_up);
service_named_unbind_command("chanserv", &cs_down);
}
static void cs_cmd_up(sourceinfo_t *si, int parc, char *parv[])
{
chanuser_t *cu;
mychan_t *mc;
char *name = parv[0];
int fl;
if (!name)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "UP");
command_fail(si, fault_needmoreparams, "Syntax: UP <#channel>");
return;
}
if (!(mc = mychan_find(name)))
{
command_fail(si, fault_nosuch_target, "\2%s\2 is not registered.", name);
return;
}
if (metadata_find(mc, "private:close:closer"))
{
command_fail(si, fault_noprivs, "\2%s\2 is closed.", name);
return;
}
if (!mc->chan)
{
command_fail(si, fault_nosuch_target, "\2%s\2 does not exist.", name);
return;
}
if (!si->su)
return; // needs to be done over IRC
cu = chanuser_find(mc->chan, si->su);
if (!cu)
{
command_fail(si, fault_nosuch_target, "You are not on \2%s\2.", mc->name);
return;
}
fl = chanacs_user_flags(mc, cu->user);
// Don't check NOOP, because they are explicitly requesting status
if (ircd->uses_owner)
{
if (fl & CA_USEOWNER)
{
if (fl & CA_AUTOOP && !(ircd->owner_mode & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_ADD, ircd->owner_mchar[1], CLIENT_NAME(cu->user));
cu->modes |= ircd->owner_mode;
}
}
}
if (ircd->uses_protect)
{
if (fl & CA_USEPROTECT)
{
if (fl & CA_AUTOOP && !(ircd->protect_mode & cu->modes) && !(ircd->uses_owner && cu->modes & ircd->owner_mode))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_ADD, ircd->protect_mchar[1], CLIENT_NAME(cu->user));
cu->modes |= ircd->protect_mode;
}
}
}
if (fl & (CA_AUTOOP | CA_OP))
{
if (fl & CA_AUTOOP && !(CSTATUS_OP & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_ADD, 'o', CLIENT_NAME(cu->user));
cu->modes |= CSTATUS_OP;
}
}
if (ircd->uses_halfops)
{
if (fl & (CA_AUTOHALFOP | CA_HALFOP))
{
if (fl & CA_AUTOHALFOP && !(ircd->halfops_mode & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_ADD, ircd->halfops_mchar[1], CLIENT_NAME(cu->user));
cu->modes |= ircd->halfops_mode;
}
}
}
if (fl & (CA_AUTOVOICE | CA_VOICE))
{
if (fl & CA_AUTOVOICE && !(CSTATUS_VOICE & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_ADD, 'v', CLIENT_NAME(cu->user));
cu->modes |= CSTATUS_VOICE;
}
}
command_success_nodata(si, "Upped successfully on \2%s\2.", mc->name);
}
static void cs_cmd_down(sourceinfo_t *si, int parc, char *parv[])
{
chanuser_t *cu;
mychan_t *mc;
char *name = parv[0];
if (!name)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DOWN");
command_fail(si, fault_needmoreparams, "Syntax: DOWN <#channel>");
return;
}
if (!(mc = mychan_find(name)))
{
command_fail(si, fault_nosuch_target, "\2%s\2 is not registered.", name);
return;
}
if (metadata_find(mc, "private:close:closer"))
{
command_fail(si, fault_noprivs, "\2%s\2 is closed.", name);
return;
}
if (!mc->chan)
{
command_fail(si, fault_nosuch_target, "\2%s\2 does not exist.", name);
return;
}
if (!si->su)
return; // needs to be done over IRC
cu = chanuser_find(mc->chan, si->su);
if (!cu)
{
command_fail(si, fault_nosuch_target, "You are not on \2%s\2.", mc->name);
return;
}
chanacs_user_flags(mc, cu->user);
// Don't check NOOP, because they are explicitly requesting status
if (ircd->uses_owner)
{
if (ircd->owner_mode & cu->modes)
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_DEL, ircd->owner_mchar[1], CLIENT_NAME(cu->user));
cu->modes &= ~ircd->owner_mode;
}
}
if (ircd->uses_protect)
{
if (ircd->protect_mode & cu->modes)
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_DEL, ircd->protect_mchar[1], CLIENT_NAME(cu->user));
cu->modes &= ~ircd->protect_mode;
}
}
if ((CSTATUS_OP & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_DEL, 'o', CLIENT_NAME(cu->user));
cu->modes &= ~CSTATUS_OP;
}
if (ircd->uses_halfops)
{
if (ircd->halfops_mode & cu->modes)
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_DEL, ircd->halfops_mchar[1], CLIENT_NAME(cu->user));
cu->modes &= ~ircd->halfops_mode;
}
}
if ((CSTATUS_VOICE & cu->modes))
{
modestack_mode_param(chansvs.nick, mc->chan, MTYPE_DEL, 'v', CLIENT_NAME(cu->user));
cu->modes &= ~CSTATUS_VOICE;
}
command_success_nodata(si, "Downed successfully on \2%s\2.", mc->name);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

157
cs_userinfo.c Normal file
View File

@ -0,0 +1,157 @@
/*
* Copyright (c) 2007 Jilles Tjoelker, et al
* Rights to this code are as documented in doc/LICENSE.
*
* Per-channel userinfo thingie
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/cs_userinfo", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void userinfo_check_join(hook_channel_joinpart_t *hdata);
static void cs_cmd_userinfo(sourceinfo_t *si, int parc, char *parv[]);
command_t cs_userinfo = { "USERINFO", N_("Sets a userinfo message."),
AC_NONE, 3, cs_cmd_userinfo, { .path = "contrib/userinfo" } };
void _modinit(module_t *m)
{
hook_add_event("channel_join");
hook_add_channel_join(userinfo_check_join);
service_named_bind_command("chanserv", &cs_userinfo);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_join(userinfo_check_join);
service_named_unbind_command("chanserv", &cs_userinfo);
}
/* USERINFO <channel> [user] [message] */
static void cs_cmd_userinfo(sourceinfo_t *si, int parc, char *parv[])
{
mowgli_node_t *n;
myuser_t *mu;
mychan_t *mc;
chanacs_t *ca;
metadata_t *md;
unsigned int restrictflags;
if (parc < 1)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "USERINFO");
command_fail(si, fault_needmoreparams, _("Syntax: USERINFO <channel> [target] [info]"));
return;
}
mc = mychan_find(parv[0]);
if (!mc)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), parv[0]);
return;
}
if (metadata_find(mc, "private:close:closer") && !has_priv(si, PRIV_CHAN_AUSPEX))
{
command_fail(si, fault_noprivs, _("\2%s\2 is closed."), mc->name);
return;
}
restrictflags = chanacs_source_flags(mc, si);
if (parc == 1)
{
if (!(restrictflags & CA_ACLVIEW))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
return;
}
command_success_nodata(si, _("Nickname Info"));
command_success_nodata(si, "------------------- ---------------");
MOWGLI_ITER_FOREACH(n, mc->chanacs.head)
{
ca = n->data;
if (ca->entity == NULL)
continue;
md = metadata_find(ca, "userinfo");
if (md == NULL)
continue;
command_success_nodata(si, "%-19s %s", ca->entity->name, md->value);
}
command_success_nodata(si, "------------------- ---------------");
command_success_nodata(si, _("End of \2%s\2 USERINFO listing."), mc->name);
logcommand(si, CMDLOG_GET, "USERINFO:LIST: \2%s\2", mc->name);
}
else
{
if (!(restrictflags & CA_FLAGS))
{
command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
return;
}
mu = myuser_find_ext(parv[1]);
if (mu == NULL)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), parv[1]);
return;
}
ca = chanacs_find_literal(mc, entity(mu), 0);
if (ca == NULL || ca->level & CA_AKICK)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 has no access to \2%s\2."), entity(mu)->name, mc->name);
return;
}
if (ca->level & ~allow_flags(mc, restrictflags))
{
command_fail(si, fault_noprivs, _("You are not authorized to modify the access entry for \2%s\2 on \2%s\2."), entity(mu)->name, mc->name);
return;
}
if (parc == 2)
{
metadata_delete(ca, "userinfo");
command_success_nodata(si, _("Deleted userinfo for \2%s\2 on \2%s\2."),
entity(mu)->name, mc->name);
logcommand(si, CMDLOG_SET, "USERINFO:DEL: \2%s\2 on \2%s\2", entity(mu)->name, mc->name);
return;
}
metadata_add(ca, "userinfo", parv[2]);
command_success_nodata(si, _("Added userinfo for \2%s\2 on \2%s\2."),
entity(mu)->name, mc->name);
logcommand(si, CMDLOG_SET, "USERINFO:ADD: \2%s\2 on \2%s\2 (\2%s\2)", entity(mu)->name, mc->name, parv[2]);
}
return;
}
static void userinfo_check_join(hook_channel_joinpart_t *hdata)
{
chanuser_t *cu = hdata->cu;
myuser_t *mu;
mychan_t *mc;
chanacs_t *ca;
metadata_t *md;
if (cu == NULL)
return;
if (!(cu->user->server->flags & SF_EOB))
return;
mu = cu->user->myuser;
mc = MYCHAN_FROM(cu->chan);
if (mu == NULL || mc == NULL)
return;
ca = chanacs_find_literal(mc, entity(mu), 0);
if (ca == NULL || ca->level & CA_AKICK)
return;
if (!(md = metadata_find(ca, "userinfo")))
return;
msg(chansvs.nick, cu->chan->name, "[%s] %s", cu->user->nick, md->value);
}

615
dnsbl.c Normal file
View File

@ -0,0 +1,615 @@
/*
* charybdis: A slightly useful ircd.
* blacklist.c: Manages DNS blacklist entries and lookups
*
* Copyright (C) 2006-2008 charybdis development team
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/* To configure/use, add a block to the general{} section of your atheme.conf
* like this:
*
* blacklists {
* "dnsbl.dronebl.org";
* "rbl.efnetrbl.org";
* };
*/
#include "atheme.h"
#include "conf.h"
DECLARE_MODULE_V1
(
"contrib/dnsbl", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
mowgli_list_t blacklist_list = { NULL, NULL, 0 };
mowgli_patricia_t **os_set_cmdtree;
static char *action = NULL;
/* A configured DNSBL */
struct Blacklist {
unsigned int status; /* If CONF_ILLEGAL, delete when no clients */
int refcount;
char host[IRCD_RES_HOSTLEN + 1];
unsigned int hits;
time_t lastwarning;
};
/* A lookup in progress for a particular DNSBL for a particular client */
struct BlacklistClient {
struct Blacklist *blacklist;
user_t *u;
dns_query_t dns_query;
mowgli_node_t node;
};
struct dnsbl_exempt_ {
char *ip;
time_t exempt_ts;
char *creator;
char *reason;
};
typedef struct dnsbl_exempt_ dnsbl_exempt_t;
mowgli_list_t dnsbl_elist;
static void os_cmd_set_dnsblaction(sourceinfo_t *si, int parc, char *parv[]);
static void dnsbl_hit(user_t *u, struct Blacklist *blptr);
static void os_cmd_dnsblexempt(sourceinfo_t *si, int parc, char *parv[]);
static void os_cmd_dnsblscan(sourceinfo_t *si, int parc, char *parv[]);
static void write_dnsbl_exempt_db(database_handle_t *db);
static void db_h_ble(database_handle_t *db, const char *type);
static void lookup_blacklists(user_t *u);
command_t os_set_dnsblaction = { "DNSBLACTION", N_("Changes what happens to a user when they hit a DNSBL."), PRIV_USER_ADMIN, 1, os_cmd_set_dnsblaction, { .path = "contrib/set_dnsblaction" } };
command_t os_dnsblexempt = { "DNSBLEXEMPT", N_("Manage the list of IP's exempt from DNSBL checking."), PRIV_USER_ADMIN, 3, os_cmd_dnsblexempt, { .path = "contrib/dnsblexempt" } };
command_t os_dnsblscan = { "DNSBLSCAN", N_("Manually scan if a user is in a DNSBL."), PRIV_USER_ADMIN, 1, os_cmd_dnsblscan, { .path = "contrib/dnsblscan" } };
static inline mowgli_list_t *dnsbl_queries(user_t *u)
{
mowgli_list_t *l;
return_val_if_fail(u != NULL, NULL);
l = privatedata_get(u, "dnsbl:queries");
if (l != NULL)
return l;
l = mowgli_list_create();
privatedata_set(u, "dnsbl:queries", l);
return l;
}
static void os_cmd_set_dnsblaction(sourceinfo_t *si, int parc, char *parv[])
{
char *act = parv[0];
if (!act)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLACTION");
command_fail(si, fault_needmoreparams, _("Syntax: SET DNSBLACTION <action>"));
return;
}
if (!strcasecmp("SNOOP", act) || !strcasecmp("KLINE", act) || !strcasecmp("NOTIFY", act))
{
action = sstrdup(act);
command_success_nodata(si, _("DNSBLACTION successfully set to \2%s\2"), act);
logcommand(si, CMDLOG_ADMIN, "SET:DNSBLACTION: \2%s\2", act);
return;
}
else if (!strcasecmp("NONE", act))
{
action = NULL;
command_success_nodata(si, _("DNSBLACTION successfully set to \2%s\2"), act);
logcommand(si, CMDLOG_ADMIN, "SET:DNSBLACTION: \2%s\2", act);
return;
}
else
{
command_fail(si, fault_badparams, _("Invalid action given."));
return;
}
}
static void os_cmd_dnsblexempt(sourceinfo_t *si, int parc, char *parv[])
{
char *command = parv[0];
char *ip = parv[1];
char *reason = parv[2];
mowgli_node_t *n, *tn;
dnsbl_exempt_t *de;
if (!command)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason]"));
return;
}
if (!strcasecmp("ADD", command))
{
if (!ip || !reason)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD <ip> <reason>"));
return;
}
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
{
de = n->data;
if (!irccasecmp(de->ip, ip))
{
command_success_nodata(si, _("\2%s\2 has already been entered into the DNSBL exempts list."), ip);
return;
}
}
de = smalloc(sizeof(dnsbl_exempt_t));
de->exempt_ts = CURRTIME;;
de->creator = sstrdup(get_source_name(si));
de->reason = sstrdup(reason);
de->ip = sstrdup(ip);
mowgli_node_add(de, mowgli_node_create(), &dnsbl_elist);
command_success_nodata(si, _("You have added \2%s\2 to the DNSBL exempts list."), ip);
logcommand(si, CMDLOG_ADMIN, "DNSBL:EXEMPT:ADD: \2%s\2 \2%s\2", ip, reason);
}
else if (!strcasecmp("DEL", command))
{
if (!ip)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLEXEMPT");
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT DEL <ip>"));
return;
}
MOWGLI_ITER_FOREACH_SAFE(n, tn, dnsbl_elist.head)
{
de = n->data;
if (!irccasecmp(de->ip, ip))
{
logcommand(si, CMDLOG_SET, "DNSBL:EXEMPT:DEL: \2%s\2", de->ip);
command_success_nodata(si, _("DNSBL Exempt IP \2%s\2 has been deleted."), de->ip);
mowgli_node_delete(n, &dnsbl_elist);
free(de->creator);
free(de->reason);
free(de->ip);
free(de);
return;
}
}
command_success_nodata(si, _("IP \2%s\2 not found in DNSBL Exempt database."), ip);
}
else if (!strcasecmp("LIST", command))
{
char buf[BUFSIZE];
struct tm tm;
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
{
de = n->data;
tm = *localtime(&de->exempt_ts);
strftime(buf, BUFSIZE, TIME_FORMAT, &tm);
command_success_nodata(si, "IP: \2%s\2 Reason: \2%s\2 (%s - %s)",
de->ip, de->reason, de->creator, buf);
}
command_success_nodata(si, "End of list.");
logcommand(si, CMDLOG_GET, "DNSBL:EXEMPT:LIST");
}
else
{
command_fail(si, fault_needmoreparams, STR_INVALID_PARAMS, "DNSBLEXEMPT");
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLEXEMPT ADD|DEL|LIST [ip] [reason]"));
return;
}
}
static void os_cmd_dnsblscan(sourceinfo_t *si, int parc, char *parv[])
{
char *user = parv[0];
user_t *u;
if (!user)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "DNSBLSCAN");
command_fail(si, fault_needmoreparams, _("Syntax: DNSBLSCAN <user>"));
return;
}
if ((u = user_find_named(user)))
{
lookup_blacklists(u);
logcommand(si, CMDLOG_ADMIN, "DNSBLSCAN: %s", user);
command_success_nodata(si, "%s has been scanned.", user);
return;
}
else
{
command_fail(si, fault_badparams, "User %s is not on the network, you can not scan them.", user);
return;
}
}
/* private interfaces */
static struct Blacklist *find_blacklist(char *name)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
{
struct Blacklist *blptr = (struct Blacklist *) n->data;
if (!strcasecmp(blptr->host, name))
return blptr;
}
return NULL;
}
static void blacklist_dns_callback(void *vptr, dns_reply_t *reply)
{
struct BlacklistClient *blcptr = (struct BlacklistClient *) vptr;
int listed = 0;
mowgli_list_t *l;
if (blcptr == NULL)
return;
if (blcptr->u == NULL)
{
free(blcptr);
return;
}
if (reply != NULL)
{
/* only accept 127.x.y.z as a listing */
if (reply->addr.saddr.sa.sa_family == AF_INET &&
!memcmp(&((struct sockaddr_in *)&reply->addr)->sin_addr, "\177", 1))
listed++;
else if (blcptr->blacklist->lastwarning + 3600 < CURRTIME)
{
slog(LG_DEBUG,
"Garbage reply from blacklist %s",
blcptr->blacklist->host);
blcptr->blacklist->lastwarning = CURRTIME;
}
}
/* they have a blacklist entry for this client */
if (listed)
{
dnsbl_hit(blcptr->u, blcptr->blacklist);
return;
}
l = dnsbl_queries(blcptr->u);
mowgli_node_delete(&blcptr->node, l);
free(blcptr);
}
/* XXX: no IPv6 implementation, not to concerned right now though. */
static void initiate_blacklist_dnsquery(struct Blacklist *blptr, user_t *u)
{
struct BlacklistClient *blcptr = malloc(sizeof(struct BlacklistClient));
char buf[IRCD_RES_HOSTLEN + 1];
int ip[4];
mowgli_list_t *l;
blcptr->blacklist = blptr;
blcptr->u = u;
blcptr->dns_query.ptr = blcptr;
blcptr->dns_query.callback = blacklist_dns_callback;
/* A sscanf worked fine for chary for many years, it'll be fine here */
sscanf(u->ip, "%d.%d.%d.%d", &ip[3], &ip[2], &ip[1], &ip[0]);
/* becomes 2.0.0.127.torbl.ahbl.org or whatever */
snprintf(buf, sizeof buf, "%d.%d.%d.%d.%s", ip[0], ip[1], ip[2], ip[3], blptr->host);
gethost_byname_type(buf, &blcptr->dns_query, T_A);
l = dnsbl_queries(u);
mowgli_node_add(blcptr, &blcptr->node, l);
blptr->refcount++;
}
/* public interfaces */
static struct Blacklist *new_blacklist(char *name)
{
struct Blacklist *blptr;
if (name == NULL)
return NULL;
blptr = find_blacklist(name);
if (blptr == NULL)
{
blptr = malloc(sizeof(struct Blacklist));
mowgli_node_add(blptr, mowgli_node_create(), &blacklist_list);
}
mowgli_strlcpy(blptr->host, name, IRCD_RES_HOSTLEN + 1);
blptr->lastwarning = 0;
return blptr;
}
static void lookup_blacklists(user_t *u)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
{
struct Blacklist *blptr = (struct Blacklist *) n->data;
blptr->status = 0;
if (u == NULL)
return;
initiate_blacklist_dnsquery(blptr, u);
}
}
/* This appears to be unnecessary on Atheme and only causes crashes so #if 0
* it out, at least for now. --jdhore
*/
#if 0
static void abort_blacklist_queries(user_t *u)
{
mowgli_node_t *n, *tn;
mowgli_list_t *l;
struct BlacklistClient *blcptr;
if (u == NULL)
return;
l = dnsbl_queries(u);
MOWGLI_ITER_FOREACH_SAFE(n, tn, l->head)
{
blcptr = n->data;
mowgli_node_delete(&blcptr->node, l);
unref_blacklist(blcptr->blacklist);
delete_resolver_queries(&blcptr->dns_query);
free(blcptr);
}
}
#endif
static void destroy_blacklists(void)
{
mowgli_node_t *n, *tn;
struct Blacklist *blptr;
MOWGLI_ITER_FOREACH_SAFE(n, tn, blacklist_list.head)
{
blptr = n->data;
blptr->hits = 0; /* keep it simple and consistent */
free(n->data);
mowgli_node_delete(n, &blacklist_list);
mowgli_node_free(n);
}
}
static int dnsbl_config_handler(mowgli_config_file_entry_t *ce)
{
mowgli_config_file_entry_t *cce;
MOWGLI_ITER_FOREACH(cce, ce->entries)
{
char *line = sstrdup(cce->varname);
new_blacklist(line);
}
return 0;
}
static void dnsbl_config_purge(void *unused)
{
destroy_blacklists();
}
static void check_dnsbls(hook_user_nick_t *data)
{
user_t *u = data->u;
mowgli_node_t *n;
if (!u)
return;
if (is_internal_client(u))
return;
if (!action)
return;
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
{
dnsbl_exempt_t *de = n->data;
if (!irccasecmp(de->ip, u->ip))
return;
}
lookup_blacklists(u);
}
static void dnsbl_hit(user_t *u, struct Blacklist *blptr)
{
service_t *svs;
svs = service_find("operserv");
if (!strcasecmp("SNOOP", action))
{
slog(LG_INFO, "DNSBL: \2%s\2!%s@%s [%s] is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
/* abort_blacklist_queries(u); */
return;
}
else if (!strcasecmp("NOTIFY", action))
{
slog(LG_INFO, "DNSBL: \2%s\2!%s@%s [%s] is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
notice(svs->nick, u->nick, "Your IP address %s is listed in DNS Blacklist %s", u->ip, blptr->host);
/* abort_blacklist_queries(u); */
return;
}
else if (!strcasecmp("KLINE", action))
{
slog(LG_INFO, "DNSBL: k-lining \2%s\2!%s@%s [%s] who is listed in DNS Blacklist %s.", u->nick, u->user, u->host, u->gecos, blptr->host);
/* abort_blacklist_queries(u); */
notice(svs->nick, u->nick, "Your IP address %s is listed in DNS Blacklist %s", u->ip, blptr->host);
kline_sts("*", "*", u->host, 86400, "Banned (DNS Blacklist)");
return;
}
}
static void osinfo_hook(sourceinfo_t *si)
{
mowgli_node_t *n;
if (action)
command_success_nodata(si, "Action taken when a user is an a DNSBL: %s", action);
else
command_success_nodata(si, "Action taken when a user is an a DNSBL: %s", "None");
MOWGLI_ITER_FOREACH(n, blacklist_list.head)
{
struct Blacklist *blptr = (struct Blacklist *) n->data;
command_success_nodata(si, "Blacklist(s): %s", blptr->host);
}
}
static void write_dnsbl_exempt_db(database_handle_t *db)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, dnsbl_elist.head)
{
dnsbl_exempt_t *de = n->data;
db_start_row(db, "BW");
db_write_word(db, de->ip);
db_write_time(db, de->exempt_ts);
db_write_word(db, de->creator);
db_write_word(db, de->reason);
db_commit_row(db);
}
}
static void db_h_ble(database_handle_t *db, const char *type)
{
const char *ip = db_sread_word(db);
time_t exempt_ts = db_sread_time(db);
const char *creator = db_sread_word(db);
const char *reason = db_sread_word(db);
dnsbl_exempt_t *de = smalloc(sizeof(dnsbl_exempt_t));
de->ip = sstrdup(ip);
de->exempt_ts = exempt_ts;
de->creator = sstrdup(creator);
de->reason = sstrdup(reason);
mowgli_node_add(de, mowgli_node_create(), &dnsbl_elist);
}
void
_modinit(module_t *m)
{
MODULE_TRY_REQUEST_SYMBOL(m, os_set_cmdtree, "operserv/set", "os_set_cmdtree");
if (!module_find_published("backend/opensex"))
{
slog(LG_INFO, "Module %s requires use of the OpenSEX database backend, refusing to load.", m->name);
m->mflags = MODTYPE_FAIL;
return;
}
hook_add_db_write(write_dnsbl_exempt_db);
db_register_type_handler("BLE", db_h_ble);
service_named_bind_command("operserv", &os_dnsblexempt);
service_named_bind_command("operserv", &os_dnsblscan);
hook_add_event("config_purge");
hook_add_config_purge(dnsbl_config_purge);
hook_add_event("user_add");
hook_add_user_add(check_dnsbls);
hook_add_event("operserv_info");
hook_add_operserv_info(osinfo_hook);
add_dupstr_conf_item("dnsbl_action", &conf_gi_table, 0, &action, NULL);
add_conf_item("BLACKLISTS", &conf_gi_table, dnsbl_config_handler);
command_add(&os_set_dnsblaction, *os_set_cmdtree);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_db_write(write_dnsbl_exempt_db);
hook_del_user_add(check_dnsbls);
hook_del_config_purge(dnsbl_config_purge);
hook_del_operserv_info(osinfo_hook);
db_unregister_type_handler("BLE");
del_conf_item("dnsbl_action", &conf_gi_table);
del_conf_item("BLACKLISTS", &conf_gi_table);
command_delete(&os_set_dnsblaction, *os_set_cmdtree);
service_named_unbind_command("operserv", &os_dnsblexempt);
service_named_unbind_command("operserv", &os_dnsblscan);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

88
gen_echoserver.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2005 William Pitcock <nenolod -at- nenolod.net>
* Rights to this code are as documented in doc/LICENSE.
*
* An echo server. (proof of concept for integrated XMLRPC HTTPD)
*
*/
#include "atheme.h"
#include "datastream.h"
DECLARE_MODULE_V1
(
"contrib/gen_echoserver", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod -at- nenolod.net>"
);
connection_t *listener;
static int my_read(connection_t *cptr, char *buf)
{
int n;
if ((n = read(cptr->fd, buf, BUFSIZE)) > 0)
{
buf[n] = '\0';
cnt.bin += n;
}
return n;
}
static void do_packet(connection_t *cptr, char *buf)
{
char *ptr, buf2[BUFSIZE * 2];
static char tmp[BUFSIZE * 2 + 1];
while ((ptr = strchr(buf, '\n')))
{
snprintf(buf2, (BUFSIZE * 2), "%s%s", tmp, buf);
*tmp = '\0';
slog(LG_DEBUG, "-{incoming}-> %s", buf2);
sendq_add(cptr, buf2, strlen(buf2));
buf = ptr + 1;
}
if (*buf)
{
mowgli_strlcpy(tmp, buf, BUFSIZE * 2);
tmp[BUFSIZE * 2] = '\0';
}
}
static void my_rhandler(connection_t * cptr)
{
char buf[BUFSIZE * 2];
if (!my_read(cptr, buf))
connection_close(cptr);
do_packet(cptr, buf);
}
static void do_listen(connection_t *cptr)
{
connection_t *newptr;
newptr = connection_accept_tcp(cptr, my_rhandler, NULL);
slog(LG_DEBUG, "do_listen(): accepted %d", cptr->fd);
}
void _modinit(module_t *m)
{
listener = connection_open_listener_tcp("127.0.0.1", 7100, do_listen);
}
void _moddeinit(module_unload_intent_t intent)
{
connection_close(listener);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

92
gen_listenerdemo.c Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2005 William Pitcock <nenolod -at- nenolod.net>
* Rights to this code are as documented in doc/LICENSE.
*
* Listener code demo.
*
*/
#include "atheme.h"
#include "datastream.h"
DECLARE_MODULE_V1
(
"contrib/gen_listenerdemo", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod -at- nenolod.net>"
);
connection_t *listener;
static int my_read(connection_t * cptr, char *buf)
{
int n;
if ((n = read(cptr->fd, buf, BUFSIZE)) > 0)
{
buf[n] = '\0';
cnt.bin += n;
}
return n;
}
static void do_packet(char *buf)
{
char *ptr, buf2[BUFSIZE * 2];
static char tmp[BUFSIZE * 2 + 1];
while ((ptr = strchr(buf, '\n')))
{
*ptr = '\0';
if (ptr != buf && *(ptr - 1) == '\r')
*(ptr - 1) = '\0';
snprintf(buf2, (BUFSIZE * 2), "%s%s", tmp, buf);
*tmp = '\0';
slog(LG_DEBUG, "-{incoming}-> %s", buf2);
buf = ptr + 1;
}
if (*buf)
{
mowgli_strlcpy(tmp, buf, BUFSIZE * 2);
tmp[BUFSIZE * 2] = '\0';
}
}
static void my_rhandler(connection_t * cptr)
{
char buf[BUFSIZE * 2];
if (!my_read(cptr, buf))
connection_close(cptr);
do_packet(buf);
}
static void do_listen(connection_t *cptr)
{
connection_t *newptr;
newptr = connection_accept_tcp(cptr, my_rhandler, NULL);
slog(LG_DEBUG, "do_listen(): accepted %d", cptr->fd);
}
void _modinit(module_t *m)
{
listener = connection_open_listener_tcp("127.0.0.1", 7100, do_listen);
}
void _moddeinit(module_unload_intent_t intent)
{
connection_close(listener);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

119
gen_vhostonreg.c Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright (c) 2005-2006 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* Sets usercloak metadata on register.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/gen_vhostonreg", false, _modinit, _moddeinit,
"$Revision: 7785 $",
"Atheme Development Group <http://www.atheme.org>"
);
/* allow us-ascii letters, digits and the following characters */
#define VALID_SPECIALS "-"
static int counter;
static void handle_verify_register(hook_user_req_t *req);
static void hook_user_identify(user_t *u);
void _modinit(module_t *m)
{
hook_add_event("user_verify_register");
hook_add_user_verify_register(handle_verify_register);
hook_add_event("user_identify");
hook_add_user_identify(hook_user_identify);
counter = (CURRTIME << 8) % 100000;
if (counter < 0)
counter += 100000;
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_verify_register(handle_verify_register);
hook_del_user_identify(hook_user_identify);
}
static void user_add_host(myuser_t *mu)
{
int maxlen1, i;
char newhost[HOSTLEN];
const char *p;
bool invalidchar = false;
maxlen1 = HOSTLEN - 2 - strlen(me.hidehostsuffix);
if (maxlen1 < 9)
return;
p = entity(mu)->name;
i = 0;
while (i < maxlen1 && *p != '\0')
{
if (isalnum(*p) || strchr(VALID_SPECIALS, *p))
newhost[i++] = *p;
else
invalidchar = true;
p++;
}
if (invalidchar || *p != '\0')
{
if (i > maxlen1 - 6)
i = maxlen1 - 6;
snprintf(newhost + i, sizeof newhost - i, "-%05d", counter);
counter++;
if (counter >= 100000)
counter = 0;
if (nicksvs.me != NULL)
{
myuser_notice(nicksvs.nick, mu, "Your account name cannot be used in a vhost directly. To ensure uniqueness, a number was added.");
myuser_notice(nicksvs.nick, mu, "To avoid this, register an account name containing only letters, digits and %s.", VALID_SPECIALS);
if (!nicksvs.no_nick_ownership && command_find(nicksvs.me->commands, "GROUP"))
myuser_notice(nicksvs.nick, mu, "If you drop %s you can group it to your new account.", entity(mu)->name);
}
}
else
newhost[i] = '\0';
mowgli_strlcat(newhost, ".", sizeof newhost);
mowgli_strlcat(newhost, me.hidehostsuffix, sizeof newhost);
metadata_add(mu, "private:usercloak", newhost);
}
static void handle_verify_register(hook_user_req_t *req)
{
myuser_t *mu = req->mu;
mowgli_node_t *n;
user_t *u;
if (me.hidehostsuffix == NULL)
return;
user_add_host(mu);
MOWGLI_ITER_FOREACH(n, mu->logins.head)
{
u = n->data;
hook_call_user_identify(u); /* XXX */
}
}
static void hook_user_identify(user_t *u)
{
/* if they have an existing cloak, don't do anything */
if ((metadata_find(u->myuser, "private:usercloak")) || (me.hidehostsuffix == NULL))
return;
/* they do not, add one. */
user_add_host(u->myuser);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

198
graphtastical.c Normal file
View File

@ -0,0 +1,198 @@
/*
* Copyright (c) 2007 William Pitcock
* Rights to this code are as documented in doc/LICENSE.
*
* Creates a .dot file for use with neato which displays
* user->channel relationships.
*
* == How to generate the graphs and How It Works ==
* Graphtastical creates a .dot file for graphviz's neato
* filter to use. The DOT language describes a graph's
* structure to graphviz in an opaque way.
*
* Because Graphviz nodes use unique identifiers for
* interconnection, the channels.dot file contains also
* information about social networks.
*
* Eventually Graphtastical will dump other graph datafiles
* too.
*
* To make a file from the data dumped by Graphtastical,
* the following commands will do:
*
* $ cat channels.dot | neato -Tgif -o map-channels.gif
* $ cat channels.dot | neato -Tsvg -o map-channels.svg
*
* Some maps (for larger networks) are going to be large,
* so you may want to provide links to both the GIF and
* SVG files as some people may only be able to make use of
* one or the other. Why that is, I'm not sure, and I'm not
* covering it here.
*
* == Privacy concerns ==
* If you are running Graphtastical on a network that has
* privacy concerns; you probably shouldn't.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/graphtastical", true, _modinit, NULL,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static mowgli_eventloop_timer_t *channels_timer = NULL;
static mowgli_eventloop_timer_t *uchannels_timer = NULL;
/* write channels.dot */
static void write_channels_dot_file(void *arg)
{
mychan_t *mc;
chanacs_t *ca;
mowgli_node_t *tn;
FILE *f;
int errno1, was_errored = 0;
mowgli_patricia_iteration_state_t state;
int root = 1;
mychan_t *pmc;
errno = 0;
/* write to a temporary file first */
if (!(f = fopen(DATADIR "/channels.dot.new", "w")))
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot create channels.dot.new: %s", strerror(errno1));
return;
}
fprintf(f, "graph channels {\n");
fprintf(f, "edge [color=blue len=7.5 fontname=\"Verdana\" fontsize=8]\n");
fprintf(f, "node [fontname=\"Verdana\" fontsize=8]\n");
slog(LG_DEBUG, "graphtastical: dumping mychans");
MOWGLI_PATRICIA_FOREACH(mc, &state, mclist)
{
fprintf(f, "\"%s\"", mc->name);
if (!root)
fprintf(f, "-- \"%s\"", pmc->name);
pmc = mc;
fprintf(f, "[fontname=\"Verdana\" fontsize=8]\n");
MOWGLI_ITER_FOREACH(tn, mc->chanacs.head)
{
ca = (chanacs_t *)tn->data;
if (ca->level & CA_AKICK)
continue;
fprintf(f, "\"%s\" -- \"%s\" [fontname=\"Verdana\" fontsize=8]\n", ca->entity ? ca->entity->name : ca->host, mc->name);
}
}
fprintf(f, "}\n");
was_errored = ferror(f);
was_errored |= fclose(f);
if (was_errored)
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot write to channels.dot.new: %s", strerror(errno1));
return;
}
/* now, replace the old database with the new one, using an atomic rename */
if ((srename(DATADIR "/channels.dot.new", DATADIR "/channels.dot")) < 0)
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot rename channels.dot.new to channels.dot: %s", strerror(errno1));
return;
}
}
/* write uchannels.dot */
static void write_uchannels_dot_file(void *arg)
{
channel_t *c;
chanuser_t *cu;
mowgli_node_t *tn;
FILE *f;
int errno1, was_errored = 0;
mowgli_patricia_iteration_state_t state;
errno = 0;
/* write to a temporary file first */
if (!(f = fopen(DATADIR "/uchannels.dot.new", "w")))
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot create channels.dot.new: %s", strerror(errno1));
return;
}
fprintf(f, "graph uchannels {\n");
fprintf(f, "edge [color=blue len=7.5 fontname=\"Verdana\" fontsize=8]\n");
fprintf(f, "node [fontname=\"Verdana\" fontsize=8]\n");
slog(LG_DEBUG, "graphtastical: dumping chans");
MOWGLI_PATRICIA_FOREACH(c, &state, chanlist)
{
fprintf(f, "\"%s\"", c->name);
fprintf(f, "[fontname=\"Verdana\" fontsize=8]\n");
MOWGLI_ITER_FOREACH(tn, c->members.head)
{
cu = (chanuser_t *)tn->data;
fprintf(f, "\"%s\" -- \"%s\" [fontname=\"Verdana\" fontsize=8]\n", cu->user->nick, c->name);
}
}
fprintf(f, "}\n");
was_errored = ferror(f);
was_errored |= fclose(f);
if (was_errored)
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot write to uchannels.dot.new: %s", strerror(errno1));
return;
}
/* now, replace the old database with the new one, using an atomic rename */
if ((srename(DATADIR "/uchannels.dot.new", DATADIR "/uchannels.dot")) < 0)
{
errno1 = errno;
slog(LG_ERROR, "graphtastical: cannot rename uchannels.dot.new to uchannels.dot: %s", strerror(errno1));
return;
}
}
void _modinit(module_t *m)
{
write_channels_dot_file(NULL);
write_uchannels_dot_file(NULL);
channels_timer = mowgli_timer_add(base_eventloop, "write_channels_dot_file", write_channels_dot_file, NULL, 60);
uchannels_timer = mowgli_timer_add(base_eventloop, "write_uchannels_dot_file", write_uchannels_dot_file, NULL, 60);
}
void _moddeinit(module_unload_intent_t intent)
{
mowgli_timer_destroy(base_eventloop, channels_timer);
mowgli_timer_destroy(base_eventloop, uchannels_timer);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

74
gs_roulette.c Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2005-2006 William Pitcock <nenolod@nenolod.net> et al
* Rights to this code are documented in doc/LICENSE.
*
* Russian Roulette game. Will actually /KILL the user that gets "shot".
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/gs_roulette", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void command_roulette(sourceinfo_t *si, int parc, char *parv[]);
command_t cmd_roulette = { "ROULETTE", N_("A game of Russian Roulette."), AC_NONE, 0, command_roulette, { .path = "contrib/roulette" } };
void _modinit(module_t * m)
{
service_named_bind_command("gameserv", &cmd_roulette);
service_named_bind_command("chanserv", &cmd_roulette);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("gameserv", &cmd_roulette);
service_named_unbind_command("chanserv", &cmd_roulette);
}
/*
* Handle reporting for both fantasy commands and normal commands in GameServ
* quickly and easily. Of course, sourceinfo has a vtable that can be manipulated,
* but this is quicker and easier... -- nenolod
*/
static void gs_command_report(sourceinfo_t *si, const char *fmt, ...)
{
va_list args;
char buf[BUFSIZE];
va_start(args, fmt);
vsnprintf(buf, BUFSIZE, fmt, args);
va_end(args);
if (si->c != NULL)
msg(chansvs.nick, si->c->name, "%s", buf);
else
command_success_nodata(si, "%s", buf);
if (!strcasecmp(buf, "*BANG*"))
kill_user(si->service->me, si->su, "Lost at Russian Roulette.");
}
static void command_roulette(sourceinfo_t *si, int parc, char *parv[])
{
static const char *roulette_responses[2] = {
N_("*BANG*"),
N_("*CLICK*")
};
srand(time(NULL));
gs_command_report(si, "%s", roulette_responses[rand() % 6 == 0]);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

395
ircd_announceserv.c Normal file
View File

@ -0,0 +1,395 @@
/*
* Copyright (c) 2005 Atheme Development Group
* Rights to this code are documented in doc/LICENSE.
*
* This file contains the main() routine.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ircd_announceserv", false, _modinit, _moddeinit,
PACKAGE_STRING,
"JD and Taros"
);
service_t *announcesvs;
static void as_cmd_help(sourceinfo_t *si, int parc, char *parv[]);
static void account_drop_request(myuser_t *mu);
static void as_cmd_request(sourceinfo_t *si, int parc, char *parv[]);
static void as_cmd_waiting(sourceinfo_t *si, int parc, char *parv[]);
static void as_cmd_reject(sourceinfo_t *si, int parc, char *parv[]);
static void as_cmd_activate(sourceinfo_t *si, int parc, char *parv[]);
static void as_cmd_cancel(sourceinfo_t *si, int parc, char *parv[]);
static void write_asreqdb(database_handle_t *db);
static void db_h_ar(database_handle_t *db, const char *type);
command_t as_help = { "HELP", N_(N_("Displays contextual help information.")), AC_NONE, 2, as_cmd_help, { .path = "help/help" } };
command_t as_request = { "REQUEST", N_("Requests new announcement."), AC_AUTHENTICATED, 2, as_cmd_request, { .path = "contrib/as_request" } };
command_t as_waiting = { "WAITING", N_("Lists announcements currently waiting for activation."), PRIV_GLOBAL, 1, as_cmd_waiting, { .path = "contrib/as_waiting" } };
command_t as_reject = { "REJECT", N_("Reject the requested announcement for the given nick."), PRIV_GLOBAL, 2, as_cmd_reject, { .path = "contrib/as_reject" } };
command_t as_activate = { "ACTIVATE", N_("Activate the requested announcement for a given nick."), PRIV_GLOBAL, 2, as_cmd_activate, { .path = "contrib/as_activate" } };
command_t as_cancel = { "CANCEL", N_("Cancels your requested announcement."), AC_AUTHENTICATED, 0, as_cmd_cancel, { .path = "contrib/as_cancel" } };
struct asreq_ {
char *nick;
char *subject;
time_t announce_ts;
char *creator;
char *text;
};
typedef struct asreq_ asreq_t;
mowgli_list_t as_reqlist;
void _modinit(module_t *m)
{
announcesvs = service_add("announceserv", NULL);
hook_add_event("user_drop");
hook_add_user_drop(account_drop_request);
hook_add_db_write(write_asreqdb);
db_register_type_handler("AR", db_h_ar);
if (announcesvs == NULL)
return;
service_bind_command(announcesvs, &as_help);
service_bind_command(announcesvs, &as_request);
service_bind_command(announcesvs, &as_waiting);
service_bind_command(announcesvs, &as_reject);
service_bind_command(announcesvs, &as_activate);
service_bind_command(announcesvs, &as_cancel);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_drop(account_drop_request);
hook_del_db_write(write_asreqdb);
db_unregister_type_handler("AR");
if (announcesvs != NULL)
{
service_unbind_command(announcesvs, &as_help);
service_unbind_command(announcesvs, &as_request);
service_unbind_command(announcesvs, &as_waiting);
service_unbind_command(announcesvs, &as_reject);
service_unbind_command(announcesvs, &as_activate);
service_unbind_command(announcesvs, &as_cancel);
service_delete(announcesvs);
announcesvs = NULL;
}
}
static void write_asreqdb(database_handle_t *db)
{
mowgli_node_t *n;
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
asreq_t *l = n->data;
db_start_row(db, "AR");
db_write_word(db, l->nick);
db_write_word(db, l->subject);
db_write_time(db, l->announce_ts);
db_write_word(db, l->creator);
db_write_str(db, l->text);
db_commit_row(db);
}
}
static void db_h_ar(database_handle_t *db, const char *type)
{
const char *nick = db_sread_word(db);
const char *subject = db_sread_word(db);
time_t announce_ts = db_sread_time(db);
const char *creator = db_sread_word(db);
const char *text = db_sread_str(db);
asreq_t *l = smalloc(sizeof(asreq_t));
l->nick = strshare_get(nick);
l->creator = strshare_get(creator);
l->subject = sstrdup(subject);
l->announce_ts = announce_ts;
l->text = sstrdup(text);
mowgli_node_add(l, mowgli_node_create(), &as_reqlist);
}
/* Properly remove announcement requests from the DB if an account is dropped */
static void account_drop_request(myuser_t *mu)
{
mowgli_node_t *n;
asreq_t *l;
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
if (!irccasecmp(l->nick, entity(mu)->name))
{
slog(LG_REGISTER, "ANNOUNCEREQ:DROPACCOUNT: \2%s\2 %s\2", l->nick, l->text);
mowgli_node_delete(n, &as_reqlist);
strshare_unref(l->nick);
strshare_unref(l->creator);
free(l->subject);
free(l->text);
free(l);
return;
}
}
}
/* HELP <command> [params] */
void as_cmd_help(sourceinfo_t *si, int parc, char *parv[])
{
char *command = parv[0];
if (!command)
{
command_success_nodata(si, _("***** \2%s Help\2 *****"), si->service->nick);
command_success_nodata(si, _("\2%s\2 allows users to request a network announcement."), si->service->nick);
command_success_nodata(si, " ");
command_success_nodata(si, _("For more information on a command, type:"));
command_success_nodata(si, "\2/%s%s help <command>\2", (ircd->uses_rcommand == false) ? "msg " : "", si->service->disp);
command_success_nodata(si, " ");
command_help(si, si->service->commands);
command_success_nodata(si, _("***** \2End of Help\2 *****"));
return;
}
/* take the command through the hash table */
help_display(si, si->service, command, si->service->commands);
}
static void as_cmd_request(sourceinfo_t *si, int parc, char *parv[])
{
char *subject = parv[0];
char *text = parv[1];
char *target;
char *subject2;
char buf [BUFSIZE];
mowgli_node_t *n;
asreq_t *l;
if (!text || !subject)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "REQUEST");
command_fail(si, fault_needmoreparams, _("Syntax: REQUEST <subject> <text>"));
return;
}
if (metadata_find(si->smu, "private:restrict:setter"))
{
command_fail(si, fault_noprivs, _("You have been restricted from requesting announcements by network staff."));
return;
}
target = entity(si->smu)->name;
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
if (!irccasecmp(l->nick, target))
{
command_fail(si, fault_badparams, _("You cannot request more than one announcement. Use CANCEL if you wish to cancel your current announcement and submit another."));
return;
}
}
/* Check the subject for being too long as well. 35 chars is probably a safe limit here.
* Used here because we don't want users that don't know any better making too-long messages.
*/
if (strlen(subject) > 35)
{
command_fail(si, fault_badparams, _("Your subject is too long. Subjects need to be under 35 characters."));
return;
}
/* Check if the announcement is too long or not. 450 characters is safe for our usecase */
if (strlen(text) > 450)
{
command_fail(si, fault_badparams, _("Your announcement is too long. Announcements need to be under 450 characters."));
return;
}
snprintf(buf, BUFSIZE, "%s", text);
l = smalloc(sizeof(asreq_t));
l->nick = strshare_ref(target);
l->subject = sstrdup(subject);
l->announce_ts = CURRTIME;;
l->creator = strshare_ref(target);
l->text = sstrdup(buf);
n = mowgli_node_create();
mowgli_node_add(l, n, &as_reqlist);
subject2 = sstrdup(l->subject);
/* This doesn't need to be as efficient as InfoServ, so let's just use replace() */
replace(subject2, BUFSIZE, "_", " ");
command_success_nodata(si, _("You have requested the following announcement: "));
command_success_nodata(si, _("[%s - %s] %s"), subject2, l->creator, buf);
/* This is kind of hacky, and the slog will come from operserv, not announceserv.
* Still, it's required so the message will cut off properly, and I can't find a less hacky way to do it. */
logcommand(si, CMDLOG_REQUEST, "REQUEST:");
slog(CMDLOG_REQUEST, "[%s - %s] %s", subject2, l->creator, buf);
free(subject2);
return;
}
static void as_cmd_activate(sourceinfo_t *si, int parc, char *parv[])
{
char *nick = parv[0];
user_t *u;
char *subject2;
char buf[BUFSIZE];
asreq_t *l;
mowgli_node_t *n;
if (!nick)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ACTIVATE");
command_fail(si, fault_needmoreparams, _("Syntax: ACTIVATE <nick>"));
return;
}
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
if (!irccasecmp(l->nick, nick))
{
if ((u = user_find_named(nick)) != NULL)
notice(si->service->nick, u->nick, "[auto memo] Your requested announcement has been approved.");
subject2 = sstrdup(l->subject);
replace(subject2, BUFSIZE, "_", " ");
logcommand(si, CMDLOG_REQUEST, "ACTIVATE: \2%s\2", nick);
snprintf(buf, BUFSIZE, "[%s - %s] %s", subject2, l->creator, l->text);
mowgli_node_delete(n, &as_reqlist);
free(subject2);
free(l->nick);
free(l->subject);
free(l->creator);
free(l->text);
free(l);
notice_global_sts(si->service->me, "*", buf);
return;
}
}
command_success_nodata(si, _("Nick \2%s\2 not found in announce request database."), nick);
}
static void as_cmd_reject(sourceinfo_t *si, int parc, char *parv[])
{
char *nick = parv[0];
user_t *u;
asreq_t *l;
mowgli_node_t *n;
if (!nick)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "REJECT");
command_fail(si, fault_needmoreparams, _("Syntax: REJECT <nick>"));
return;
}
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
if (!irccasecmp(l->nick, nick))
{
if ((u = user_find_named(nick)) != NULL)
notice(si->service->nick, u->nick, "[auto memo] Your requested announcement has been rejected.");
logcommand(si, CMDLOG_REQUEST, "REJECT: \2%s\2", nick);
mowgli_node_delete(n, &as_reqlist);
free(l->nick);
free(l->subject);
free(l->creator);
free(l->text);
free(l);
return;
}
}
command_success_nodata(si, _("Nick \2%s\2 not found in announcement request database."), nick);
}
static void as_cmd_waiting(sourceinfo_t *si, int parc, char *parv[])
{
asreq_t *l;
mowgli_node_t *n;
char *subject2;
char buf[BUFSIZE];
struct tm tm;
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
tm = *localtime(&l->announce_ts);
strftime(buf, BUFSIZE, TIME_FORMAT, &tm);
subject2 = sstrdup(l->subject);
replace(subject2, BUFSIZE, "_", " ");
/* This needs to be two lines for cutoff purposes */
command_success_nodata(si, "Account:\2%s\2, Subject: %s, Requested On: \2%s\2, Announcement:",
l->nick, subject2, buf);
command_success_nodata(si, "%s", l->text);
free(subject2);
}
command_success_nodata(si, "End of list.");
logcommand(si, CMDLOG_GET, "WAITING");
}
static void as_cmd_cancel(sourceinfo_t *si, int parc, char *parv[])
{
asreq_t *l;
mowgli_node_t *n;
char *target;
target = entity(si->smu)->name;
MOWGLI_LIST_FOREACH(n, as_reqlist.head)
{
l = n->data;
if (!irccasecmp(l->nick, target))
{
mowgli_node_delete(n, &as_reqlist);
strshare_unref(l->nick);
strshare_unref(l->creator);
free(l->subject);
free(l->text);
free(l);
command_success_nodata(si, "Your pending announcement has been canceled.");
logcommand(si, CMDLOG_REQUEST, "CANCEL");
return;
}
}
command_fail(si, fault_badparams, _("You do not have a pending announcement to cancel."));
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

58
ircd_catserv.c Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2005 William Pitcock, et al.
* The rights to this code are as documented under doc/LICENSE.
*
* Meow!
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ircd_catserv", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
service_t *catserv;
static void catserv_cmd_meow(sourceinfo_t *si, int parc, char *parv[]);
static void catserv_cmd_help(sourceinfo_t *si, int parc, char *parv[]);
command_t catserv_meow = { "MEOW", "Makes the cute little kitty-cat meow!",
AC_NONE, 0, catserv_cmd_meow, { .path = "" } };
command_t catserv_help = { "HELP", "Displays contextual help information.",
AC_NONE, 1, catserv_cmd_help, { .path = "help" } };
void _modinit(module_t *m)
{
catserv = service_add("catserv", NULL);
service_bind_command(catserv, &catserv_meow);
service_bind_command(catserv, &catserv_help);
}
void _moddeinit(module_unload_intent_t intent)
{
service_unbind_command(catserv, &catserv_meow);
service_unbind_command(catserv, &catserv_help);
service_delete(catserv);
}
static void catserv_cmd_meow(sourceinfo_t *si, int parc, char *parv[])
{
command_success_nodata(si, "Meow!");
}
static void catserv_cmd_help(sourceinfo_t *si, int parc, char *parv[])
{
command_help(si, si->service->commands);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

325
ircd_loveserv.c Normal file
View File

@ -0,0 +1,325 @@
/*
* Copyright (c) 2006 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* LoveServ implementation.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ircd_loveserv", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
service_t *loveserv;
static void _ls_admirer(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ADMIRER");
command_fail(si, fault_needmoreparams, "Syntax: ADMIRER <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "%s has been told that they have a secret admirer. :)", target);
notice(loveserv->nick, target, "You have a secret admirer ;)");
}
command_t ls_admirer = { "ADMIRER", "Tell somebody they have a secret admirer.", AC_NONE, 1, _ls_admirer, { .path = "" } };
static void _ls_rose(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "ROSE");
command_fail(si, fault_needmoreparams, "Syntax: ROSE <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your rose has been sent to %s! :)", target);
notice(loveserv->nick, target, "%s has sent you a pretty rose: \00303--<--<--<{\00304@", si->su->nick);
}
command_t ls_rose = { "ROSE", "Sends a rose to somebody.", AC_NONE, 1, _ls_rose, { .path = "" } };
static void _ls_chocolate(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "CHOCOLATE");
command_fail(si, fault_needmoreparams, "Syntax: CHOCOLATE <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your chocolates have been sent to %s! :)", target);
notice(loveserv->nick, target, "%s would like you to have this YUMMY box of chocolates.", si->su->nick);
}
command_t ls_chocolate = { "CHOCOLATE", "Sends chocolates to somebody.", AC_NONE, 1, _ls_chocolate, { .path = "" } };
static void _ls_candy(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "CANDY");
command_fail(si, fault_needmoreparams, "Syntax: CANDY <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your bag of candy has been sent to %s! :)", target);
notice(loveserv->nick, target, "%s would like you to have this bag of heart-shaped candies.", si->su->nick);
}
command_t ls_candy = { "CANDY", "Sends a bag of candy to somebody.", AC_NONE, 1, _ls_candy, { .path = "" } };
static void _ls_hug(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "HUG");
command_fail(si, fault_needmoreparams, "Syntax: HUG <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "You have virtually hugged %s!", target);
notice(loveserv->nick, target, "%s has sent you a \002BIG WARM HUG\002.", si->su->nick);
}
command_t ls_hug = { "HUG", "Reach out and hug somebody.", AC_NONE, 1, _ls_hug, { .path = "" } };
static void _ls_kiss(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "KISS");
command_fail(si, fault_needmoreparams, "Syntax: KISS <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "You have virtually kissed %s!", target);
notice(loveserv->nick, target, "%s has sent you a \00304kiss\003.", si->su->nick);
}
command_t ls_kiss = { "KISS", "Kiss somebody.", AC_NONE, 1, _ls_kiss, { .path = "" } };
static void _ls_lovenote(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
char *note = parv[1];
if (!target || !note)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "LOVENOTE");
command_fail(si, fault_needmoreparams, "Syntax: LOVENOTE <target> <message>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your love-note to %s has been sent.", target);
notice(loveserv->nick, target, "%s has sent you a love-note which reads: %s", si->su->nick, note);
}
command_t ls_lovenote = { "LOVENOTE", "Sends a lovenote to somebody.", AC_NONE, 2, _ls_lovenote, { .path = "" } };
static void _ls_apology(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
char *note = parv[1];
if (!target || !note)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "APOLOGY");
command_fail(si, fault_needmoreparams, "Syntax: APOLOGY <target> <message>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your apology to %s has been sent.", target);
notice(loveserv->nick, target, "%s would like to apologize for: %s", si->su->nick, note);
}
command_t ls_apology = { "APOLOGY", "Sends an apology to somebody.", AC_NONE, 2, _ls_apology, { .path = "" } };
static void _ls_thankyou(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
char *note = parv[1];
if (!target || !note)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "THANKYOU");
command_fail(si, fault_needmoreparams, "Syntax: THANKYOU <target> <message>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your thank-you note to %s has been sent.", target);
notice(loveserv->nick, target, "%s would like to thank you for: %s", si->su->nick, note);
}
command_t ls_thankyou = { "THANKYOU", "Sends a thank-you note to somebody.", AC_NONE, 2, _ls_thankyou, { .path = "" } };
static void _ls_spank(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SPANK");
command_fail(si, fault_needmoreparams, "Syntax: SPANK <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "You have virtually spanked %s!", target);
notice(loveserv->nick, target, "%s has given you a virtual playful spanking.", si->su->nick);
}
command_t ls_spank = { "SPANK", "Gives somebody a spanking.", AC_NONE, 1, _ls_spank, { .path = "" } };
static void _ls_chocobo(sourceinfo_t *si, int parc, char *parv[]) /* silly */
{
char *target = parv[0];
if (!target)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "CHOCOBO");
command_fail(si, fault_needmoreparams, "Syntax: CHOCOBO <target>");
return;
}
if (!user_find_named(target))
{
command_fail(si, fault_nosuch_target, "As much as I'd love to do this, you need to specify a person who really exists!");
return;
}
command_success_nodata(si, "Your chocobo has been sent to %s.", target);
notice(loveserv->nick, target, "%s would like you to have this chocobo. \00308Kweh!\003", si->su->nick);
}
command_t ls_chocobo = { "CHOCOBO", "Sends a chocobo to somebody.", AC_NONE, 1, _ls_chocobo, { .path = "" } };
static void _ls_help(sourceinfo_t *si, int parc, char *parv[])
{
command_help(si, si->service->commands);
}
command_t ls_help = { "HELP", "Displays contextual help information.",
AC_NONE, 1, _ls_help, { .path = "help" } };
void _modinit(module_t *m)
{
loveserv = service_add("LoveServ", NULL);
service_bind_command(loveserv, &ls_admirer);
service_bind_command(loveserv, &ls_rose);
service_bind_command(loveserv, &ls_chocolate);
service_bind_command(loveserv, &ls_candy);
service_bind_command(loveserv, &ls_hug);
service_bind_command(loveserv, &ls_kiss);
service_bind_command(loveserv, &ls_lovenote);
service_bind_command(loveserv, &ls_apology);
service_bind_command(loveserv, &ls_thankyou);
service_bind_command(loveserv, &ls_spank);
service_bind_command(loveserv, &ls_chocobo);
service_bind_command(loveserv, &ls_help);
}
void _moddeinit(module_unload_intent_t intent)
{
service_unbind_command(loveserv, &ls_admirer);
service_unbind_command(loveserv, &ls_rose);
service_unbind_command(loveserv, &ls_chocolate);
service_unbind_command(loveserv, &ls_candy);
service_unbind_command(loveserv, &ls_hug);
service_unbind_command(loveserv, &ls_kiss);
service_unbind_command(loveserv, &ls_lovenote);
service_unbind_command(loveserv, &ls_apology);
service_unbind_command(loveserv, &ls_thankyou);
service_unbind_command(loveserv, &ls_spank);
service_unbind_command(loveserv, &ls_chocobo);
service_unbind_command(loveserv, &ls_help);
if (loveserv)
service_delete(loveserv);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

159
ircs_crypto_trans.c Normal file
View File

@ -0,0 +1,159 @@
/*
* Copyright (c) 2005 Atheme Development Group
* Rights to this code are as documented in doc/LICENSE.
*
* IRCServices's weird password encryption thingy, taken from Anope 1.6.3.
*
*/
/* Include file for high-level encryption routines.
*
* (C) 2003 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for furhter details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* $\Id: myencrypt.c 5 2004-03-29 01:29:50Z dane $
*
*/
#include "atheme.h"
// Record password transition log?
// #define PW_TRANSITION_LOG "./pwtransition.sh"
/* necessary anope defines */
#define PASSMAX 32
#define ENCRYPT_MD5
/*************************************************************************/
/******** Our own high-level routines. ********/
#define XTOI(c) ((c)>9 ? (c)-'A'+10 : (c)-'0')
/* Result of this:
* c in [-128,9] => [-183,-46]
* c in [10,127] => [-38,79]
*/
/* Encrypt `src' of length `len' and store the result in `dest'. If the
* resulting string would be longer than `size', return -1 and leave `dest'
* unchanged; else return 0.
*/
static int myencrypt(const char *src, int len, char *dest, int size)
{
#ifdef ENCRYPT_MD5
MD5_CTX context;
char digest[33];
char dest2[16];
int i;
#ifdef PW_TRANSITION_LOG
FILE *tp;
#endif
if (size < 32)
return -1;
memset(&context, 0, sizeof(context));
memset(&digest, 0, sizeof(digest));
MD5Init(&context);
MD5Update(&context, (unsigned const char *) src, (size_t) len);
MD5Final((unsigned char *) digest, &context);
/* convert to hex, skipping last 8 bytes (constant) -- jilles */
// strcpy(digest, "$ircservices$");
for (i = 0; i <= 7; i++)
// sprintf(dest + 13 + 2 * i, "%02x", 255 & digest[i]);
sprintf(dest + 2 * i, "%02x", 255 & digest[i]);
#ifdef PW_TRANSITION_LOG
tp = popen(PW_TRANSITION_LOG,"w");
if (tp != NULL)
{
fprintf(tp,"%s\n%s\n",src,dest);
pclose(tp);
}
#endif
return 0;
#endif
return -1; /* unknown encryption algorithm */
}
#if 0 /* unused */
/* Shortcut for encrypting a null-terminated string in place. */
static int encrypt_in_place(char *buf, int size)
{
return myencrypt(buf, strlen(buf), buf, size);
}
#endif
/* Compare a plaintext string against an encrypted password. Return 1 if
* they match, 0 if not, and -1 if something went wrong. */
static int check_password(const char *plaintext, const char *password)
{
char buf[BUFSIZE];
if (myencrypt(plaintext, strlen(plaintext), buf, sizeof(buf)) < 0)
return -1;
#ifdef ENCRYPT_MD5
if (strcmp(buf, password) == 0)
#else
if (0)
#endif
return 1;
else
return 0;
}
/*************************************************************************/
DECLARE_MODULE_V1
(
"crypto/ircservices", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Jilles Tjoelker <jilles@stack.nl>"
);
static char *ircservices_crypt_string(char *key, char *salt)
{
static char output[PASSMAX];
if (salt[0] == '$' && salt[1] == '1') /* this is a new pw XXX */
{
myencrypt(key, strlen(key), output, PASSMAX);
return output;
}
else
{
if (check_password(key, salt))
return salt;
else
return "";
}
}
void _modinit(module_t *m)
{
crypt_string = &ircservices_crypt_string;
crypto_module_loaded = true;
}
void _moddeinit(module_unload_intent_t intent)
{
crypt_string = &generic_crypt_string;
crypto_module_loaded = false;
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

65
mlocktweaker.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2007 William Pitcock
* Rights to this code are as documented in doc/LICENSE.
*
* mlocktweaker.c: A module which tweaks mlock on registration.
*
*/
#include "atheme.h"
/*
* Set this to the string of mlock changes you want to make.
* This is in addition to the default mlock, so -nt if you want to
* remove those mlocks, etcetera.
*/
#define MLOCK_CHANGE "-t+c"
DECLARE_MODULE_V1
(
"contrib/mlocktweaker", false, _modinit, _moddeinit,
PACKAGE_STRING,
"William Pitcock <nenolod@atheme.org>"
);
static void handle_channel_register(void *vptr);
void _modinit(module_t *m)
{
hook_add_event("channel_register");
hook_add_first_channel_register(handle_channel_register);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_register(handle_channel_register);
}
static void handle_channel_register(void *vptr)
{
hook_channel_req_t *hdata = vptr;
mychan_t *mc = hdata->mc;
unsigned int *target;
char *it, *str = MLOCK_CHANGE;
if (mc == NULL)
return;
target = &mc->mlock_on;
it = str;
switch(*it++ != '\0')
{
case '+':
target = &mc->mlock_on;
break;
case '-':
target = &mc->mlock_off;
break;
default:
*target |= mode_to_flag(*it);
break;
}
mc->mlock_off &= ~mc->mlock_on;
}

181
ms_fsend.c Normal file
View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2005 Atheme Development Group
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the Memoserv FSEND function
*
*/
#include "atheme.h"
/* MEMOLEN + 8, so the "[FORCE] " string will fit */
#define FMEMOLEN 308
DECLARE_MODULE_V1
(
"contrib/ms_fsend", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void ms_cmd_fsend(sourceinfo_t *si, int parc, char *parv[]);
/* MARK is prolly the most appropriate priv (that I can think of), if you can
* think of a better one, feel free to change it. --jdhore
*/
command_t ms_fsend = { "FSEND", N_("Forcibly sends a memo to a user."),
PRIV_MARK, 2, ms_cmd_fsend, { .path = "contrib/fsend" } };
void _modinit(module_t *m)
{
service_named_bind_command("memoserv", &ms_fsend);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("memoserv", &ms_fsend);
}
static void ms_cmd_fsend(sourceinfo_t *si, int parc, char *parv[])
{
/* misc structs etc */
user_t *tu;
myuser_t *tmu;
mowgli_node_t *n;
mymemo_t *memo;
service_t *memoserv;
/* Grab args */
char *target = parv[0];
char *m = parv[1];
/* Arg validation */
if (!target || !m)
{
command_fail(si, fault_needmoreparams,
STR_INSUFFICIENT_PARAMS, "FSEND");
command_fail(si, fault_needmoreparams,
"Syntax: FSEND <user> <memo>");
return;
}
if (!si->smu)
{
command_fail(si, fault_noprivs, _("You are not logged in."));
return;
}
/* rate limit it -- jilles */
if (CURRTIME - si->smu->memo_ratelimit_time > MEMO_MAX_TIME)
si->smu->memo_ratelimit_num = 0;
if (si->smu->memo_ratelimit_num > MEMO_MAX_NUM && !has_priv(si, PRIV_FLOOD))
{
command_fail(si, fault_toomany, _("You have used this command too many times; please wait a while and try again."));
return;
}
/* Check for memo text length -- includes/common.h */
if (strlen(m) >= MEMOLEN)
{
command_fail(si, fault_badparams,
"Please make sure your memo is less than %d characters", MEMOLEN);
return;
}
/* Check to make sure the memo doesn't contain hostile CTCP responses.
* realistically, we'll probably want to check the _entire_ message for this... --nenolod
*/
if (*m == '\001')
{
command_fail(si, fault_badparams, _("Your memo contains invalid characters."));
return;
}
memoserv = service_find("memoserv");
if (memoserv == NULL)
memoserv = si->service;
if (*target != '#' && *target != '!')
{
/* See if target is valid */
if (!(tmu = myuser_find_ext(target)))
{
command_fail(si, fault_nosuch_target,
"\2%s\2 is not registered.", target);
return;
}
si->smu->memo_ratelimit_num++;
si->smu->memo_ratelimit_time = CURRTIME;
/* Check to make sure target inbox not full */
if (tmu->memos.count >= me.mdlimit)
{
command_fail(si, fault_toomany, _("%s's inbox is full"), target);
logcommand(si, CMDLOG_SET, "failed SEND to \2%s\2 (target inbox full)", entity(tmu)->name);
return;
}
logcommand(si, CMDLOG_ADMIN, "FSEND: to \2%s\2", entity(tmu)->name);
/* Malloc and populate struct */
memo = smalloc(sizeof(mymemo_t));
memo->sent = CURRTIME;
memo->status = 0;
mowgli_strlcpy(memo->sender,entity(si->smu)->name,NICKLEN);
mowgli_strlcpy(memo->text, "[FORCE] ", FMEMOLEN);
mowgli_strlcat(memo->text, m, FMEMOLEN);
/* Create a linked list node and add to memos */
n = mowgli_node_create();
mowgli_node_add(memo, n, &tmu->memos);
tmu->memoct_new++;
/* Should we email this? */
if (tmu->flags & MU_EMAILMEMOS)
{
sendemail(si->su, EMAIL_MEMO, tmu, memo->text);
}
/* Note: do not disclose other nicks they're logged in with
* -- jilles
*
* Actually, I don't see the point in this at all. If they want this information,
* they should use WHOIS. --nenolod
*/
tu = user_find_named(target);
if (tu != NULL && tu->myuser == tmu)
command_success_nodata(si, _("%s is currently online, and you may talk directly, by sending a private message."), target);
/* Is the user online? If so, tell them about the new memo. */
if (si->su == NULL || !irccasecmp(si->su->nick, entity(si->smu)->name))
myuser_notice(memoserv->nick, tmu, "You have a new memo from %s (%zu).", entity(si->smu)->name, MOWGLI_LIST_LENGTH(&tmu->memos));
else
myuser_notice(memoserv->nick, tmu, "You have a new memo from %s (nick: %s) (%zu).", entity(si->smu)->name, si->su->nick, MOWGLI_LIST_LENGTH(&tmu->memos));
myuser_notice(memoserv->nick, tmu, _("To read it, type /%s%s READ %zu"),
ircd->uses_rcommand ? "" : "msg ", memoserv->disp, MOWGLI_LIST_LENGTH(&tmu->memos));
/* Tell user memo sent */
command_success_nodata(si, _("The memo has been successfully sent to \2%s\2."), target);
}
else if (*target == '#')
{
command_fail(si, fault_nosuch_target, _("Channel memos may not be forced."));
}
else
{
command_fail(si, fault_nosuch_target, _("Group memos may not be forced."));
}
return;
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

221
ns_ajoin.c Normal file
View File

@ -0,0 +1,221 @@
/*
* Copyright (c) 2007 Jilles Tjoelker
* Copyright (c) 2008 Robin Burchell
* Rights to this code are as documented in doc/LICENSE.
*
* Services-side autojoin using SVSJOIN
*
*/
#include "atheme.h"
#include "uplink.h"
DECLARE_MODULE_V1
(
"contrib/ns_ajoin", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void ajoin_on_identify(user_t *u);
static void ns_cmd_ajoin(sourceinfo_t *si, int parc, char *parv[])
{
char buf[512];
char *chan;
metadata_t *md;
if (!parv[0])
{
command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN");
command_fail(si, fault_badparams, "Syntax: AJOIN <list|add|del> [#channel]");
return;
}
if (!strcasecmp(parv[0], "LIST"))
{
command_success_nodata(si, "\2AJOIN LIST\2:");
if ((md = metadata_find(si->smu, "private:autojoin")))
{
mowgli_strlcpy(buf, md->value, sizeof buf);
chan = strtok(buf, ",");
while (chan != NULL)
{
command_success_nodata(si, "%s", chan);
chan = strtok(NULL, ",");
}
}
command_success_nodata(si, "End of \2AJOIN LIST\2");
}
else if (!strcasecmp(parv[0], "ADD"))
{
if (!parv[1])
{
command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN");
command_fail(si, fault_badparams, "Syntax: AJOIN <list|add|del|clear> [#channel]");
return;
}
if ((md = metadata_find(si->smu, "private:autojoin")))
{
mowgli_strlcpy(buf, md->value, sizeof buf);
chan = strtok(buf, ",");
while (chan != NULL)
{
if (!strcasecmp(chan, parv[1]))
{
command_fail(si, fault_badparams, "%s is already on your AJOIN list.", parv[1]);
return;
}
chan = strtok(NULL, ",");
}
// Little arbitrary, but stop both overflow and RAM consumption going out of control
if (strlen(md->value) + strlen(parv[1]) > 400)
{
command_fail(si, fault_badparams, "Sorry, you have too many AJOIN entries set.");
return;
}
mowgli_strlcpy(buf, md->value, sizeof buf);
mowgli_strlcat(buf, ",", sizeof buf);
mowgli_strlcat(buf, parv[1], sizeof buf);
metadata_delete(si->smu, "private:autojoin");
metadata_add(si->smu, "private:autojoin", buf);
}
else
{
metadata_add(si->smu, "private:autojoin", parv[1]);
}
command_success_nodata(si, "%s added to AJOIN successfully.", parv[1]);
}
else if (!strcasecmp(parv[0], "CLEAR"))
{
metadata_delete(si->smu, "private:autojoin");
command_success_nodata(si, "AJOIN list cleared successfully.");
}
else if (!strcasecmp(parv[0], "DEL"))
{
if (!parv[1])
{
command_fail(si, fault_badparams, STR_INSUFFICIENT_PARAMS, "AJOIN");
command_fail(si, fault_badparams, "Syntax: AJOIN <list|add|del|clear> [#channel]");
return;
}
if (!(md = metadata_find(si->smu, "private:autojoin")))
{
command_fail(si, fault_badparams, "%s is not on your AJOIN list.", parv[1]);
return;
}
// Thanks to John Brooks for his help with this.
char *list = md->value;
char *remove1 = parv[1];
int listlen = 0;
int rmlen = 0;
int itempos = 0;
int i = 0, j = 0;
// This loop will find the item (if present), find the length of the item, and find the length of the entire string.
for (; list[i]; i++)
{
if (!rmlen)
{
// We have not found the string yet
if (tolower(list[i]) == tolower(remove1[j]))
{
if (j == 0)
{
// First character of a potential match; remember it's location
itempos = i;
}
j++;
if (!remove1[j])
{
// Found the entire string
rmlen = j;
}
}
else
j = 0;
}
}
if (remove1[j])
{
command_fail(si, fault_badparams, "%s is not on your AJOIN list.", parv[1]);
return;
}
listlen = i;
// listlen is the length of the list, rmlen is the length of the item to remove, itempos is the beginning of that item.
if (!list[itempos + rmlen])
{
// This item is the last item in the list, so we can simply truncate
if (itempos > 0)
{
itempos--;
list[itempos] = '\0';
}
else
metadata_delete(si->smu, "private:autojoin");
}
else
{
// There are items after this one, so we must copy memory
// Account for the comma following this item (if there is a space, account for that too, depends on how you format your list)
rmlen += 1;
memmove(list + itempos, list + itempos + rmlen, listlen - rmlen - itempos);
list[listlen - rmlen] = '\0';
}
command_success_nodata(si, "%s removed from AJOIN successfully.", parv[1]);
}
}
command_t ns_ajoin = { "AJOIN", "Manages automatic-join on identify.", AC_AUTHENTICATED, 2, ns_cmd_ajoin, { .path = "contrib/ajoin" } };
void _modinit(module_t *m)
{
hook_add_event("user_identify");
hook_add_user_identify(ajoin_on_identify);
service_named_bind_command("nickserv", &ns_ajoin);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_identify(ajoin_on_identify);
service_named_unbind_command("nickserv", &ns_ajoin);
}
static void ajoin_on_identify(user_t *u)
{
myuser_t *mu = u->myuser;
metadata_t *md;
char buf[512];
char *chan;
if (!(md = metadata_find(mu, "private:autojoin")))
return;
mowgli_strlcpy(buf, md->value, sizeof buf);
chan = strtok(buf, " ,");
while (chan != NULL)
{
if(ircd->type == PROTOCOL_SHADOWIRCD)
{
sts(":%s ENCAP * SVSJOIN %s %s", ME, CLIENT_NAME(u), chan);
}
else
{
sts(":%s SVSJOIN %s %s", CLIENT_NAME(nicksvs.me->me), CLIENT_NAME(u), chan);
}
chan = strtok(NULL, ",");
}
}

113
ns_cleannick.c Normal file
View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Clean obnoxious nicknames, such as LaMENiCK -> lamenick.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_cleannick", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
#define LAMENESS_WEIGHT 0.35f
/*
* Determine if a nickname is lame. Non-alphabetical characters
* are penalized twice, uppercase characters are penalized once.
*/
static bool is_nickname_lame(const char *nickname)
{
const char *p;
unsigned int capcount = 0;
float score;
return_val_if_fail(nickname != NULL, false);
for (p = nickname; *p != '\0'; p++)
{
if (IsUpper(*p))
capcount++;
#ifdef NOTYET
if (!IsAlpha(*p))
capcount += 2;
#endif
}
score = (float) capcount / (float) strlen(nickname);
slog(LG_DEBUG, "is_nickname_lame(%s): score %0.3f %d/%zu caps", nickname, score, capcount, strlen(nickname));
if (score > LAMENESS_WEIGHT)
return true;
return false;
}
/*
* Sanitize a nickname and then change it to the sanitized version forcefully.
*/
static void clean_nickname(user_t *u)
{
char nickbuf[NICKLEN];
char *p;
return_if_fail(u != NULL);
mowgli_strlcpy(nickbuf, u->nick, NICKLEN);
p = nickbuf;
while (*p++)
{
if (IsUpper(*p))
*p = ToLower(*p);
}
if (is_nickname_lame(nickbuf))
{
slog(LG_DEBUG, "clean_nickname(%s): cleaned nickname %s is still lame", u->nick, nickbuf);
return;
}
notice(nicksvs.nick, u->nick, "Your nick has been changed to \2%s\2 per %s nickname quality guidelines.",
nickbuf, me.netname);
fnc_sts(nicksvs.me->me, u, nickbuf, FNC_FORCE);
}
static void user_state_changed(hook_user_nick_t *data)
{
return_if_fail(data != NULL);
return_if_fail(data->u != NULL);
if (!is_internal_client(data->u) && is_nickname_lame(data->u->nick))
{
#ifdef TAUNT_LAME_USERS
if (data->oldnick != NULL && !is_nickname_lame(data->oldnick))
notice(nicksvs.nick, data->u->nick, "\2%s\2 was much less lame, \2%s\2.",
data->oldnick, data->u->nick);
#endif
clean_nickname(data->u);
}
}
void _modinit(module_t *m)
{
hook_add_event("user_add");
hook_add_user_add(user_state_changed);
hook_add_event("user_nickchange");
hook_add_user_nickchange(user_state_changed);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_add(user_state_changed);
hook_del_user_nickchange(user_state_changed);
}

91
ns_fenforce.c Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2005-2007 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the NickServ FENFORCE function.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_fenforce", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void ns_cmd_fenforce(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_fenforce = { "FENFORCE", "Enables or disables protection of another user's nicknames.", PRIV_USER_ADMIN, 2, ns_cmd_fenforce, { .path = "contrib/fenforce" } };
void _modinit(module_t *m)
{
MODULE_TRY_REQUEST_DEPENDENCY(m, "nickserv/enforce");
service_named_bind_command("nickserv", &ns_fenforce);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_fenforce);
}
static void ns_cmd_fenforce(sourceinfo_t *si, int parc, char *parv[])
{
char *setting;
myuser_t *mu;
if (parc < 2)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FENFORCE");
command_fail(si, fault_needmoreparams, _("Syntax: FENFORCE <account> ON|OFF"));
return;
}
mu = myuser_find_ext(parv[0]);
if (!mu)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), parv[0]);
return;
}
setting = parv[1];
if (strcasecmp(setting, "ON") == 0)
{
if (metadata_find(mu, "private:doenforce"))
{
command_fail(si, fault_nochange, _("The \2%s\2 flag is already set for account \2%s\2."), "ENFORCE", entity(mu)->name);
}
else
{
wallops("%s enabled ENFORCE on the account \2%s\2.", get_oper_name(si), entity(mu)->name);
logcommand(si, CMDLOG_ADMIN, "FENFORCE:ON: \2%s\2", entity(mu)->name);
metadata_add(mu, "private:doenforce", "1");
command_success_nodata(si, _("The \2%s\2 flag has been set for account \2%s\2."), "ENFORCE", entity(mu)->name);
}
}
else if (strcasecmp(setting, "OFF") == 0)
{
if (metadata_find(mu, "private:doenforce"))
{
wallops("%s disabled ENFORCE on the account \2%s\2.", get_oper_name(si), entity(mu)->name);
logcommand(si, CMDLOG_ADMIN, "FENFORCE:OFF: \2%s\2", entity(mu)->name);
metadata_delete(mu, "private:doenforce");
command_success_nodata(si, _("The \2%s\2 flag has been removed for account \2%s\2."), "ENFORCE", entity(mu)->name);
}
else
{
command_fail(si, fault_nochange, _("The \2%s\2 flag is not set for account \2%s\2."), "ENFORCE", entity(mu)->name);
}
}
else
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "FENFORCE");
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

150
ns_forbid.c Normal file
View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2005-2007 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the NickServ FORBID function.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_forbid", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
#define FORBID_EMAIL "noemail"
static void ns_cmd_forbid(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_forbid = { "FORBID", "Disallows use of a nickname.", PRIV_USER_ADMIN, 3, ns_cmd_forbid, { .path = "contrib/forbid" } };
void _modinit(module_t *m)
{
service_named_bind_command("nickserv", &ns_forbid);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_forbid);
}
static void make_forbid(sourceinfo_t *si, const char *account, const char *reason)
{
myuser_t *mu;
mynick_t *mn = NULL;
user_t *u;
if (!nicksvs.no_nick_ownership && IsDigit(*account))
{
command_fail(si, fault_badparams, "For security reasons, you can't forbid a UID.");
return;
}
if (strchr(account, ' ') || strchr(account, '\n') || strchr(account, '\r') || account[0] == '=' || account[0] == '#' || account[0] == '@' || account[0] == '+' || account[0] == '%' || account[0] == '!' || strchr(account, ','))
{
command_fail(si, fault_badparams, "The account name \2%s\2 is invalid.", account);
return;
}
/* make sure it isn't registered already */
if (nicksvs.no_nick_ownership ? myuser_find(account) != NULL : mynick_find(account) != NULL)
{
command_fail(si, fault_alreadyexists, "\2%s\2 is already registered.", account);
return;
}
mu = myuser_add(account, "*", FORBID_EMAIL, MU_CRYPTPASS | MU_ENFORCE | MU_HOLD | MU_NOBURSTLOGIN);
mu->registered = CURRTIME;
mu->lastlogin = CURRTIME;
metadata_add(mu, "private:freeze:freezer", get_oper_name(si));
metadata_add(mu, "private:freeze:reason", reason);
metadata_add(mu, "private:freeze:timestamp", number_to_string(CURRTIME));
if (!nicksvs.no_nick_ownership)
{
mn = mynick_add(mu, entity(mu)->name);
mn->registered = CURRTIME;
mn->lastseen = CURRTIME;
u = user_find_named(entity(mu)->name);
if (u != NULL)
{
notice(si->service->nick, u->nick,
_("The nick \2%s\2 is now forbidden."),
entity(mu)->name);
hook_call_nick_enforce((&(hook_nick_enforce_t){ .u = u, .mn = mn }));
}
}
logcommand(si, CMDLOG_ADMIN | CMDLOG_REGISTER, "FORBID:ON: \2%s\2 (reason: \2%s\2)", account, reason);
wallops("%s forbade the nickname \2%s\2 (%s).", get_oper_name(si), account, reason);
command_success_nodata(si, "\2%s\2 is now forbidden.", entity(mu)->name);
/* don't call hooks, hmm */
}
static void destroy_forbid(sourceinfo_t *si, const char *account)
{
myuser_t *mu;
metadata_t *md;
mu = myuser_find(account);
if (!mu)
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), account);
return;
}
md = metadata_find(mu, "private:freeze:freezer");
if (md == NULL || mu->registered != mu->lastlogin ||
MOWGLI_LIST_LENGTH(&mu->nicks) != 1 ||
strcmp(mu->email, FORBID_EMAIL))
{
command_fail(si, fault_nosuch_target, _("\2%s\2 is not a forbidden nickname."), account);
return;
}
logcommand(si, CMDLOG_ADMIN | CMDLOG_REGISTER, "FORBID:OFF: \2%s\2", entity(mu)->name);
wallops("%s unforbade the nickname \2%s\2.", get_oper_name(si), account);
command_success_nodata(si, "\2%s\2 is no longer forbidden.", entity(mu)->name);
/* no hooks here either, hmm */
object_unref(mu);
}
static void ns_cmd_forbid(sourceinfo_t *si, int parc, char *parv[])
{
const char *account;
const char *action;
const char *reason;
account = parv[0], action = parv[1], reason = parv[2];
if (!account || !action)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FORBID");
command_fail(si, fault_needmoreparams, "Syntax: FORBID <nickname> ON|OFF [reason]");
return;
}
if (!strcasecmp(action, "ON"))
{
if (!reason)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FORBID");
command_fail(si, fault_needmoreparams, _("Usage: FORBID <nickname> ON <reason>"));
return;
}
make_forbid(si, account, reason);
}
else if (!strcasecmp(action, "OFF"))
destroy_forbid(si, account);
else
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FORBID");
command_fail(si, fault_needmoreparams, _("Usage: FORBID <nickname> ON|OFF [reason]"));
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

126
ns_fregister.c Normal file
View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2005-2007 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the NickServ FREGISTER function.
*
* Remember to give the user:fregister priv to any soper you want
* to be able to use this command.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_fregister", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void ns_cmd_fregister(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_fregister = { "FREGISTER", "Registers a nickname on behalf of another user.", PRIV_USER_FREGISTER, 20, ns_cmd_fregister, { .path = "contrib/fregister" } };
void _modinit(module_t *m)
{
service_named_bind_command("nickserv", &ns_fregister);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_fregister);
}
static void ns_cmd_fregister(sourceinfo_t *si, int parc, char *parv[])
{
myuser_t *mu;
mynick_t *mn = NULL;
char *account;
char *pass;
char *email;
int i, uflags = 0;
hook_user_req_t req;
account = parv[0], pass = parv[1], email = parv[2];
if (!account || !pass || !email)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "FREGISTER");
command_fail(si, fault_needmoreparams, "Syntax: FREGISTER <account> <password> <email> [CRYPTPASS]");
return;
}
for (i = 3; i < parc; i++)
{
if (!strcasecmp(parv[i], "CRYPTPASS"))
uflags |= MU_CRYPTPASS;
else if (!strcasecmp(parv[i], "HIDEMAIL"))
uflags |= MU_HIDEMAIL;
else if (!strcasecmp(parv[i], "NOOP"))
uflags |= MU_NOOP;
else if (!strcasecmp(parv[i], "NEVEROP"))
uflags |= MU_NEVEROP;
}
if (!(uflags & MU_CRYPTPASS) && strlen(pass) > 32)
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "FREGISTER");
return;
}
if (!nicksvs.no_nick_ownership && IsDigit(*account))
{
command_fail(si, fault_badparams, "For security reasons, you can't register your UID.");
return;
}
if (strchr(account, ' ') || strchr(account, '\n') || strchr(account, '\r') || account[0] == '=' || account[0] == '#' || account[0] == '@' || account[0] == '+' || account[0] == '%' || account[0] == '!' || strchr(account, ','))
{
command_fail(si, fault_badparams, "The account name \2%s\2 is invalid.", account);
return;
}
if (!validemail(email))
{
command_fail(si, fault_badparams, "\2%s\2 is not a valid email address.", email);
return;
}
/* make sure it isn't registered already */
if (nicksvs.no_nick_ownership ? myuser_find(account) != NULL : mynick_find(account) != NULL)
{
command_fail(si, fault_alreadyexists, "\2%s\2 is already registered.", account);
return;
}
mu = myuser_add(account, pass, email, uflags | config_options.defuflags | MU_NOBURSTLOGIN);
mu->registered = CURRTIME;
mu->lastlogin = CURRTIME;
if (!nicksvs.no_nick_ownership)
{
mn = mynick_add(mu, entity(mu)->name);
mn->registered = CURRTIME;
mn->lastseen = CURRTIME;
}
logcommand(si, CMDLOG_REGISTER, "FREGISTER: \2%s\2 to \2%s\2", account, email);
if (is_soper(mu))
{
wallops("%s used FREGISTER on account \2%s\2 with services operator privileges.", get_oper_name(si), entity(mu)->name);
slog(LG_INFO, "SOPER: \2%s\2", entity(mu)->name);
}
command_success_nodata(si, "\2%s\2 is now registered to \2%s\2.", entity(mu)->name, mu->email);
hook_call_user_register(mu);
req.si = si;
req.mu = mu;
req.mn = mn;
hook_call_user_verify_register(&req);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

60
ns_generatehash.c Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2010 Atheme development group
* Rights to this code are as documented in doc/LICENSE.
*
* Generates a hash for use as a operserv "password".
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_generatehash", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme development group"
);
static void ns_cmd_generatehash(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_generatehash = { "GENERATEHASH", "Generates a hash for SOPER.",
AC_NONE, 1, ns_cmd_generatehash, { .path = "contrib/generatehash" } };
void _modinit(module_t *m)
{
service_named_bind_command("nickserv", &ns_generatehash);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_generatehash);
}
static void ns_cmd_generatehash(sourceinfo_t *si, int parc, char *parv[])
{
char *pass = parv[0];
char hash[PASSLEN];
if (parc < 1)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "GENERATEHASH");
command_fail(si, fault_needmoreparams, _("Syntax: GENERATEHASH <password>"));
return;
}
if (crypto_module_loaded)
{
mowgli_strlcpy(hash, crypt_string(pass, gen_salt()), PASSLEN);
command_success_string(si, hash, "Hash is: %s", hash);
}
else
command_success_nodata(si, "No crypto module loaded so could not hash anything.");
logcommand(si, CMDLOG_GET, "GENERATEHASH");
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

55
ns_generatepass.c Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2005 Greg Feigenson
* Rights to this code are as documented in doc/LICENSE.
*
* Generates a new password, either n digits long (w/ nickserv arg), or 7 digits
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_generatepass", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Epiphanic Networks <http://www.epiphanic.org>"
);
static void ns_cmd_generatepass(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_generatepass = { "GENERATEPASS", "Generates a random password.",
AC_NONE, 1, ns_cmd_generatepass, { .path = "contrib/generatepass" } };
void _modinit(module_t *m)
{
service_named_bind_command("nickserv", &ns_generatepass);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_generatepass);
}
static void ns_cmd_generatepass(sourceinfo_t *si, int parc, char *parv[])
{
int n = 0;
char *newpass;
if (parc >= 1)
n = atoi(parv[0]);
if (n <= 0 || n > 127)
n = 7;
newpass = random_string(n);
command_success_string(si, newpass, "Randomly generated password: %s", newpass);
free(newpass);
logcommand(si, CMDLOG_GET, "GENERATEPASS");
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

96
ns_guestnoreg.c Normal file
View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2010 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for blocking registrations of guest nicks.
* Particularly for use with webchat clients.
*
* To actually use this, add a something like the following to
* the nickserv {} block of your atheme.conf:
* guestnicks {
* "mib_";
* "WebUser";
* };
*
*/
#include "atheme.h"
#include "conf.h"
DECLARE_MODULE_V1
(
"contrib/ns_guestnoreg", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static mowgli_list_t guestnicks = { NULL, NULL, 0 };
static void guestnoreg_hook(hook_user_register_check_t *hdata)
{
mowgli_node_t *n;
return_if_fail(hdata != NULL);
return_if_fail(hdata->si != NULL);
MOWGLI_ITER_FOREACH(n, guestnicks.head)
{
char *nick = n->data;
int nicklen = strlen(nick);
if (!strncasecmp(hdata->account, nick, nicklen))
{
command_fail(hdata->si, fault_badparams, _("Registering of guest nicknames is disallowed."));
hdata->approved++;
}
}
}
static int guestnoreg_config_handler(mowgli_config_file_entry_t *ce)
{
mowgli_config_file_entry_t *cce;
MOWGLI_ITER_FOREACH(cce, ce->entries)
{
char *nick = sstrdup(cce->varname);
mowgli_node_add(nick, mowgli_node_create(), &guestnicks);
}
return 0;
}
static void guestnoreg_config_purge(void *unused)
{
mowgli_node_t *n, *tn;
MOWGLI_ITER_FOREACH_SAFE(n, tn, guestnicks.head)
{
char *nick = n->data;
free(nick);
mowgli_node_delete(n, &guestnicks);
mowgli_node_free(n);
}
}
void
_modinit(module_t *m)
{
hook_add_event("config_purge");
hook_add_config_purge(guestnoreg_config_purge);
hook_add_event("user_can_register");
hook_add_user_can_register(guestnoreg_hook);
add_conf_item("GUESTNICKS", &nicksvs.me->conf_table, guestnoreg_config_handler);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_user_can_register(guestnoreg_hook);
hook_del_config_purge(guestnoreg_config_purge);
del_conf_item("GUESTNICKS", &nicksvs.me->conf_table);
}

58
ns_listlogins.c Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2005-2007 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for the NickServ LISTLOGINS function.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/ns_listlogins", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void ns_cmd_listlogins(sourceinfo_t *si, int parc, char *parv[]);
command_t ns_listlogins = { "LISTLOGINS", N_("Lists details of clients authenticated as you."), AC_AUTHENTICATED, 1, ns_cmd_listlogins, { .path = "contrib/listlogins" } };
void _modinit(module_t *m)
{
service_named_bind_command("nickserv", &ns_listlogins);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("nickserv", &ns_listlogins);
}
static void ns_cmd_listlogins(sourceinfo_t *si, int parc, char *parv[])
{
user_t *u;
mowgli_node_t *n;
int matches = 0;
if (si->smu->flags & MU_WAITAUTH)
{
command_fail(si, fault_noprivs, _("You have to verify your email address before you can perform this operation."));
return;
}
command_success_nodata(si, "Clients identified to account \2%s\2", entity(si->smu)->name);
MOWGLI_ITER_FOREACH(n, si->smu->logins.head)
{
u = n->data;
command_success_nodata(si, "- %s!%s@%s (real host: %s)", u->nick, u->user, u->vhost, u->host);
matches++;
}
command_success_nodata(si, ngettext(N_("\2%d\2 client found"), N_("\2%d\2 clients found"), matches), matches);
logcommand(si, CMDLOG_GET, "LISTLOGINS: (\2%d\2 matches)", matches);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

89
ns_mxcheck.c Normal file
View File

@ -0,0 +1,89 @@
#include "atheme.h"
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
DECLARE_MODULE_V1
(
"contrib/ns_mxcheck", false, _modinit, _moddeinit,
"1.1",
"Jamie L. Penman-Smithson <jamie@slacked.org>"
);
static void check_registration(hook_user_register_check_t *hdata);
int count_mx (const char *host);
void _modinit(module_t *m)
{
hook_add_event("user_can_register");
hook_add_user_can_register(check_registration);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_can_register(check_registration);
}
static void check_registration(hook_user_register_check_t *hdata)
{
char buf[1024];
const char *user;
const char *domain;
int count;
if (hdata->approved)
return;
mowgli_strlcpy(buf, hdata->email, sizeof buf);
user = strtok(buf, "@");
domain = strtok(NULL, "@");
count = count_mx(domain);
if (count > 0)
{
/* there are MX records for this domain */
slog(LG_INFO, "REGISTER: mxcheck: %d MX records for %s", count, domain);
}
else
{
/* no MX records or error */
struct hostent *host;
/* attempt to resolve host (fallback to A) */
if((host = gethostbyname(domain)) == NULL)
{
slog(LG_INFO, "REGISTER: mxcheck: no A/MX records for %s - "
"REGISTER failed", domain);
command_fail(hdata->si, fault_noprivs, "Sorry, \2%s\2 does not exist, "
"I can't send mail there. Please check and try again.", domain);
hdata->approved = 1;
return;
}
}
}
int count_mx (const char *host)
{
u_char nsbuf[4096];
ns_msg amsg;
int l;
l = res_query (host, ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
if (l < 0)
{
return 0;
}
else
{
ns_initparse (nsbuf, l, &amsg);
l = ns_msg_count (amsg, ns_s_an);
}
return l;
}
#endif

152
ns_mxcheck_async.c Normal file
View File

@ -0,0 +1,152 @@
#include "atheme.h"
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <netdb.h>
DECLARE_MODULE_V1
(
"contrib/ns_mxcheck_async", false, _modinit, _moddeinit,
"1.1",
"Jamie L. Penman-Smithson <jamie@slacked.org>"
);
struct procdata
{
char name[NICKLEN];
char email[EMAILLEN];
};
#define MAX_CHILDPROCS 10
static unsigned int proccount;
static struct procdata procdata[MAX_CHILDPROCS];
static void childproc_cb(pid_t pid, int status, void *data);
static void check_registration(hook_user_register_check_t *hdata);
int count_mx (const char *host);
void _modinit(module_t *m)
{
hook_add_event("user_can_register");
hook_add_user_can_register(check_registration);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_user_can_register(check_registration);
childproc_delete_all(childproc_cb);
}
static void childproc_cb(pid_t pid, int status, void *data)
{
struct procdata *pd = data;
myuser_t *mu;
const char *domain;
return_if_fail(proccount > 0);
proccount--;
if (!WIFEXITED(status))
return;
mu = myuser_find(pd->name);
if (mu == NULL || strcmp(pd->email, mu->email))
return;
domain = strchr(pd->email, '@');
if (domain == NULL)
return;
domain++;
if (WEXITSTATUS(status) == 1)
{
slog(LG_INFO, "REGISTER: mxcheck: no A/MX records for %s - "
"REGISTER failed", domain);
myuser_notice(nicksvs.nick, mu, "Sorry, \2%s\2 does not exist, "
"I can't send mail there. Please check and try again.", domain);
object_unref(mu);
}
else if (WEXITSTATUS(status) == 0)
{
slog(LG_INFO, "REGISTER: mxcheck: valid MX records for %s", domain);
}
}
static void check_registration(hook_user_register_check_t *hdata)
{
char buf[1024];
const char *user;
const char *domain;
int count;
pid_t pid;
struct procdata *pd;
if (hdata->approved)
return;
if (proccount >= MAX_CHILDPROCS)
{
command_fail(hdata->si, fault_toomany, "Sorry, too many registrations in progress. Try again later.");
hdata->approved = 1;
return;
}
switch (pid = fork())
{
case 0: /* child */
connection_close_all_fds();
mowgli_strlcpy(buf, hdata->email, sizeof buf);
user = strtok(buf, "@");
domain = strtok(NULL, "@");
count = count_mx(domain);
if (count <= 0)
{
/* no MX records or error */
struct hostent *host;
/* attempt to resolve host (fallback to A) */
if((host = gethostbyname(domain)) == NULL)
_exit(1);
}
_exit(0);
break;
case -1: /* error */
slog(LG_ERROR, "fork() failed for check_registration(): %s",
strerror(errno));
command_fail(hdata->si, fault_toomany, "Sorry, too many registrations in progress. Try again later.");
hdata->approved = 1;
return;
default: /* parent */
pd = &procdata[proccount++];
mowgli_strlcpy(pd->name, hdata->account, sizeof pd->name);
mowgli_strlcpy(pd->email, hdata->email, sizeof pd->email);
childproc_add(pid, "ns_mxcheck_async", childproc_cb, pd);
return;
}
}
int count_mx (const char *host)
{
u_char nsbuf[4096];
ns_msg amsg;
int l;
l = res_query (host, ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
if (l < 0)
{
return 0;
}
else
{
ns_initparse (nsbuf, l, &amsg);
l = ns_msg_count (amsg, ns_s_an);
}
return l;
}
#endif

80
ns_regnotice.c Normal file
View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2010 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Sends a customized welcome message on nickname registration.
*/
#include "atheme.h"
#include "conf.h"
DECLARE_MODULE_V1
(
"contrib/ns_regnotice", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static mowgli_list_t regnotices = { NULL, NULL, 0 };
static void regnotice_hook(myuser_t *mu)
{
mowgli_node_t *n;
return_if_fail(mu != NULL);
MOWGLI_ITER_FOREACH(n, regnotices.head)
{
char *line = n->data;
myuser_notice(nicksvs.nick, mu, "%s", line);
}
}
static int regnotice_config_handler(mowgli_config_file_entry_t *ce)
{
mowgli_config_file_entry_t *cce;
MOWGLI_ITER_FOREACH(cce, ce->entries)
{
char *line = sstrdup(cce->varname);
mowgli_node_add(line, mowgli_node_create(), &regnotices);
}
return 0;
}
static void regnotice_config_purge(void *unused)
{
mowgli_node_t *n, *tn;
MOWGLI_ITER_FOREACH_SAFE(n, tn, regnotices.head)
{
char *line = n->data;
free(line);
mowgli_node_delete(n, &regnotices);
mowgli_node_free(n);
}
}
void
_modinit(module_t *m)
{
hook_add_event("config_purge");
hook_add_config_purge(regnotice_config_purge);
hook_add_event("user_register");
hook_add_user_register(regnotice_hook);
add_conf_item("REGNOTICE", &nicksvs.me->conf_table, regnotice_config_handler);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_user_register(regnotice_hook);
hook_del_config_purge(regnotice_config_purge);
del_conf_item("REGNOTICE", &nicksvs.me->conf_table);
}

66
ns_waitreg.c Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2010 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains code for delaying user registration
*/
#include "atheme.h"
#include "conf.h"
#include <limits.h>
DECLARE_MODULE_V1
(
"contrib/ns_waitreg", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
unsigned int waitreg_time = 0;
static void waitreg_hook(hook_user_register_check_t *hdata)
{
return_if_fail(hdata != NULL);
return_if_fail(hdata->si != NULL);
return_if_fail(hdata->password != NULL);
if (hdata->si->su == NULL)
return;
unsigned int nickage = CURRTIME - hdata->si->su->ts;
if (nickage < waitreg_time)
{
command_fail(hdata->si, fault_badparams, _("You can not register your nick so soon after connecting. Please wait a while and try again."));
hdata->approved++;
}
}
static void info_hook(sourceinfo_t *si)
{
return_if_fail(si != NULL);
command_success_nodata(si, "Time (in seconds) before users may register an account: %u", waitreg_time);
}
void
_modinit(module_t *m)
{
hook_add_event("user_can_register");
hook_add_user_can_register(waitreg_hook);
hook_add_event("operserv_info");
hook_add_operserv_info(info_hook);
add_uint_conf_item("WAITREG_TIME", &nicksvs.me->conf_table, 0, &waitreg_time, 0, INT_MAX, 0);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_user_can_register(waitreg_hook);
hook_del_operserv_info(info_hook);
del_conf_item("WAITREG_TIME", &nicksvs.me->conf_table);
}

144
on_db_save.c Normal file
View File

@ -0,0 +1,144 @@
#include "atheme.h"
#include "conf.h"
#include "datastream.h"
#ifndef _WIN32
DECLARE_MODULE_V1
(
"contrib/on_db_save", false, _modinit, _moddeinit,
"",
"Atheme Development Group <http://www.atheme.org>"
);
static char *command = NULL;
static void on_db_save(void *unused);
static struct update_command_state {
connection_t *out, *err;
pid_t pid;
int running;
} update_command_proc;
void _modinit(module_t *m)
{
hook_add_event("db_saved");
hook_add_db_saved(on_db_save);
add_dupstr_conf_item("db_update_command", &conf_gi_table, 0, &command, NULL);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_db_saved(on_db_save);
del_conf_item("db_update_command", &conf_gi_table);
}
static void update_command_finished(pid_t pid, int status, void *data)
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
slog(LG_ERROR, "ERROR: Database update command failed with error %d", WEXITSTATUS(status));
update_command_proc.running = 0;
}
static void update_command_recvq_handler(connection_t *cptr, int err)
{
char buf[BUFSIZE];
int count;
count = recvq_getline(cptr, buf, sizeof(buf) - 1);
if (count <= 0)
return;
if (buf[count-1] == '\n')
count--;
if (count == 0)
buf[count++] = ' ';
buf[count] = '\0';
if (err)
{
slog(LG_ERROR, "ERROR: database update command said: %s", buf);
}
else
slog(LG_DEBUG, "db update command stdout: %s", buf);
}
static void update_command_stdout_handler(connection_t *cptr)
{
update_command_recvq_handler(cptr, 0);
}
static void update_command_stderr_handler(connection_t *cptr)
{
update_command_recvq_handler(cptr, 1);
}
static void on_db_save(void *unused)
{
int stdout_pipes[2], stderr_pipes[2];
pid_t pid;
int errno1;
if (!command)
return;
if (update_command_proc.running)
{
slog(LG_ERROR, "ERROR: database update command is still running");
return;
}
if (pipe(stdout_pipes) == -1)
{
int err = errno;
slog(LG_ERROR, "ERROR: Couldn't create pipe for database update command: %s", strerror(err));
return;
}
if (pipe(stderr_pipes) == -1)
{
int err = errno;
slog(LG_ERROR, "ERROR: Couldn't create pipe for database update command: %s", strerror(err));
close(stdout_pipes[0]);
close(stdout_pipes[1]);
return;
}
pid = fork();
switch (pid)
{
case -1:
errno1 = errno;
slog(LG_ERROR, "Failed to fork for database update command: %s", strerror(errno1));
return;
case 0:
connection_close_all_fds();
close(stdout_pipes[0]);
close(stderr_pipes[0]);
dup2(stdout_pipes[1], 1);
dup2(stderr_pipes[1], 2);
close(stdout_pipes[1]);
close(stderr_pipes[1]);
execl("/bin/sh", "sh", "-c", command, NULL);
write(2, "Failed to exec /bin/sh\n", 23);
_exit(255);
return;
default:
close(stdout_pipes[1]);
close(stderr_pipes[1]);
update_command_proc.out = connection_add("update_command_stdout", stdout_pipes[0], 0, recvq_put, NULL);
update_command_proc.err = connection_add("update_command_stderr", stderr_pipes[0], 0, recvq_put, NULL);
update_command_proc.out->recvq_handler = update_command_stdout_handler;
update_command_proc.err->recvq_handler = update_command_stderr_handler;
update_command_proc.pid = pid;
update_command_proc.running = 1;
childproc_add(pid, "db_update", update_command_finished, NULL);
break;
}
}
#endif

172
os_akillnicklist.c Normal file
View File

@ -0,0 +1,172 @@
/*
* Copyright (c) 2010 William Pitcock, et al.
* The rights to this code are as documented under doc/LICENSE.
*
* Automatically AKILL a list of clients, given their operating parameters.
*
* Basically this builds a keyword patricia. O(NICKLEN) lookups at the price
* of a longer startup process.
*
* Configuration.
* ==============
*
* This module adds a new block to the config file:
*
* nicklists {
* all = "/home/nenolod/atheme-production.hg4576/etc/nicklists/azn.txt";
* nick = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-nicks.txt";
* user = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-users.txt";
* real = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-gecos.txt";
* };
*
* You can add multiple all, nick, user and real entries. The entries will be merged.
* I would also like to say: fuck you GNAA, you guys need to go play in fucking traffic.
* Thanks for reading my crappy docs, and have a nice day.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_akillnicklist", false, _modinit, _moddeinit,
"0.1",
"Atheme Development Group <http://www.atheme.org>"
);
static mowgli_patricia_t *akillalllist = NULL;
static mowgli_patricia_t *akillnicklist = NULL;
static mowgli_patricia_t *akilluserlist = NULL;
static mowgli_patricia_t *akillreallist = NULL;
static mowgli_list_t conft = { NULL, NULL, 0 };
static void
add_contents_of_file_to_list(const char *filename, mowgli_patricia_t *list)
{
char value[BUFSIZE];
FILE *f;
f = fopen(filename, "r");
if (!f)
return;
while (fgets(value, BUFSIZE, f) != NULL)
{
strip(value);
if (!*value)
continue;
mowgli_patricia_add(list, value, (void *) 0x1);
}
fclose(f);
}
static int
nicklist_config_handler_all(mowgli_config_file_entry_t *entry)
{
add_contents_of_file_to_list(entry->vardata, akillalllist);
return 0;
}
static int
nicklist_config_handler_nick(mowgli_config_file_entry_t *entry)
{
add_contents_of_file_to_list(entry->vardata, akillnicklist);
return 0;
}
static int
nicklist_config_handler_user(mowgli_config_file_entry_t *entry)
{
add_contents_of_file_to_list(entry->vardata, akilluserlist);
return 0;
}
static int
nicklist_config_handler_real(mowgli_config_file_entry_t *entry)
{
add_contents_of_file_to_list(entry->vardata, akillreallist);
return 0;
}
static void
aknl_nickhook(hook_user_nick_t *data)
{
user_t *u;
bool doit = false;
char *username;
return_if_fail(data != NULL);
return_if_fail(data->u != NULL);
u = data->u;
if (is_internal_client(u))
return;
if (is_autokline_exempt(u))
return;
username = u->user;
if (*username == '~')
username++;
if (mowgli_patricia_retrieve(akillalllist, u->nick) != NULL &&
mowgli_patricia_retrieve(akillalllist, username) != NULL &&
mowgli_patricia_retrieve(akillalllist, u->gecos) != NULL)
doit = true;
if (mowgli_patricia_retrieve(akillnicklist, u->nick) != NULL)
doit = true;
if (mowgli_patricia_retrieve(akilluserlist, username) != NULL)
doit = true;
if (mowgli_patricia_retrieve(akillreallist, u->gecos) != NULL)
doit = true;
if (!doit)
return;
slog(LG_INFO, "AKNL: k-lining \2%s\2!%s@%s [%s] due to appearing to be a possible spambot", u->nick, u->user, u->host, u->gecos);
kline_sts("*", "*", u->host, 86400, "Possible spambot");
}
void
_modinit(module_t *m)
{
add_subblock_top_conf("NICKLISTS", &conft);
add_conf_item("ALL", &conft, nicklist_config_handler_all);
add_conf_item("NICK", &conft, nicklist_config_handler_nick);
add_conf_item("USER", &conft, nicklist_config_handler_user);
add_conf_item("REAL", &conft, nicklist_config_handler_real);
akillalllist = mowgli_patricia_create(strcasecanon);
akillnicklist = mowgli_patricia_create(strcasecanon);
akilluserlist = mowgli_patricia_create(strcasecanon);
akillreallist = mowgli_patricia_create(strcasecanon);
hook_add_event("user_add");
hook_add_user_add(aknl_nickhook);
hook_add_event("user_nickchange");
hook_add_user_nickchange(aknl_nickhook);
}
void
_moddeinit(module_unload_intent_t intent)
{
hook_del_user_add(aknl_nickhook);
hook_del_user_nickchange(aknl_nickhook);
del_conf_item("ALL", &conft);
del_conf_item("NICK", &conft);
del_conf_item("USER", &conft);
del_conf_item("REAL", &conft);
del_top_conf("NICKLISTS");
}

239
os_defcon.c Normal file
View File

@ -0,0 +1,239 @@
/*
* Copyright (c) 2010 JD Horelick <jdhore1@gmail.com>
* Rights to this code are as defined in doc/LICENSE.
*
* DEFCON implementation.
*
* By default, any setting except 5 will expire after 15 minutes,
* to change this, add a defcon_timeout = X; option to the operserv{}
* block in your atheme.con. X = amount of time in minutes for a defcon
* setting to time out/expire.
*
*/
#include "atheme.h"
#define DEFCON_CMODE "R"
DECLARE_MODULE_V1
(
"contrib/os_defcon", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void os_cmd_defcon(sourceinfo_t *si, int parc, char *parv[]);
static void defcon_nouserreg(hook_user_register_check_t *hdata);
static void defcon_nochanreg(hook_channel_register_check_t *hdatac);
static void defcon_useradd(hook_user_nick_t *data);
static void defcon_timeoutfunc(void *dummy);
static int level = 5;
static unsigned int defcon_timeout = 900;
static mowgli_eventloop_timer_t *defcon_timer = NULL;
command_t os_defcon = { "DEFCON", N_("Implements Defense Condition lockdowns."), PRIV_ADMIN, 1, os_cmd_defcon, { .path = "contrib/defcon" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_defcon);
TAINT_ON("Using os_defcon", "Use of os_defcon is unsupported and not recommend. Use only at your own risk.");
/* Hooks for all the stuff defcon disables */
hook_add_event("user_can_register");
hook_add_user_can_register(defcon_nouserreg);
hook_add_event("channel_can_register");
hook_add_channel_can_register(defcon_nochanreg);
hook_add_event("user_add");
hook_add_user_add(defcon_useradd);
service_t *svs;
svs = service_find("operserv");
add_duration_conf_item("DEFCON_TIMEOUT", &svs->conf_table, 0, &defcon_timeout, "m", 900);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_defcon);
hook_del_user_can_register(defcon_nouserreg);
hook_del_channel_can_register(defcon_nochanreg);
hook_del_user_add(defcon_useradd);
service_t *svs;
svs = service_find("operserv");
del_conf_item("DEFCON_TIMEOUT", &svs->conf_table);
if (defcon_timer != NULL)
mowgli_timer_destroy(base_eventloop, defcon_timer);
}
static void defcon_nouserreg(hook_user_register_check_t *hdata)
{
return_if_fail(hdata != NULL);
return_if_fail(hdata->si != NULL);
if (level < 5)
{
command_fail(hdata->si, fault_badparams, _("Registrations are currently disabled on this network, please try again later."));
hdata->approved++;
}
}
static void defcon_nochanreg(hook_channel_register_check_t *hdatac)
{
return_if_fail(hdatac != NULL);
return_if_fail(hdatac->si != NULL);
if (level < 5)
{
command_fail(hdatac->si, fault_badparams, _("Registrations are currently disabled on this network, please try again later."));
hdatac->approved++;
}
}
static void defcon_useradd(hook_user_nick_t *data)
{
user_t *u = data->u;
if (!u)
return;
if (is_internal_client(u))
return;
if (level == 1)
{
slog(LG_INFO, "DEFCON:KLINE: %s!%s@%s", u->nick, u->user, u->host);
kline_sts("*", u->user, u->host, 900, "This network is currently not accepting connections, please try again later.");
}
}
static void defcon_svsignore(void)
{
svsignore_t *svsignore;
mowgli_node_t *n, *tn;
if (level <= 2)
{
MOWGLI_ITER_FOREACH(n, svs_ignore_list.head)
{
svsignore = (svsignore_t *)n->data;
if (!strcasecmp(svsignore->mask, "*@*"))
return;
}
slog(LG_INFO, "DEFCON:IGNORE:ADD");
svsignore = svsignore_add("*@*", "DEFCON Level 1 or 2 activated");
svsignore->setby = "DEFCON";
svsignore->settime = CURRTIME;
}
else if (level >= 3)
{
MOWGLI_ITER_FOREACH_SAFE(n, tn, svs_ignore_list.head)
{
svsignore = (svsignore_t *)n->data;
if (!strcasecmp(svsignore->mask,"*@*"))
{
slog(LG_INFO, "DEFCON:IGNORE:REMOVE");
svsignore_delete(svsignore);
}
}
}
}
static void defcon_forcechanmodes(void)
{
channel_t *chptr;
mowgli_patricia_iteration_state_t state;
service_t *svs;
char modesetbuf[256];
char modeunsetbuf[256];
svs = service_find("operserv");
if (level <= 3)
{
snprintf(modesetbuf, sizeof modesetbuf, "+%s", DEFCON_CMODE);
slog(LG_INFO, "DEFCON:MODE: %s", modesetbuf);
MOWGLI_PATRICIA_FOREACH(chptr, &state, chanlist)
{
channel_mode_va(svs->me, chptr, 1, modesetbuf);
}
}
else if (level >= 4)
{
snprintf(modeunsetbuf, sizeof modeunsetbuf, "-%s", DEFCON_CMODE);
slog(LG_INFO, "DEFCON:MODE: %s", modeunsetbuf);
MOWGLI_PATRICIA_FOREACH(chptr, &state, chanlist)
{
channel_mode_va(svs->me, chptr, 1, modeunsetbuf);
}
}
}
static void defcon_timeoutfunc(void *dummy)
{
service_t *svs;
char buf[BUFSIZE];
svs = service_find("operserv");
level = 5;
defcon_svsignore();
defcon_forcechanmodes();
slog(LG_INFO, "DEFCON:TIMEOUT");
snprintf(buf, sizeof buf, "The DEFCON level is now back to normal (\2%d\2). Sorry for any inconvenience this caused.", level);
notice_global_sts(svs->me, "*", buf);
}
static void os_cmd_defcon(sourceinfo_t *si, int parc, char *parv[])
{
char *defcon = parv[0];
char buf[BUFSIZE];
if (!defcon)
{
command_success_nodata(si, _("Defense condition is currently level \2%d\2."), level);
return;
}
level = atoi(defcon);
if ((level <= 0) || (level > 5))
{
command_fail(si, fault_badparams, _("Defcon level must be between 1 and 5"));
level = 5;
return;
}
/* Call the 2 functions that don't use hooks */
defcon_svsignore();
defcon_forcechanmodes();
if (level < 5)
{
snprintf(buf, sizeof buf, "The DEFCON level has been changed to \2%d\2. We apologize for any inconvenience.", level);
if (defcon_timer == NULL)
defcon_timer = mowgli_timer_add_once(base_eventloop, "defcon_timeout", defcon_timeoutfunc, NULL, defcon_timeout);
}
else
{
snprintf(buf, sizeof buf, "The DEFCON level is now back to normal (\2%d\2). Sorry for any inconvenience this caused.", level);
mowgli_timer_destroy(base_eventloop, defcon_timer);
defcon_timer = NULL;
}
notice_global_sts(si->service->me, "*", buf);
command_success_nodata(si, _("Defense condition set to level \2%d\2."), level);
wallops(_("\2%s\2 set Defense condition to level \2%d\2."), get_oper_name(si), level);
logcommand(si, CMDLOG_ADMIN, "DEFCON: \2%d\2", level);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

39
os_helpme.c Normal file
View File

@ -0,0 +1,39 @@
/* os_helpme.c - set user mode +h
* elly+atheme@leptoquark.net
*/
#include "atheme.h"
#include "uplink.h" /* sts() */
DECLARE_MODULE_V1
(
"contrib/os_helpme", false, _modinit, _moddeinit,
"os_helpme.c",
"elly+atheme@leptoquark.net"
);
static void os_cmd_helpme(sourceinfo_t *si, int parc, char *parv[]);
command_t os_helpme = { "HELPME", N_("Makes you into a network helper."),
PRIV_HELPER, 0, os_cmd_helpme, { .path = "contrib/helpme" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_helpme);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_helpme);
}
static void os_cmd_helpme(sourceinfo_t *si, int parc, char *parv[])
{
service_t *svs;
svs = service_find("operserv");
sts(":%s MODE %s :+h", svs->nick, si->su->nick);
command_success_nodata(si, _("You are now a network helper."));
}

254
os_joinmon.c Normal file
View File

@ -0,0 +1,254 @@
/*
* Copyright (c) 2005 William Pitcock <nenolod -at- nenolod.net>
* Rights to this code are as documented in doc/LICENSE.
*
* Facilitates watching what channels a user joins.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_joinmon", true, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.net>"
);
static void watch_user_joins(hook_channel_joinpart_t *hdata);
static void os_cmd_joinmon(sourceinfo_t *si, int parc, char *parv[]);
static void write_jmdb(database_handle_t *db);
static void db_h_jm(database_handle_t *db, const char *type);
command_t os_joinmon = { "JOINMON", N_("Monitors what channels a user is joining."), PRIV_USER_ADMIN, 3, os_cmd_joinmon, { .path = "contrib/joinmon" } };
struct joinmon_ {
char *user;
/* This module is Jamaican...mon. */
time_t mon_ts;
char *creator;
char *reason;
};
typedef struct joinmon_ joinmon_t;
mowgli_list_t os_monlist;
void _modinit(module_t *m)
{
if (!module_find_published("backend/opensex"))
{
slog(LG_INFO, "Module %s requires use of the OpenSEX database backend, refusing to load.", m->name);
m->mflags = MODTYPE_FAIL;
return;
}
hook_add_event("channel_join");
hook_add_channel_join(watch_user_joins);
hook_add_db_write(write_jmdb);
db_register_type_handler("JM", db_h_jm);
service_named_bind_command("operserv", &os_joinmon);
}
void _moddeinit(module_unload_intent_t intent)
{
hook_del_channel_join(watch_user_joins);
hook_del_db_write(write_jmdb);
db_unregister_type_handler("JM");
service_named_unbind_command("operserv", &os_joinmon);
}
static void write_jmdb(database_handle_t *db)
{
mowgli_node_t *n;
MOWGLI_ITER_FOREACH(n, os_monlist.head)
{
joinmon_t *l = n->data;
db_start_row(db, "JM");
db_write_word(db, l->user);
db_write_time(db, l->mon_ts);
db_write_word(db, l->creator);
db_write_str(db, l->reason);
db_commit_row(db);
}
}
static void db_h_jm(database_handle_t *db, const char *type)
{
const char *user = db_sread_word(db);
time_t mon_ts = db_sread_time(db);
const char *creator = db_sread_word(db);
const char *reason = db_sread_str(db);
joinmon_t *l = smalloc(sizeof(joinmon_t));
l->user = sstrdup(user);
l->mon_ts = mon_ts;
l->creator = sstrdup(creator);
l->reason = sstrdup(reason);
mowgli_node_add(l, mowgli_node_create(), &os_monlist);
}
static void watch_user_joins(hook_channel_joinpart_t *hdata)
{
mowgli_node_t *n;
chanuser_t *cu = hdata->cu;
joinmon_t *l;
if (cu == NULL)
return;
if (!(cu->user->server->flags & SF_EOB))
return;
MOWGLI_ITER_FOREACH(n, os_monlist.head)
{
l = n->data;
/* Use match so you can monitor patterns like SomeBot* or
* t???h?????1
*/
if (!match(l->user, cu->user->nick))
{
/* Use LG_INFO because there's really no better logtype and creating
* one just for this module (ie: having to put stuff in core) is
* kind of stupid. Give it it's own logtype if logtypes are ever
* addable by modules.
*/
slog(LG_INFO, "JOINMON: \2%s\2 who matches \2%s\2 has joined \2%s\2",
cu->user->nick, l->user, cu->chan->name);
return;
}
}
}
static void os_cmd_joinmon(sourceinfo_t *si, int parc, char *parv[])
{
char *action = parv[0];
char *pattern = parv[1];
char *reason = parv[2];
mowgli_node_t *n, *tn;
joinmon_t *l;
if (!action)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "JOINMON");
command_fail(si, fault_needmoreparams, _("Syntax: JOINMON ADD|DEL|LIST [parameters]"));
return;
}
if (!strcasecmp("ADD", action))
{
if (!pattern)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "JOINMON");
command_fail(si, fault_needmoreparams, _("Syntax: JOINMON ADD <pattern> [reason]"));
return;
}
if (si->smu == NULL)
{
command_fail(si, fault_noprivs, _("You are not logged in."));
return;
}
/* search for it */
MOWGLI_ITER_FOREACH(n, os_monlist.head)
{
l = n->data;
if (!irccasecmp(l->user, pattern))
{
command_success_nodata(si, _("Pattern \2%s\2 is already being monitored."), pattern);
return;
}
}
l = smalloc(sizeof(joinmon_t));
l->user = sstrdup(pattern);
l->mon_ts = CURRTIME;;
l->creator = sstrdup(get_source_name(si));
if (reason)
{
l->reason = sstrdup(reason);
logcommand(si, CMDLOG_ADMIN, "JOINMON:ADD: \2%s\2 (Reason: \2%s\2)", pattern, reason);
}
else
{
l->reason = _("None");
logcommand(si, CMDLOG_ADMIN, "JOINMON:ADD: \2%s\2", pattern);
}
n = mowgli_node_create();
mowgli_node_add(l, n, &os_monlist);
command_success_nodata(si, _("\2%s\2 is now being monitored."), pattern);
return;
}
else if (!strcasecmp("DEL", action))
{
if (!pattern)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "JOINMON");
command_fail(si, fault_needmoreparams, _("Syntax: JOINMON DEL <pattern>"));
return;
}
MOWGLI_ITER_FOREACH_SAFE(n, tn, os_monlist.head)
{
l = n->data;
if (!irccasecmp(l->user, pattern))
{
logcommand(si, CMDLOG_ADMIN, "JOINMON:DEL: \2%s\2", l->user);
mowgli_node_delete(n, &os_monlist);
free(l->user);
free(l->creator);
free(l->reason);
free(l);
return;
}
}
command_success_nodata(si, _("Pattern \2%s\2 not found in joinmon database."), pattern);
return;
}
else if (!strcasecmp("LIST", action))
{
char buf[BUFSIZE];
struct tm tm;
MOWGLI_ITER_FOREACH(n, os_monlist.head)
{
l = n->data;
tm = *localtime(&l->mon_ts);
strftime(buf, BUFSIZE, TIME_FORMAT, &tm);
command_success_nodata(si, "Pattern: \2%s\2, Reason: \2%s\2 (%s - %s)",
l->user, l->reason, l->creator, buf);
}
command_success_nodata(si, "End of list.");
logcommand(si, CMDLOG_GET, "JOINMON:LIST");
return;
}
else
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "JOINMON");
return;
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

56
os_kill.c Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2005 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* Kill users through services, requested by christel@freenode.
*
* This differs from the ircd /kill command in that it does not show to
* normal users who issued the kill, although the reason will usually be
* shown. This is useful in cases where a kline would normally be used,
* but would not remove the user, but the user cannot (fully) reconnect.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_kill", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void os_cmd_kill(sourceinfo_t *si, int parc, char *parv[]);
command_t os_kill = { "KILL", "Kill a user with Services.", PRIV_OMODE, 2, os_cmd_kill, { .path = "contrib/kill" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_kill);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_kill);
}
static void os_cmd_kill(sourceinfo_t *si, int parc, char *parv[])
{
user_t *target;
if(!parv[0] || !parv[1])
{
command_fail(si, fault_badparams, "Usage: \2KILL\2 <target> <reason>");
return;
}
if(!(target = user_find_named(parv[0])))
{
command_fail(si, fault_nosuch_target, "\2%s\2 is not on the network", parv[0]);
return;
}
logcommand(si, CMDLOG_ADMIN, "KILL: \2%s\2 (reason: \2%s\2)", target->nick, parv[1]);
command_success_nodata(si, "\2%s\2 has been killed.", target->nick);
kill_user(si->service->me, target, "Requested: %s", parv[1]);
}

215
os_klinechan.c Normal file
View File

@ -0,0 +1,215 @@
/*
* Copyright (c) 2005-2007 Atheme Development Group
* Rights to this code are as documented in doc/LICENSE.
*
* Autokline channels.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_klinechan", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Jilles Tjoelker <http://www.stack.nl/~jilles/irc/>"
);
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];
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"))
{
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)",
cu->user->host, 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)",
cu->user->host, cu->user->nick,
cu->user->user, cu->user->host,
cu->chan->name);
kline_sts("*", "*", cu->user->host, 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> <ON|OFF> [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 <reason>");
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> <ON|OFF> [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);
}

72
os_modeall.c Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2005-2006 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* This file contains functionality which implements the OService MODEALL
* command.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_modeall", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void os_cmd_modeall(sourceinfo_t *si, int parc, char *parv[]);
command_t os_modeall = { "MODEALL", N_("Changes modes on all channels."), PRIV_OMODE, 2, os_cmd_modeall, { .path = "contrib/os_modeall" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_modeall);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_modeall);
}
static void set_channel_mode(service_t *s, channel_t *c, int modeparc, char *modeparv[])
{
channel_mode(s->me, c, modeparc, modeparv);
}
static void os_cmd_modeall(sourceinfo_t *si, int parc, char *parv[])
{
char *mode = parv[0];
channel_t *c;
int modeparc;
char *modeparv[256];
mowgli_patricia_iteration_state_t state;
int count = 0;
if (!mode)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "MODEALL");
command_fail(si, fault_needmoreparams, _("Syntax: MODEALL <parameters>"));
return;
}
modeparc = sjtoken(mode, ' ', modeparv);
MOWGLI_PATRICIA_FOREACH(c, &state, chanlist)
{
set_channel_mode(si->service, c, modeparc, modeparv);
count++;
}
command_success_nodata(si, _("Set modes \2%s\2 on \2%d\2 channels."), mode, count);
wallops("\2%s\2 is using MODEALL (set: \2%s\2)",
get_oper_name(si), mode);
logcommand(si, CMDLOG_ADMIN, "MODEALL: \2%s\2", mode);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

156
os_pingspam.c Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2005 William Pitcock, et al.
* Rights to this code are as documented in doc/LICENSE.
*
* ping spammer thingy
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_pingspam", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
char *notices[] =
{
"Scanning for proxies.",
"Killing off bottlers.",
"LOL ok so like we are teh SKANZ0RZING j00 becuz well like OMG deze bots r h3r3 an liek they are FL00DING!!#@! ignore plz",
"gaben",
"Please ignore this notice.",
"Scanning for warez.",
"All your pr0n are belong to us!",
"Move over! This is the police!",
"This notice brought to you by Burma-Shave.",
"They're coming...",
":)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(::)(:",
"lolz!",
"<Hikaru> your a pagan",
"* Ads needs to shower soon",
"<Hik`Coding> Don't make me get Yakuza on you",
"beu fails it",
"BAN KAI~!$"
};
char *phrases[] =
{
"",
" please-ignore",
" proxy scan",
" ignore me",
" <3 neostats",
};
void pingspam(user_t *u);
static void user_add_hook(hook_user_nick_t *data);
static void os_cmd_pingspam(sourceinfo_t *si, int parc, char *parv[]);
static void os_cmd_autopingspam(sourceinfo_t *si, int parc, char *parv[]);
command_t os_pingspam = { "PINGSPAM", "Spam a user with pings from every service, plus some bonus notices.", PRIV_OMODE, 1, os_cmd_pingspam, { .path = "contrib/pingspam" } };
command_t os_autopingspam = { "AUTOPINGSPAM", "Spam connecting users with pings from every service, plus some bonus notices (setting).", PRIV_ADMIN, 1, os_cmd_autopingspam, { .path = "contrib/autopingspam" } };
int spamming;
void _modinit(module_t *m)
{
spamming = 0;
service_named_bind_command("operserv", &os_pingspam);
service_named_bind_command("operserv", &os_autopingspam);
hook_add_event("user_add");
hook_add_user_add(user_add_hook);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_pingspam);
service_named_unbind_command("operserv", &os_autopingspam);
hook_del_user_add(user_add_hook);
}
static void user_add_hook(hook_user_nick_t *data)
{
user_t *u;
u = data->u;
if (u == NULL)
return;
if (is_internal_client(u))
return;
if (spamming)
pingspam(u);
}
static void os_cmd_pingspam(sourceinfo_t *si, int parc, char *parv[])
{
char *target = parv[0];
user_t *u;
if(!target)
{
command_fail(si, fault_badparams, "Usage: \2PINGSPAM\2 <target>");
return;
}
if(!(u = user_find_named(target)))
{
command_fail(si, fault_nosuch_target, "\2%s\2 is not on the network", target);
return;
}
pingspam(u);
command_success_nodata(si, "\2%s\2 has been pwned.", target);
logcommand(si, CMDLOG_ADMIN, "PINGSPAM: \2%s\2", target);
}
static void os_cmd_autopingspam(sourceinfo_t *si, int parc, char *parv[])
{
char *mode = parv[0];
if(!mode)
{
command_success_nodata(si, "Auto-pingspam is currently \2%s\2", spamming ? "ON" : "OFF");
return;
}
if(strcasecmp(mode, "on") == 0 || atoi(mode))
{
spamming = 1;
command_success_nodata(si, "Auto-pingspam is now \2ON\2");
}else{
spamming = 0;
command_success_nodata(si, "Auto-pingspam is now \2OFF\2");
}
}
void pingspam(user_t *u)
{
user_t *sptr;
mowgli_node_t *n;
int i;
service_t *svs;
if((svs = service_find("global")) != NULL)
for(i = 0;i < 6;i++)
notice(svs->me->nick, u->nick, "%s", notices[rand() % sizeof(notices) / sizeof(char*)]);
MOWGLI_ITER_FOREACH(n, me.me->userlist.head)
{
sptr = n->data;
msg(sptr->nick, u->nick, "\001PING %ld%s\001",
time(NULL),
phrases[rand() % sizeof(phrases) / sizeof(char*)]
);
}
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

101
os_procwatch.c Normal file
View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2009 Jilles Tjoelker, et al
* Rights to this code are as documented in doc/LICENSE.
*
* Monitors exit of given processes, using kqueue.
* The kqueue is added to the main poll loop.
*/
#include "atheme.h"
#include <sys/event.h>
DECLARE_MODULE_V1
(
"contrib/os_procwatch", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void procwatch_readhandler(connection_t *cptr);
static void os_cmd_procwatch(sourceinfo_t *si, int parc, char *parv[]);
command_t os_procwatch = { "PROCWATCH", "Notifies snoop channel on process exit.",
PRIV_ADMIN, 1, os_cmd_procwatch, { .path = "contrib/procwatch" } };
static connection_t *kq_conn;
void _modinit(module_t *m)
{
int kq;
kq = kqueue();
if (kq == -1)
{
m->mflags = MODTYPE_FAIL;
return;
}
kq_conn = connection_add("procwatch kqueue", kq, 0, procwatch_readhandler, NULL);
service_named_bind_command("operserv", &os_procwatch);
}
void _moddeinit(module_unload_intent_t intent)
{
if (kq_conn != NULL)
connection_close_soon(kq_conn);
service_named_unbind_command("operserv", &os_procwatch);
}
static void procwatch_readhandler(connection_t *cptr)
{
struct kevent ev;
if (cptr != kq_conn)
{
slog(LG_INFO, "procwatch_readhandler(): called with unexpected fd %d", cptr->fd);
return;
}
while (kevent(cptr->fd, NULL, 0, &ev, 1, &(const struct timespec){ 0, 0 }) > 0)
{
slog(LG_INFO, "PROCWATCH: %ld exited with status %x",
(long)ev.ident, (unsigned)ev.data);
}
}
static void os_cmd_procwatch(sourceinfo_t *si, int parc, char *parv[])
{
long v;
char *end;
struct kevent ev;
if (parc == 0)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "PROCWATCH");
command_fail(si, fault_needmoreparams, _("Syntax: PROCWATCH <pid>"));
return;
}
errno = 0;
v = strtol(parv[0], &end, 10);
if (errno != 0 || *end != '\0' || v < 0 || (pid_t)v != v)
{
command_fail(si, fault_needmoreparams, STR_INVALID_PARAMS, "PROCWATCH");
command_fail(si, fault_needmoreparams, _("Syntax: PROCWATCH <pid>"));
return;
}
EV_SET(&ev, v, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_EXIT, 0, NULL);
if (kevent(kq_conn->fd, &ev, 1, NULL, 0, NULL) == -1)
{
command_fail(si, fault_toomany, _("Failed to add pid %ld"), v);
return;
}
command_success_nodata(si, "Added pid %ld to list.", v);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

79
os_resolve.c Normal file
View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Does an A record lookup.
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_resolve", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void os_cmd_resolve(sourceinfo_t *si, int parc, char *parv[]);
command_t cmd_resolve = { "RESOLVE", N_("Perform DNS lookup on hostname"), PRIV_ADMIN, 1, os_cmd_resolve, { .path = "contrib/os_resolve" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &cmd_resolve);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &cmd_resolve);
}
typedef struct {
dns_query_t dns_query;
sourceinfo_t *si;
} resolve_req_t;
mowgli_heap_t *request_heap = NULL;
static void resolve_cb(void *vptr, dns_reply_t *reply)
{
resolve_req_t *req = vptr;
char buf[BUFSIZE];
return_if_fail(vptr != NULL);
return_if_fail(reply != NULL);
if (reply->addr.saddr.sa.sa_family != AF_INET)
return;
inet_ntop(reply->addr.saddr.sa.sa_family, &reply->addr.saddr.sin.sin_addr, buf, reply->addr.saddr_len);
command_success_nodata(req->si, "Result is %s", buf);
mowgli_heap_free(request_heap, req);
object_unref(req->si);
}
static void os_cmd_resolve(sourceinfo_t *si, int parc, char *parv[])
{
resolve_req_t *req;
if (request_heap == NULL)
request_heap = mowgli_heap_create(sizeof(resolve_req_t), 32, BH_LAZY);
if (!parv[0])
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "RESOLVE");
return;
}
req = mowgli_heap_alloc(request_heap);
req->si = si;
req->dns_query.ptr = req;
req->dns_query.callback = resolve_cb;
gethost_byname_type(parv[0], &req->dns_query, T_A);
object_ref(req->si);
}

180
os_savechanmodes.c Normal file
View File

@ -0,0 +1,180 @@
/*
* Copyright (c) 2008 Jilles Tjoelker
* Rights to this code are as documented in doc/LICENSE.
*
* Dump/restore channel modes
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_savechanmodes", false, _modinit, _moddeinit,
"$Revision: 7785 $",
"Jilles Tjoelker <jilles -at- stack.nl>"
);
static void os_cmd_savechanmodes(sourceinfo_t *si, int parc, char *parv[]);
static void os_cmd_loadchanmodes(sourceinfo_t *si, int parc, char *parv[]);
command_t os_savechanmodes = { "SAVECHANMODES", "Dumps channel modes to a file.",
PRIV_ADMIN, 1, os_cmd_savechanmodes, { .path = "contrib/savechanmodes" } };
command_t os_loadchanmodes = { "LOADCHANMODES", "Restores channel modes from a file.",
PRIV_ADMIN, 1, os_cmd_loadchanmodes, { .path = "contrib/loadchanmodes" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_savechanmodes);
service_named_bind_command("operserv", &os_loadchanmodes);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_savechanmodes);
service_named_unbind_command("operserv", &os_loadchanmodes);
}
static void os_cmd_savechanmodes(sourceinfo_t *si, int parc, char *parv[])
{
FILE *out;
mowgli_patricia_iteration_state_t state;
channel_t *c;
mowgli_node_t *n;
chanban_t *cb;
if (!(out = fopen(DATADIR "/chanmodes.txt", "w")))
{
command_fail(si, fault_nosuch_source, "Cannot open %s: %s",
DATADIR "/chanmodes.txt", strerror(errno));
return;
}
logcommand(si, CMDLOG_ADMIN, "SAVECHANMODES");
wallops("\2%s\2 is dumping channel modes", get_oper_name(si));
MOWGLI_PATRICIA_FOREACH(c, &state, chanlist)
{
fprintf(out, "chan %s %s\n", c->name, channel_modes(c, true));
if (c->topic)
fprintf(out, "topic %s %lu %s\n", c->topic_setter,
(unsigned long)c->topicts,
c->topic);
MOWGLI_ITER_FOREACH(n, c->bans.head)
{
cb = n->data;
fprintf(out, "ban %c %s\n", cb->type, cb->mask);
}
}
fclose(out);
command_success_nodata(si, "Channel modes saved to %s.",
DATADIR "/chanmodes.txt");
}
static channel_t *restore_channel(char *name, char *modes)
{
channel_t *c;
int modeparc;
char *modeparv[256];
service_t *svs;
svs = service_find("operserv");
if (svs == NULL)
svs = chansvs.me;
join(name, chansvs.nick);
c = channel_find(name);
if (c != NULL)
{
modeparc = sjtoken(modes, ' ', modeparv);
channel_mode(svs->me, c, modeparc, modeparv);
}
return c;
}
static void os_cmd_loadchanmodes(sourceinfo_t *si, int parc, char *parv[])
{
FILE *in;
char *item, buf[2048];
char *name, *modes, *setter, *tsstr, *topic, *type, *mask;
time_t ts, prevtopicts;
channel_t *c;
int line;
if (!(in = fopen(DATADIR "/chanmodes.txt", "r")))
{
command_fail(si, fault_nosuch_source, "Cannot open %s: %s",
DATADIR "/chanmodes.txt", strerror(errno));
return;
}
logcommand(si, CMDLOG_ADMIN, "LOADCHANMODES");
wallops("\2%s\2 is restoring channel modes", get_oper_name(si));
line = 0;
c = NULL;
while (fgets(buf, sizeof buf, in))
{
line++;
item = strtok(buf, " ");
strip(item);
if (item == NULL || *item == '#')
continue;
if (!strcmp(item, "chan"))
{
name = strtok(NULL, " ");
modes = strtok(NULL, "\n");
if (name == NULL || modes == NULL)
continue;
c = restore_channel(name, modes);
}
else if (!strcmp(item, "topic"))
{
if (c == NULL)
continue;
setter = strtok(NULL, " ");
tsstr = strtok(NULL, " ");
topic = strtok(NULL, "\n");
if (setter == NULL || tsstr == NULL || topic == NULL)
continue;
if (c->topic != NULL)
continue;
prevtopicts = c->topicts;
ts = strtoul(tsstr, NULL, 10);
handle_topic(c, setter, ts, topic);
topic_sts(c, chansvs.me->me, setter, ts, prevtopicts, topic);
}
else if (!strcmp(item, "ban"))
{
if (c == NULL)
continue;
type = strtok(NULL, " ");
mask = strtok(NULL, "\n");
if (type == NULL || mask == NULL)
continue;
modestack_mode_param(chansvs.nick, c, MTYPE_ADD, type[0], mask);
chanban_add(c, mask, type[0]);
}
}
fclose(in);
command_success_nodata(si, "Channel modes restored from %s.",
DATADIR "/chanmodes.txt");
command_success_nodata(si, "Remember to restart services to make %s leave channels it should not be in.",
chansvs.nick);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

54
os_tabletest.c Normal file
View File

@ -0,0 +1,54 @@
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_tabletest", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
static void os_cmd_tabletest(sourceinfo_t *si, int parc, char *parv[]);
command_t os_tabletest = { "TABLETEST", "Table test.", AC_NONE, 0, os_cmd_tabletest, { .path = "" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_tabletest);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_tabletest);
}
static void os_cmd_tabletest(sourceinfo_t *si, int parc, char *parv[])
{
table_t *t = table_new("Table \2test\2");
table_row_t *r = table_row_new(t);
table_cell_associate(r, "foo", "bar");
table_cell_associate(r, "F", "-");
table_cell_associate(r, "baz", "splork");
r = table_row_new(t);
table_cell_associate(r, "foo", "1");
table_cell_associate(r, "F", "+");
table_cell_associate(r, "baz", "2");
r = table_row_new(t);
table_cell_associate(r, "foo", "beagle4");
table_cell_associate(r, "F", "+");
table_cell_associate(r, "baz", "boo");
command_success_table(si, t);
object_unref(t);
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

135
os_testcmd.c Normal file
View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2006 Jilles Tjoelker, et al
* Rights to this code are as documented in doc/LICENSE.
*
* Calls a command without a user_t.
*
*/
#include "atheme.h"
DECLARE_MODULE_V1
(
"contrib/os_testcmd", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
struct testcmddata
{
sourceinfo_t *prevsi;
bool got_result;
};
static void os_cmd_testcmd(sourceinfo_t *si, int parc, char *parv[]);
static void testcmd_command_fail(sourceinfo_t *si, faultcode_t code, const char *message);
static void testcmd_command_success_nodata(sourceinfo_t *si, const char *message);
static void testcmd_command_success_string(sourceinfo_t *si, const char *result, const char *message);
command_t os_testcmd = { "TESTCMD", "Executes a command without a user_t.",
AC_NONE, 3, os_cmd_testcmd, { .path = "contrib/testcmd" } };
struct sourceinfo_vtable testcmd_vtable = {
.description = "testcmd",
.cmd_fail = testcmd_command_fail,
.cmd_success_nodata = testcmd_command_success_nodata,
.cmd_success_string = testcmd_command_success_string
};
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_testcmd);
}
void _moddeinit(module_unload_intent_t intent)
{
service_named_unbind_command("operserv", &os_testcmd);
}
static void testcmd_command_fail(sourceinfo_t *si, faultcode_t code, const char *message)
{
struct testcmddata *udata = si->callerdata;
command_success_nodata(udata->prevsi, "Command failed with fault %d, \"%s\"", code, message);
udata->got_result = true;
}
static void testcmd_command_success_nodata(sourceinfo_t *si, const char *message)
{
struct testcmddata *udata = si->callerdata;
if (udata->got_result)
command_success_nodata(udata->prevsi, "More comment \"%s\"", message);
else
command_success_nodata(udata->prevsi, "Command succeeded with no data, \"%s\"", message);
udata->got_result = true;
}
static void testcmd_command_success_string(sourceinfo_t *si, const char *result, const char *message)
{
struct testcmddata *udata = si->callerdata;
command_success_nodata(udata->prevsi, "Command succeeded with string \"%s\", \"%s\"", result, message);
udata->got_result = true;
}
static void os_cmd_testcmd(sourceinfo_t *si, int parc, char *parv[])
{
service_t *svs;
command_t *cmd;
sourceinfo_t newsi;
struct testcmddata udata;
int newparc;
char *newparv[256];
if (parc < 2)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "TESTCMD");
command_fail(si, fault_needmoreparams, "Syntax: TESTCMD <service> <command> [arguments]");
return;
}
svs = service_find_nick(parv[0]);
if (svs == NULL)
{
command_fail(si, fault_nosuch_target, "No such service \2%s\2", parv[0]);
return;
}
if (svs->commands == NULL)
{
command_fail(si, fault_noprivs, "Service \2%s\2 has no commands", svs->nick);
return;
}
cmd = command_find(svs->commands, parv[1]);
if (cmd == NULL)
{
command_fail(si, fault_nosuch_key, "No such command \2%s\2 in service \2%s\2", parv[1], svs->nick);
return;
}
udata.prevsi = si;
udata.got_result = false;
memset(newparv, '\0', sizeof newparv);
if (parc >= 3)
newparc = sjtoken(parv[2], ';', newparv);
else
newparc = 0;
memset(&newsi, '\0', sizeof newsi);
newsi.smu = si->smu;
if (si->su != NULL)
newsi.sourcedesc = si->su->ip != NULL ? si->su->ip : si->su->host;
else
newsi.sourcedesc = si->sourcedesc;
newsi.service = svs;
newsi.v = &testcmd_vtable;
newsi.callerdata = &udata;
command_exec(svs, &newsi, cmd, newparc, newparv);
if (!udata.got_result)
command_success_nodata(si, "Command returned without giving a result");
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

138
os_testproc.c Normal file
View File

@ -0,0 +1,138 @@
/*
* Copyright (c) 2009 Jilles Tjoelker, et al
* Rights to this code are as documented in doc/LICENSE.
*
* Reads data from a child process via a pipe.
*/
#include "atheme.h"
#include "datastream.h"
#ifndef _WIN32
DECLARE_MODULE_V1
(
"contrib/os_testproc", false, _modinit, _moddeinit,
PACKAGE_STRING,
"Atheme Development Group <http://www.atheme.org>"
);
struct testprocdata
{
char dest[NICKLEN];
connection_t *pip;
};
static struct testprocdata procdata;
static void os_cmd_testproc(sourceinfo_t *si, int parc, char *parv[]);
command_t os_testproc = { "TESTPROC", "Does something with child processes.",
AC_NONE, 0, os_cmd_testproc, { .path = "contrib/testproc" } };
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_testproc);
}
void _moddeinit(module_unload_intent_t intent)
{
if (procdata.pip != NULL)
connection_close_soon(procdata.pip);
service_named_unbind_command("operserv", &os_testproc);
}
static void testproc_recvqhandler(connection_t *cptr)
{
char buf[BUFSIZE];
int count;
user_t *u;
if (cptr != procdata.pip)
{
slog(LG_INFO, "testproc_recvqhandler(): called with unexpected fd %d", cptr->fd);
return;
}
count = recvq_getline(cptr, buf, sizeof buf - 1);
if (count <= 0)
return;
if (buf[count - 1] == '\n')
count--;
if (count > 0 && buf[count - 1] == '\r')
count--;
if (count == 0)
buf[count++] = ' ';
buf[count] = '\0';
u = user_find(procdata.dest);
if (u != NULL)
notice(service_find("operserv")->me->nick, u->nick, "%s", buf);
}
static void testproc_closehandler(connection_t *cptr)
{
if (cptr != procdata.pip)
{
slog(LG_INFO, "testproc_closehandler(): called with unexpected fd %d", cptr->fd);
return;
}
slog(LG_DEBUG, "testproc_closehandler(): fd %d closed", cptr->fd);
procdata.pip = NULL;
}
static void os_cmd_testproc(sourceinfo_t *si, int parc, char *parv[])
{
int pipes[2];
if (si->su == NULL)
{
command_fail(si, fault_noprivs, _("\2%s\2 can only be executed via IRC."), "TESTPROC");
return;
}
if (procdata.pip != NULL)
{
command_fail(si, fault_toomany, "Another TESTPROC is still in progress");
return;
}
if (pipe(pipes) == -1)
{
command_fail(si, fault_toomany, "Failed to create pipe");
return;
}
switch (fork())
{
case -1:
close(pipes[0]);
close(pipes[1]);
command_fail(si, fault_toomany, "Failed to fork");
return;
case 0:
connection_close_all_fds();
close(pipes[0]);
dup2(pipes[1], 1);
dup2(pipes[1], 2);
close(pipes[1]);
execl("/bin/sh", "sh", "-c", "echo hi; sleep 1; echo hi 2; sleep 0.5; echo hi 3; sleep 4; echo hi 4", (char *)NULL);
(void)write(2, "Failed to exec /bin/sh\n", 23);
_exit(255);
break;
default:
close(pipes[1]);
procdata.pip = connection_add("testproc pipe", pipes[0], 0, recvq_put, NULL);
procdata.pip->recvq_handler = testproc_recvqhandler;
procdata.pip->close_handler = testproc_closehandler;
mowgli_strlcpy(procdata.dest, CLIENT_NAME(si->su), sizeof procdata.dest);
break;
}
}
#endif
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

928
os_trace.c Normal file
View File

@ -0,0 +1,928 @@
/*
* Copyright (c) 2010 William Pitcock <nenolod@atheme.org>
* Rights to this code are as documented in doc/LICENSE.
*
* Looks for users and performs actions on them.
*/
#include "atheme.h"
#include <limits.h>
DECLARE_MODULE_V1
(
"contrib/os_trace", false, _modinit, _moddeinit,
"Copyright (c) 2010 William Pitcock <nenolod@atheme.org>",
"Atheme Development Group <http://www.atheme.org>"
);
static char *reason_extract(char **args);
static void os_cmd_trace(sourceinfo_t *si, int parc, char *parv[]);
command_t os_trace = { "TRACE", N_("Looks for users and performs actions on them."), PRIV_USER_AUSPEX, 2, os_cmd_trace, { .path = "contrib/trace" } };
typedef struct {
void /* trace_query_domain_t */ *(*prepare)(char **args);
bool (*exec)(user_t *u, void /* trace_query_domain_t */ *q);
void (*cleanup)(void /* trace_query_domain_t */ *q);
} trace_query_constructor_t;
typedef struct {
trace_query_constructor_t *cons;
mowgli_node_t node;
} trace_query_domain_t;
static int read_comparison_operator(char** string, int default_comparison)
{
if (**string == '<')
{
if (*((*string)+1) == '=')
{
*string += 2;
return 2;
}
else
{
*string += 1;
return 1;
}
}
else if (**string == '>')
{
if (*((*string)+1) == '=')
{
*string += 2;
return 4;
}
else
{
*string += 1;
return 3;
}
}
else if (**string == '=')
{
*string += 1;
return 0;
}
else
return default_comparison;
}
char *reason_extract(char **args)
{
char *start = *args;
bool quotes = false;
while (**args == ' ')
{
(*args)++;
}
if (**args == '"')
{
start = ++(*args);
quotes = true;
}
else
start = *args;
while (**args)
{
if (quotes && **args == '"')
{
quotes = false;
**args = 0;
(*args)++;
break;
}
else if (!quotes && **args == ' ')
{
**args = 0;
(*args)++;
break;
}
(*args)++;
}
if (!(**args))
*args = NULL;
if (start == *args)
return NULL; /* No reason found. */
if (quotes)
return NULL; /* Unclosed quotes. */
return start;
}
typedef struct {
trace_query_domain_t domain;
atheme_regex_t *regex;
char *pattern;
int flags;
} trace_query_regexp_domain_t;
static void *trace_regexp_prepare(char **args)
{
trace_query_regexp_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
domain = scalloc(sizeof(trace_query_regexp_domain_t), 1);
domain->pattern = regex_extract(*args, &(*args), &domain->flags);
domain->regex = regex_create(domain->pattern, domain->flags);
return domain;
}
static bool trace_regexp_exec(user_t *u, void *q)
{
char usermask[512];
trace_query_regexp_domain_t *domain = (trace_query_regexp_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
if (domain->regex == NULL)
return false;
snprintf(usermask, 512, "%s!%s@%s %s", u->nick, u->user, u->host, u->gecos);
return regex_match(domain->regex, usermask);
}
static void trace_regexp_cleanup(void *q)
{
trace_query_regexp_domain_t *domain = (trace_query_regexp_domain_t *) q;
return_if_fail(domain != NULL);
if (domain->regex != NULL)
regex_destroy(domain->regex);
free(domain);
}
trace_query_constructor_t trace_regexp = { trace_regexp_prepare, trace_regexp_exec, trace_regexp_cleanup };
typedef struct {
trace_query_domain_t domain;
server_t *server;
} trace_query_server_domain_t;
static void *trace_server_prepare(char **args)
{
char *server;
trace_query_server_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
/* split out the next space */
server = strtok(*args, " ");
domain = scalloc(sizeof(trace_query_server_domain_t), 1);
domain->server = server_find(server);
/* advance *args to next token */
*args = strtok(NULL, "");
return domain;
}
static bool trace_server_exec(user_t *u, void *q)
{
trace_query_server_domain_t *domain = (trace_query_server_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
return (domain->server == u->server);
}
static void trace_server_cleanup(void *q)
{
trace_query_server_domain_t *domain = (trace_query_server_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_server = { trace_server_prepare, trace_server_exec, trace_server_cleanup };
typedef struct {
trace_query_domain_t domain;
char *pattern;
} trace_query_glob_domain_t;
static void *trace_glob_prepare(char **args)
{
char *pattern;
trace_query_glob_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
/* split out the next space */
pattern = strtok(*args, " ");
domain = scalloc(sizeof(trace_query_glob_domain_t), 1);
domain->pattern = sstrdup(pattern);
*args = strtok(NULL, "");
return domain;
}
static bool trace_glob_exec(user_t *u, void *q)
{
char usermask[512];
trace_query_glob_domain_t *domain = (trace_query_glob_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
if (domain->pattern == NULL)
return false;
snprintf(usermask, 512, "%s!%s@%s", u->nick, u->user, u->host);
return !match(domain->pattern, usermask);
}
static void trace_glob_cleanup(void *q)
{
trace_query_glob_domain_t *domain = (trace_query_glob_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_glob = { trace_glob_prepare, trace_glob_exec, trace_glob_cleanup };
typedef struct {
trace_query_domain_t domain;
channel_t *channel;
} trace_query_channel_domain_t;
static void *trace_channel_prepare(char **args)
{
char *channel;
trace_query_channel_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
/* split out the next space */
channel = strtok(*args, " ");
domain = scalloc(sizeof(trace_query_channel_domain_t), 1);
domain->channel = channel_find(channel);
/* advance *args to next token */
*args = strtok(NULL, "");
return domain;
}
static bool trace_channel_exec(user_t *u, void *q)
{
trace_query_channel_domain_t *domain = (trace_query_channel_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
return (chanuser_find(domain->channel, u) != NULL);
}
static void trace_channel_cleanup(void *q)
{
trace_query_channel_domain_t *domain = (trace_query_channel_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_channel = { trace_channel_prepare, trace_channel_exec, trace_channel_cleanup };
typedef struct {
trace_query_domain_t domain;
int nickage;
int comparison;
} trace_query_nickage_domain_t;
static void *trace_nickage_prepare(char **args)
{
char *nickage_string;
trace_query_nickage_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
/* split out the next space */
nickage_string = strtok(*args, " ");
domain = scalloc(sizeof(trace_query_nickage_domain_t), 1);
domain->comparison = read_comparison_operator(&nickage_string, 2);
domain->nickage = atoi(nickage_string);
/* advance *args to next token */
*args = strtok(NULL, "");
return domain;
}
static bool trace_nickage_exec(user_t *u, void *q)
{
trace_query_nickage_domain_t *domain = (trace_query_nickage_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
int nickage = CURRTIME - u->ts;
if (domain->comparison == 1)
return (nickage < domain->nickage);
else if (domain->comparison == 2)
return (nickage <= domain->nickage);
else if (domain->comparison == 3)
return (nickage > domain->nickage);
else if (domain->comparison == 4)
return (nickage >= domain->nickage);
else
return (nickage == domain->nickage);
}
static void trace_nickage_cleanup(void *q)
{
trace_query_nickage_domain_t *domain = (trace_query_nickage_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_nickage = { trace_nickage_prepare, trace_nickage_exec, trace_nickage_cleanup };
typedef struct {
trace_query_domain_t domain;
int numchan;
int comparison;
} trace_query_numchan_domain_t;
static void *trace_numchan_prepare(char **args)
{
char *numchan_string;
trace_query_numchan_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
/* split out the next space */
numchan_string = strtok(*args, " ");
domain = scalloc(sizeof(trace_query_numchan_domain_t), 1);
domain->comparison = read_comparison_operator(&numchan_string, 0);
domain->numchan = atoi(numchan_string);
/* advance *args to next token */
*args = strtok(NULL, "");
return domain;
}
static bool trace_numchan_exec(user_t *u, void *q)
{
trace_query_numchan_domain_t *domain = (trace_query_numchan_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
int numchan = u->channels.count;
if (domain->comparison == 1)
return (numchan < domain->numchan);
else if (domain->comparison == 2)
return (numchan <= domain->numchan);
else if (domain->comparison == 3)
return (numchan > domain->numchan);
else if (domain->comparison == 4)
return (numchan >= domain->numchan);
else
return (numchan == domain->numchan);
}
static void trace_numchan_cleanup(void *q)
{
trace_query_numchan_domain_t *domain = (trace_query_numchan_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_numchan = { trace_numchan_prepare, trace_numchan_exec, trace_numchan_cleanup };
typedef struct {
trace_query_domain_t domain;
bool identified;
} trace_query_identified_domain_t;
static void *trace_identified_prepare(char **args)
{
char *yesno;
bool identified;
trace_query_identified_domain_t *domain;
return_val_if_fail(args != NULL, NULL);
return_val_if_fail(*args != NULL, NULL);
yesno = strtok(*args, " ");
if (!strcasecmp(yesno, "yes"))
identified = true;
else if (!strcasecmp(yesno, "no"))
identified = false;
else
return NULL;
domain = scalloc(sizeof(trace_query_identified_domain_t), 1);
domain->identified = identified;
/* advance *args to next token */
*args = strtok(NULL, "");
return domain;
}
static bool trace_identified_exec(user_t *u, void *q)
{
trace_query_identified_domain_t *domain = (trace_query_identified_domain_t *) q;
return_val_if_fail(domain != NULL, false);
return_val_if_fail(u != NULL, false);
return (domain->identified == (u->myuser != NULL));
}
static void trace_identified_cleanup(void *q)
{
trace_query_identified_domain_t *domain = (trace_query_identified_domain_t *) q;
return_if_fail(domain != NULL);
free(domain);
}
trace_query_constructor_t trace_identified = { trace_identified_prepare, trace_identified_exec, trace_identified_cleanup };
/****************************************************************************************************/
typedef struct {
sourceinfo_t *si;
bool matched;
} trace_action_t;
typedef struct {
trace_action_t *(*prepare)(sourceinfo_t *si, char **args);
void (*exec)(user_t *u, trace_action_t *a);
void (*cleanup)(trace_action_t *a, bool succeeded);
} trace_action_constructor_t;
static void trace_action_init(trace_action_t *a, sourceinfo_t *si);
/* initializes common fields for trace action object. */
void trace_action_init(trace_action_t *a, sourceinfo_t *si)
{
return_if_fail(a != NULL);
return_if_fail(si != NULL);
a->si = si;
a->matched = false;
}
static trace_action_t *trace_print_prepare(sourceinfo_t *si, char **args)
{
trace_action_t *a;
return_val_if_fail(si != NULL, NULL);
a = scalloc(sizeof(trace_action_t), 1);
trace_action_init(a, si);
return a;
}
static void trace_print_exec(user_t *u, trace_action_t *a)
{
return_if_fail(u != NULL);
return_if_fail(a != NULL);
if(is_internal_client(u))
return;
a->matched = true;
command_success_nodata(a->si, _("\2Match:\2 %s!%s@%s %s {%s}"), u->nick, u->user, u->host, u->gecos, u->server->name);
}
static void trace_print_cleanup(trace_action_t *a, bool succeeded)
{
return_if_fail(a != NULL);
if (!a->matched && succeeded)
command_success_nodata(a->si, _("No matches."));
free(a);
}
trace_action_constructor_t trace_print = { trace_print_prepare, trace_print_exec, trace_print_cleanup };
typedef struct {
trace_action_t base;
char *reason;
} trace_action_kill_t;
static trace_action_t *trace_kill_prepare(sourceinfo_t *si, char **args)
{
trace_action_kill_t *a;
char *reason;
return_val_if_fail(si != NULL, NULL);
return_val_if_fail(args != NULL, NULL);
if (*args == NULL)
return NULL;
reason = reason_extract(args);
if (reason == NULL)
return NULL;
a = scalloc(sizeof(trace_action_kill_t), 1);
trace_action_init(&a->base, si);
a->reason = reason;
return (trace_action_t*) a;
}
static void trace_kill_exec(user_t *u, trace_action_t *act)
{
service_t *svs;
trace_action_kill_t *a = (trace_action_kill_t *) act;
return_if_fail(u != NULL);
return_if_fail(a != NULL);
if (is_internal_client(u))
return;
if (is_ircop(u))
return;
if (u->myuser && is_soper(u->myuser))
return;
if ((svs = service_find("operserv")) != NULL)
return;
act->matched = true;
kill_user(svs->me, u, "%s", a->reason);
command_success_nodata(act->si, _("\2%s\2 has been killed."), u->nick);
}
static void trace_kill_cleanup(trace_action_t *a, bool succeeded)
{
return_if_fail(a != NULL);
if (!a->matched && succeeded)
command_success_nodata(a->si, _("No matches."));
free(a);
}
trace_action_constructor_t trace_kill = { trace_kill_prepare, trace_kill_exec, trace_kill_cleanup };
typedef struct {
trace_action_t base;
long duration;
char *reason;
} trace_action_akill_t;
static trace_action_t *trace_akill_prepare(sourceinfo_t *si, char **args)
{
trace_action_akill_t *a;
char *s, *reason;
long duration = config_options.kline_time;
char token;
return_val_if_fail(si != NULL, NULL);
return_val_if_fail(args != NULL, NULL);
if (*args == NULL)
return NULL;
while (**args == ' ')
(*args)++;
/* Extract a token, but only if there's one to remove.
* Otherwise, this would clip a word off the reason. */
token = 0;
s = *args;
if (!strncasecmp(s, "!T", 2) || !strncasecmp(s, "!P", 2))
{
if (s[2] == ' ')
{
token = tolower(s[1]);
s[2] = '\0';
*args += 3;
}
else if (s[2] == '\0')
{
token = tolower(s[1]);
*args += 2;
}
}
if (token == 't')
{
s = strtok(*args, " ");
*args = strtok(NULL, "");
if (*args == NULL)
return NULL;
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)
return NULL;
}
else if (token == 'p')
duration = 0;
reason = reason_extract(args);
if (reason == NULL)
return NULL;
a = scalloc(sizeof(trace_action_akill_t), 1);
trace_action_init(&a->base, si);
a->duration = duration;
a->reason = reason;
return (trace_action_t*) a;
}
static void trace_akill_exec(user_t *u, trace_action_t *act)
{
char *kuser, *khost;
trace_action_akill_t *a = (trace_action_akill_t *) act;
return_if_fail(u != NULL);
return_if_fail(a != NULL);
if (is_internal_client(u))
return;
if (is_ircop(u))
return;
if (u->myuser && is_soper(u->myuser))
return;
kuser = "*";
khost = u->host;
if (!match(khost, "127.0.0.1") || !match_ips(khost, "127.0.0.1"))
return;
if (me.vhost != NULL && (!match(khost, me.vhost) || !match_ips(khost, me.vhost)))
return;
if (kline_find(kuser, khost))
return;
act->matched = true;
kline_add(kuser, khost, a->reason, a->duration, get_storage_oper_name(act->si));
command_success_nodata(act->si, _("\2%s\2 has been akilled."), u->nick);
}
static void trace_akill_cleanup(trace_action_t *a, bool succeeded)
{
return_if_fail(a != NULL);
if (!a->matched && succeeded)
command_success_nodata(a->si, _("No matches."));
free(a);
}
trace_action_constructor_t trace_akill = { trace_akill_prepare, trace_akill_exec, trace_akill_cleanup };
typedef struct {
trace_action_t base;
int matches;
} trace_action_count_t;
static trace_action_t *trace_count_prepare(sourceinfo_t *si, char **args)
{
trace_action_count_t *a;
return_val_if_fail(si != NULL, NULL);
a = scalloc(sizeof(trace_action_count_t), 1);
trace_action_init(&a->base, si);
return (trace_action_t *) a;
}
static void trace_count_exec(user_t *u, trace_action_t *act)
{
trace_action_count_t *a = (trace_action_count_t *) act;
return_if_fail(u != NULL);
return_if_fail(a != NULL);
if (is_internal_client(u))
return;
act->matched = true;
a->matches++;
}
static void trace_count_cleanup(trace_action_t *act, bool succeeded)
{
trace_action_count_t *a = (trace_action_count_t *) act;
return_if_fail(a != NULL);
if (succeeded)
command_success_nodata(act->si, _("\2%d\2 matches"), a->matches);
free(a);
}
trace_action_constructor_t trace_count = { trace_count_prepare, trace_count_exec, trace_count_cleanup };
/*
* Add-on interface.
*
* This allows third-party module writers to extend the trace API.
* Just copy the prototypes out of trace.c, and add the trace_cmdtree
* symbol to your module with MODULE_USE_SYMBOL().
*
* Then add your criteria to the tree with mowgli_patricia_add().
*/
mowgli_patricia_t *trace_cmdtree = NULL;
mowgli_patricia_t *trace_acttree = NULL;
void _modinit(module_t *m)
{
service_named_bind_command("operserv", &os_trace);
trace_cmdtree = mowgli_patricia_create(strcasecanon);
mowgli_patricia_add(trace_cmdtree, "REGEXP", &trace_regexp);
mowgli_patricia_add(trace_cmdtree, "SERVER", &trace_server);
mowgli_patricia_add(trace_cmdtree, "GLOB", &trace_glob);
mowgli_patricia_add(trace_cmdtree, "CHANNEL", &trace_channel);
mowgli_patricia_add(trace_cmdtree, "NICKAGE", &trace_nickage);
mowgli_patricia_add(trace_cmdtree, "NUMCHAN", &trace_numchan);
mowgli_patricia_add(trace_cmdtree, "IDENTIFIED", &trace_identified);
trace_acttree = mowgli_patricia_create(strcasecanon);
mowgli_patricia_add(trace_acttree, "PRINT", &trace_print);
mowgli_patricia_add(trace_acttree, "KILL", &trace_kill);
mowgli_patricia_add(trace_acttree, "AKILL", &trace_akill);
mowgli_patricia_add(trace_acttree, "COUNT", &trace_count);
}
void _moddeinit(module_unload_intent_t intent)
{
mowgli_patricia_destroy(trace_cmdtree, NULL, NULL);
service_named_unbind_command("operserv", &os_trace);
}
#define MAXMATCHES_DEF 1000
static bool os_cmd_trace_run(sourceinfo_t *si, trace_action_constructor_t *actcons, trace_action_t* act, mowgli_list_t *crit, char *args);
static void os_cmd_trace(sourceinfo_t *si, int parc, char *parv[])
{
mowgli_list_t crit = { NULL, NULL, 0 };
trace_action_constructor_t *actcons;
trace_action_t* act;
char *args = parv[1];
mowgli_node_t *n, *tn;
char *params;
bool succeeded;
if (!parv[0])
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "TRACE");
command_fail(si, fault_badparams, _("Syntax: TRACE <action> <params>"));
return;
}
actcons = mowgli_patricia_retrieve(trace_acttree, parv[0]);
if (actcons == NULL)
{
command_fail(si, fault_badparams, STR_INVALID_PARAMS, "TRACE");
command_fail(si, fault_badparams, _("Syntax: TRACE <action> <params>"));
return;
}
act = actcons->prepare(si, &args);
if (act == NULL)
{
command_fail(si, fault_nosuch_target, _("Action compilation failed."));
return;
}
params = sstrdup(args);
succeeded = os_cmd_trace_run(si, actcons, act, &crit, args);
MOWGLI_ITER_FOREACH_SAFE(n, tn, crit.head)
{
trace_query_domain_t *q = (trace_query_domain_t *) n->data;
q->cons->cleanup(q);
}
actcons->cleanup(act, succeeded);
if (succeeded)
logcommand(si, CMDLOG_ADMIN, "TRACE: \2%s\2 \2%s\2", parv[0], params);
free(params);
}
static bool os_cmd_trace_run(sourceinfo_t *si, trace_action_constructor_t *actcons, trace_action_t* act, mowgli_list_t *crit, char *args)
{
user_t *u;
mowgli_patricia_iteration_state_t state;
mowgli_node_t *n;
if (args == NULL)
{
command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "TRACE");
command_fail(si, fault_needmoreparams, _("Syntax: TRACE <action> <params>"));
return false;
}
while (true)
{
trace_query_constructor_t *cons;
trace_query_domain_t *q;
char *cmd = strtok(args, " ");
if (cmd == NULL)
break;
cons = mowgli_patricia_retrieve(trace_cmdtree, cmd);
if (cons == NULL)
{
command_fail(si, fault_nosuch_target, _("Invalid criteria specified."));
return false;
}
args = strtok(NULL, "");
if (args == NULL)
{
command_fail(si, fault_nosuch_target, _("Invalid criteria specified."));
return false;
}
q = cons->prepare(&args);
slog(LG_DEBUG, "operserv/trace: adding criteria %p(%s) to list [remain: %s]", q, cmd, args);
if (q == NULL)
{
command_fail(si, fault_nosuch_target, _("Invalid criteria specified."));
return false;
}
slog(LG_DEBUG, "operserv/trace: new args position [%s]", args);
q->cons = cons;
mowgli_node_add(q, &q->node, crit);
}
MOWGLI_PATRICIA_FOREACH(u, &state, userlist)
{
bool doit = true;
MOWGLI_ITER_FOREACH(n, crit->head)
{
trace_query_domain_t *q = (trace_query_domain_t *) n->data;
if (!q->cons->exec(u, q))
{
doit = false;
break;
}
}
if (doit)
actcons->exec(u, act);
}
return true;
}
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
* vim:ts=8
* vim:sw=8
* vim:noexpandtab
*/

1027
wumpus.c Normal file

File diff suppressed because it is too large Load Diff