2019-01-23 09:35:39 +01:00

1006 lines
32 KiB
C

/* Bahamut protocol module for IRC Services.
*
* IRC Services is copyright (c) 1996-2009 Andrew Church.
* E-mail: <achurch@achurch.org>
* Parts written by Andrew Kempe and others.
* This program is free but copyrighted software; see the file GPL.txt for
* details.
*/
#include "services.h"
#include "modules.h"
#include "conffile.h"
#include "language.h"
#include "messages.h"
#include "modules/operserv/operserv.h"
#include "modules/operserv/maskdata.h"
#include "modules/nickserv/nickserv.h"
#include "modules/chanserv/chanserv.h"
#define BAHAMUT_HACK /* For SJOIN; see comments in sjoin.c */
#include "banexcept.c"
#include "invitemask.c"
#include "sjoin.c"
#include "svsnick.c"
/*************************************************************************/
static Module *module_operserv;
static Module *module_chanserv;
static char *NetworkDomain = NULL;
static int32 usermode_secure = 0; /* +S */
static int32 chanmode_secure_only = 0; /* +S */
/*************************************************************************/
/***************** Local interface to external routines ******************/
/*************************************************************************/
static typeof(is_services_admin) *p_is_services_admin = NULL;
static int local_is_services_admin(User *u)
{
return p_is_services_admin && (*p_is_services_admin)(u);
}
#define is_services_admin local_is_services_admin
static char **p_s_ChanServ = &ServerName;
#define s_ChanServ (*p_s_ChanServ)
/*************************************************************************/
/************************** User/channel modes ***************************/
/*************************************************************************/
#define MI_SECURE 0x01000000 /* Usermode for users on secure conn's */
#define MI_SECURE_ONLY 0x01000000 /* Chanmode for secure-user-only chans */
struct modedata_init {
uint8 mode;
ModeData data;
};
static const struct modedata_init new_usermodes[] = {
{'g', {0x00000008}}, /* Receive globops */
{'h', {0x00000010}}, /* Helpop */
{'r', {0x00000020,0,0,0,MI_REGISTERED}},
/* Registered nick */
{'a', {0x00000040}}, /* Services admin */
{'A', {0x00000080}}, /* Server admin */
{'S', {0x00000100,0,0,0,MI_SECURE}},
/* Using secure connection */
};
static const struct modedata_init new_chanmodes[] = {
{'R', {0x00000100,0,0,0,MI_REGNICKS_ONLY}},
/* Only identified users can join */
{'r', {0x00000200,0,0,0,MI_REGISTERED}},
/* Set for all registered channels */
{'c', {0x00000400,0,0}}, /* No ANSI colors in channel */
{'O', {0x00000800,0,0,0,MI_OPERS_ONLY}},
/* Only opers can join channel */
{'M', {0x02000000,0,0}}, /* Moderated to unregged nicks */
{'j', {0x04000000,1,0}}, /* /join rate limit */
{'S', {0x08000000,0,0,0,MI_SECURE_ONLY}},
/* Only secure (+S) users allowed */
{'e', {0x80000000,1,1,0,MI_MULTIPLE}},
/* Ban exceptions */
{'I', {0x80000000,1,1,0,MI_MULTIPLE}},
/* INVITE hosts */
};
static const struct modedata_init new_chanusermodes[] = {
};
static void init_modes(void)
{
int i;
for (i = 0; i < lenof(new_usermodes); i++) {
usermodes[new_usermodes[i].mode] = new_usermodes[i].data;
if (new_usermodes[i].data.info & MI_SECURE)
usermode_secure |= new_usermodes[i].data.flag;
}
for (i = 0; i < lenof(new_chanmodes); i++) {
chanmodes[new_chanmodes[i].mode] = new_chanmodes[i].data;
if (new_chanmodes[i].data.info & MI_SECURE_ONLY)
chanmode_secure_only |= new_chanmodes[i].data.flag;
}
for (i = 0; i < lenof(new_chanusermodes); i++)
chanusermodes[new_chanusermodes[i].mode] = new_chanusermodes[i].data;
mode_setup();
};
/*************************************************************************/
/************************* IRC message receiving *************************/
/*************************************************************************/
static void m_nick(char *source, int ac, char **av)
{
char ipbuf[16], *s;
uint32 ip;
if (*source) {
/* Old user changing nicks. */
if (ac != 2) {
module_log_debug(1, "NICK message: wrong number of parameters"
" (%d) for source `%s'", ac, source);
} else {
do_nick(source, ac, av);
}
return;
}
/* New user. */
if (ac != 10) {
module_log_debug(1, "NICK message: wrong number of parameters (%d)"
" for new user", ac);
return;
}
/* Move modes to the end, like do_nick() wants */
s = av[3];
memmove(&av[3], &av[4], sizeof(char *) * (ac-4));
av[9] = s;
/* Convert IP address from 32 bit integer to string format */
ip = strtoul(av[7], &s, 10);
if (*s) {
wallops(NULL,
"\2WARNING\2: invalid IP address `%s' for new nick %s",
av[7], av[0]);
module_log("WARNING: invalid IP address `%s' for new nick %s",
av[7], av[0]);
s = NULL;
} else if (!ip && find_module("operserv/sline")) {
static int warned_no_nickip = 0;
if (!warned_no_nickip) {
wallops(NULL, "\2WARNING\2: missing IP address for new nick %s",
av[0]);
warned_no_nickip = 1;
}
module_log("WARNING: missing IP address for new nick %s", av[0]);
s = strcpy(ipbuf, "0.0.0.0");
} else {
uint8 rawip[4];
rawip[0] = ip>>24;
rawip[1] = ip>>16;
rawip[2] = ip>>8;
rawip[3] = ip;
s = unpack_ip(rawip);
if (!s || strlen(s) > sizeof(ipbuf)-1) {
/* super duper paranoia */
module_log("WARNING: unpack_ip() returned overlong or null"
" string: %s", s ? s : "(null)");
s = NULL;
} else {
strcpy(ipbuf, s); /* safe: we checked length above */
s = ipbuf;
}
}
/* Rearrange parameters for do_nick() (IP address is in `s') */
av[7] = av[6];
av[6] = av[8];
av[8] = s;
do_nick(source, ac, av);
}
/*************************************************************************/
static void m_capab(char *source, int ac, char **av)
{
int i;
for (i = 0; i < ac; i++) {
if (stricmp(av[i], "NOQUIT") == 0)
protocol_features |= PF_NOQUIT;
}
}
/*************************************************************************/
static void m_sjoin(char *source, int ac, char **av)
{
if (ac == 3 || ac < 2) {
module_log_debug(1, "SJOIN: expected 2 or >=4 params, got %d", ac);
return;
}
do_sjoin(source, ac, av);
}
/*************************************************************************/
/* SVSMODE message handler. */
static void m_svsmode(char *source, int ac, char **av)
{
if (*av[0] == '#') {
if (ac >= 3 && strcmp(av[1],"-b") == 0) {
Channel *c = get_channel(av[0]);
User *u = get_user(av[2]);
if (c && u)
clear_channel(c, CLEAR_BANS, u);
} else {
module_log("Invalid SVSMODE from %s for channel %s: %s",
source, av[0], merge_args(ac-1,av+1));
}
} else if (*av[0] == '&') {
module_log("SVSMODE from %s for invalid target (channel %s): %s",
source, av[0], merge_args(ac-1,av+1));
} else {
if (ac < 2)
return;
if (ac >= 3 && (*av[2] == '+' || *av[2] == '-')) {
/* Move the timestamp to the end */
char *ts = av[1];
memmove(av+1, av+2, sizeof(char *) * (ac-2));
av[ac-1] = ts;
}
do_umode(source, ac, av);
}
}
/*************************************************************************/
/* SGLINE/SQLINE message handlers. These remove any SGLINE/SQLINE received
* which does not exist in Services' databases, to avoid servers which were
* split during an UNSGLINE/UNSQLINE re-propogating the mask across the
* network. See m_tkl() in unreal.c for details.
*/
static void do_sgqline(char *source, int ac, char **av, int type)
{
typeof(get_maskdata) *p_get_maskdata = NULL;
typeof(put_maskdata) *p_put_maskdata = NULL;
char *mask;
if (ac != 2)
return;
if (type == MD_SGLINE) {
int masklen = strtol(av[0], NULL, 10);
mask = av[1];
if (masklen <= 0)
return;
mask[masklen] = 0;
} else { /* MD_SQLINE */
mask = av[0];
}
if (!(p_get_maskdata = get_module_symbol(NULL, "get_maskdata")))
return;
if (!(p_put_maskdata = get_module_symbol(NULL, "put_maskdata")))
return;
if ((*p_put_maskdata)((*p_get_maskdata)(type, mask)))
return;
send_cmd(ServerName, "UNS%cLINE :%s", type==MD_SGLINE ? 'G' : 'Q', mask);
}
static void m_sgline(char *source, int ac, char **av) {
do_sgqline(source, ac, av, MD_SGLINE);
}
static void m_sqline(char *source, int ac, char **av) {
do_sgqline(source, ac, av, MD_SQLINE);
}
/*************************************************************************/
/* Abbreviated message handlers.
* We do not allow Bahamut to run in SERVICESHUB mode because it cuts out
* both the topic burst (causing temporary desynch) and AWAY messages
* (causing MemoServ unaway checking to break).
*/
#ifdef ALLOW_BAHAMUT_SERVICESHUB /* not defined anywhere */
static void expand_to_privmsg(char *source, const char *cmdname,
const char *targetsym, char *msg)
{
char **targetnick = NULL;
if (targetsym)
targetnick = get_module_symbol(NULL, targetsym);
if (targetnick) {
Message *m = find_message("PRIVMSG");
if (m) {
char *av[2];
av[0] = *targetnick;
av[1] = msg;
m->func(source, 2, av);
} else {
module_log("expand_to_privmsg(): BUG: PRIVMSG message entry not"
" found");
send_cmd(ServerName, "421 %s %s :Internal error", source, msg);
}
} else {
/* We don't know what command/nick the user originally used, so
* this is about the best we can do... */
send_cmd(ServerName, "421 %s %s :Command not available", source, msg);
}
}
static void m_ns(char *source, int ac, char **av) {
expand_to_privmsg(source, "NS", "s_NickServ", av[0]); }
static void m_cs(char *source, int ac, char **av) {
expand_to_privmsg(source, "CS", "s_ChanServ", av[0]); }
static void m_ms(char *source, int ac, char **av) {
expand_to_privmsg(source, "MS", "s_MemoServ", av[0]); }
static void m_rs(char *source, int ac, char **av) {
expand_to_privmsg(source, "RS", NULL /*"s_RootServ"*/, av[0]); }
static void m_os(char *source, int ac, char **av) {
expand_to_privmsg(source, "OS", "s_OperServ", av[0]); }
static void m_ss(char *source, int ac, char **av) {
expand_to_privmsg(source, "SS", "s_StatServ", av[0]); }
static void m_hs(char *source, int ac, char **av) {
expand_to_privmsg(source, "HS", "s_HelpServ", av[0]); }
#else
static void no_serviceshub(char *source, int ac, char **av)
{
fatal("IRC Services will not function correctly when Bahamut is"
" configured as a SERVICESHUB. Please reconfigure Bahamut as a"
" regular hub and restart Services.");
}
#define m_ns no_serviceshub
#define m_cs no_serviceshub
#define m_ms no_serviceshub
#define m_rs no_serviceshub
#define m_os no_serviceshub
#define m_ss no_serviceshub
#define m_hs no_serviceshub
#endif
/*************************************************************************/
/* SQUIT handler, for remote SQUITs (SQUITs from remote clients for juped
* servers). This has to be implemented as a "receive message" callback
* because we can't layer message functions on top of each other. */
static void do_bahamut_squit(char *source, int ac, char **av)
{
Server *server;
if (ac < 1) /* also checked in do_receive_message, but for completeness */
return;
server = get_server(av[0]);
if (!server) {
send_cmd(ServerName, "402 %s %s :No such server", source, av[0]);
} else if (!server->fake || server == get_server(ServerName)) {
/* We should never see an SQUIT for ourselves, since the remote
* server will disconnect us instead, but just in case... */
send_cmd(ServerName, "402 %s %s :Not a juped server", source, av[0]);
} else {
do_squit(source, ac, av);
send_cmd(NULL, "SQUIT %s :%s", av[0], av[1] ? av[1] : "");
}
}
static int do_receive_message(char *source, char *cmd, int ac, char **av)
{
if (irc_stricmp(cmd,"SQUIT") != 0)
return 0;
if (!source || !*source || ac < 1 || irc_stricmp(source,av[0]) == 0)
return 0;
do_bahamut_squit(source, ac, av);
return 1;
}
/*************************************************************************/
static Message bahamut_messages[] = {
{ "AKILL", NULL },
{ "CAPAB", m_capab },
{ "CS", m_cs },
{ "GLOBOPS", NULL },
{ "GNOTICE", NULL },
{ "GOPER", NULL },
{ "HS", m_hs },
{ "LOCKLUSERS",NULL },
{ "MS", m_ms },
{ "NICK", m_nick },
{ "NS", m_ns },
{ "OS", m_os },
{ "RAKILL", NULL },
{ "RS", m_rs },
{ "SGLINE", m_sgline },
{ "SILENCE", NULL },
{ "SJOIN", m_sjoin },
{ "SQLINE", m_sqline },
{ "SS", m_ss },
{ "SVINFO", NULL },
{ "SVSMODE", m_svsmode },
{ "UNSGLINE", NULL },
{ "UNSQLINE", NULL },
{ NULL }
};
/*************************************************************************/
/************************** IRC message sending **************************/
/*************************************************************************/
/* Send a NICK command for a new user. */
static void do_send_nick(const char *nick, const char *user, const char *host,
const char *server, const char *name,
const char *modes)
{
/* NICK <nick> <hops> <TS> <umode> <user> <host> <server> <svsid> <ip>
* :<ircname> */
send_cmd(NULL, "NICK %s 1 %ld +%s %s %s %s 0 0 :%s", nick,
(long)time(NULL), modes, user, host, server, name);
}
/*************************************************************************/
/* Send a NICK command to change an existing user's nick. */
static void do_send_nickchange(const char *nick, const char *newnick)
{
send_cmd(nick, "NICK %s %ld", newnick, (long)time(NULL));
}
/*************************************************************************/
/* Send a command to change a user's "real name". */
static void do_send_namechange(const char *nick, const char *newname)
{
/* Not supported by this protocol. */
}
/*************************************************************************/
/* Send a SERVER command, and anything else needed at the beginning of the
* connection.
*/
static void do_send_server(void)
{
send_cmd(NULL, "PASS %s :TS", RemotePassword);
send_cmd(NULL, "CAPAB TS3 SSJOIN NICKIP NOQUIT TSMODE UNCONNECT");
send_cmd(NULL, "SERVER %s 1 :%s", ServerName, ServerDesc);
send_cmd(NULL, "SVINFO 3 3 0 :%ld", (long)time(NULL));
}
/*************************************************************************/
/* Send a SERVER command for a remote (juped) server. */
static void do_send_server_remote(const char *server, const char *reason)
{
send_cmd(NULL, "SERVER %s 2 :%s", server, reason);
}
/*************************************************************************/
/* Send a WALLOPS (really a GLOBOPS). */
static void do_wallops(const char *source, const char *fmt, ...)
{
va_list args;
char buf[BUFSIZE];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
send_cmd(source ? source : ServerName, "GLOBOPS :%s", buf);
}
/*************************************************************************/
/* Send a NOTICE to all users on the network. */
static void do_notice_all(const char *source, const char *fmt, ...)
{
va_list args;
char msgbuf[BUFSIZE];
va_start(args, fmt);
vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
va_end(args);
if (NetworkDomain) {
send_cmd(source, "NOTICE $*.%s :%s", NetworkDomain, msgbuf);
} else {
/* Go through all common top-level domains. If you have others,
* add them here. */
send_cmd(source, "NOTICE $*.com :%s", msgbuf);
send_cmd(source, "NOTICE $*.net :%s", msgbuf);
send_cmd(source, "NOTICE $*.org :%s", msgbuf);
send_cmd(source, "NOTICE $*.edu :%s", msgbuf);
}
}
/*************************************************************************/
/* Send a command which modifies channel status. */
static void do_send_channel_cmd(const char *source, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsend_cmd(source, fmt, args);
va_end(args);
}
/*************************************************************************/
/******************************* Callbacks *******************************/
/*************************************************************************/
static int do_user_servicestamp_change(User *user)
{
send_cmd(ServerName, "SVSMODE %s +d %u", user->nick, user->servicestamp);
return 0;
}
/*************************************************************************/
static int do_user_mode(User *user, int modechar, int add, char **av)
{
switch (modechar) {
case 'd':
module_log("MODE tried to change services stamp for %s", user->nick);
send_cmd(ServerName, "SVSMODE %s +d %u", user->nick,
user->servicestamp);
return 0;
case 'o':
if (add) {
user->mode |= UMODE_o;
if (add && user_identified(user) && is_services_admin(user))
send_cmd(ServerName, "SVSMODE %s +a", user->nick);
user->mode &= ~UMODE_o;
}
return 0;
case 'r':
if (user_identified(user)) {
if (!add)
send_cmd(ServerName, "SVSMODE %s +r", user->nick);
} else {
if (add)
send_cmd(ServerName, "SVSMODE %s -r", user->nick);
}
return 1;
case 'a':
if (is_oper(user)) {
if (is_services_admin(user)) {
if (!add)
send_cmd(ServerName, "SVSMODE %s +a", user->nick);
} else {
if (add)
send_cmd(ServerName, "SVSMODE %s -a", user->nick);
}
return 1;
}
} /* switch (mode) */
return 0;
}
/*************************************************************************/
static int do_channel_mode(const char *source, Channel *channel,
int modechar, int add, char **av)
{
int32 flag = mode_char_to_flag(modechar, MODE_CHANNEL);
switch (modechar) {
case 'j':
if (add) {
int ok = 0;
char *s;
int joinrate1 = strtol(av[0], &s, 0);
if (*s == ':') {
int joinrate2 = strtol(s+1, &s, 0);
if (!*s) {
if (joinrate1 && joinrate2) {
channel->mode |= flag;
channel->joinrate1 = joinrate1;
channel->joinrate2 = joinrate2;
} else {
channel->mode &= ~flag;
channel->joinrate1 = 0;
channel->joinrate2 = 0;
ok = 1;
}
ok = 1;
}
} else if (joinrate1 == 0) {
channel->mode &= ~flag;
channel->joinrate1 = 0;
channel->joinrate2 = 0;
ok = 1;
}
if (!ok) {
module_log("warning: invalid MODE +j %s for %s", av[0],
channel->name);
}
} else {
channel->mode &= ~flag;
channel->joinrate1 = 0;
channel->joinrate2 = 0;
}
return 1;
} /* switch (modechar) */
return 0;
}
/*************************************************************************/
static int do_nick_identified(User *u, int old_status)
{
if (is_oper(u) && is_services_admin(u))
send_cmd(ServerName, "SVSMODE %s +a", u->nick);
return 0;
}
/*************************************************************************/
static int do_set_topic(const char *source, Channel *c, const char *topic,
const char *setter, time_t t)
{
if (setter)
return 0;
c->topic_time = t;
send_cmd(source, "TOPIC %s %s %ld :%s", c->name, c->topic_setter,
(long)c->topic_time, c->topic ? c->topic : "");
return 1;
}
/*************************************************************************/
static int do_check_modes(Channel *c, ChannelInfo *ci, int add, int32 flag)
{
if (add) {
switch (mode_flag_to_char(flag, MODE_CHANNEL)) {
case 'j':
if (sgn(ci->mlock.joinrate1) != sgn(ci->mlock.joinrate2)) {
module_log("warning: removing +j from channel %s mode lock"
" (invalid parameter: %d:%d)", ci->name,
ci->mlock.joinrate1, ci->mlock.joinrate2);
ci->mlock.on &= ~mode_char_to_flag('j', MODE_CHANNEL);
ci->mlock.joinrate1 = ci->mlock.joinrate2 = 0;
} else if (ci->mlock.joinrate1 < 0) {
if (c->joinrate1 || c->joinrate2)
set_cmode(s_ChanServ, c, "-j");
} else {
if (c->joinrate1 != ci->mlock.joinrate1
|| c->joinrate2 != ci->mlock.joinrate2
) {
char buf[BUFSIZE];
snprintf(buf, sizeof(buf), "%d:%d",
ci->mlock.joinrate1, ci->mlock.joinrate2);
set_cmode(s_ChanServ, c, "+j", buf);
}
}
return 1;
}
}
return 0;
}
/*************************************************************************/
/*
* Callback for ChanServ channel entrance checking; we enforce the
* secure-only (+S) channel mode here.
*/
static int do_check_kick(User *user, const char *chan, ChannelInfo *ci,
char **mask_ret, const char **reason_ret)
{
/* Retrieve the channel's Channel record, if present */
Channel *c = get_channel(chan);
/* Don't let users not on secure connections (-S) into +S channels */
if ((((c?c->mode:0) | (ci?ci->mlock.on:0)) & chanmode_secure_only)
&& !(user->mode & usermode_secure)
) {
*mask_ret = create_mask(user, 1);
*reason_ret = getstring(user->ngi, CHAN_NOT_ALLOWED_TO_JOIN);
return 1;
}
/* Let other processing continue as usual */
return 0;
}
/*************************************************************************/
static int do_set_mlock(User *u, ChannelInfo *ci, int mode, int add, char **av)
{
if (!mode) {
/* Final check of new mode lock--nothing to do */
return 0;
}
/* Single mode set/clear */
if (add) {
switch (mode) {
case 'j': {
int ok = 0;
char *s;
ci->mlock.joinrate1 = strtol(av[0], &s, 0);
if (ci->mlock.joinrate1 > 0 && *s == ':') {
ci->mlock.joinrate2 = strtol(s+1, &s, 0);
if (ci->mlock.joinrate2 > 0 && !*s)
ok = 1;
}
if (!ok) {
notice_lang(s_ChanServ, u, CHAN_SET_MLOCK_BAD_PARAM, mode);
return 1;
}
break;
} /* case 'j' */
} /* switch (mode) */
} else { /* !add -> lock off */
switch (mode) {
case 'j':
ci->mlock.joinrate1 = ci->mlock.joinrate2 = -1;
break;
} /* switch (mode) */
} /* if (add) */
return 0;
}
/*************************************************************************/
static int do_send_akill(const char *username, const char *host,
time_t expires, const char *who, const char *reason)
{
time_t now = time(NULL);
/* Bahamut 1.8.0 has a "feature" which converts an expiration delay of
* zero (supposedly "unlimited") into a delay of one week. Therefore,
* we have a "feature" which sends an unlimited expiration time as
* many years in the future. */
time_t length = ((expires && expires > now) ? expires - now : 0);
if (length == 0 && now < 0x7FFFFFFF)
length = 0x7FFFFFFF - now;
send_cmd(ServerName, "AKILL %s %s %ld %s %ld :%s", host, username,
(long)length, who ? who : "<unknown>", (long)now, reason);
return 1;
}
/*************************************************************************/
static int do_cancel_akill(const char *username, const char *host)
{
send_cmd(ServerName, "RAKILL %s %s", host, username);
return 1;
}
/*************************************************************************/
static int do_send_sgline(const char *mask, time_t expires, const char *who,
const char *reason)
{
send_cmd(ServerName, "SGLINE %d :%s:%s", (int)strlen(mask), mask, reason);
return 1;
}
static int do_send_sqline(const char *mask, time_t expires, const char *who,
const char *reason)
{
send_cmd(ServerName, "SQLINE %s :%s", mask, reason);
return 1;
}
static int do_send_szline(const char *mask, time_t expires, const char *who,
const char *reason)
{
return do_send_akill("*", mask, expires, who, reason);
}
/*************************************************************************/
static int do_cancel_sgline(const char *mask)
{
send_cmd(ServerName, "UNSGLINE :%s", mask);
return 1;
}
static int do_cancel_sqline(const char *mask)
{
send_cmd(ServerName, "UNSQLINE %s", mask);
return 1;
}
static int do_cancel_szline(const char *mask)
{
return do_cancel_akill("*", mask);
return 1;
}
/*************************************************************************/
/***************************** Module stuff ******************************/
/*************************************************************************/
ConfigDirective module_config[] = {
{ "NetworkDomain", { { CD_STRING, 0, &NetworkDomain } } },
SJOIN_CONFIG,
{ NULL }
};
/*************************************************************************/
static int do_load_module(Module *mod, const char *modname)
{
if (strcmp(modname, "operserv/main") == 0) {
module_operserv = mod;
p_is_services_admin = get_module_symbol(mod, "is_services_admin");
if (!p_is_services_admin) {
module_log("warning: unable to look up symbol `is_services_admin'"
" in module `operserv/main'");
}
} else if (strcmp(modname, "operserv/akill") == 0) {
if (!add_callback(mod, "send_akill", do_send_akill))
module_log("Unable to add send_akill callback");
if (!add_callback(mod, "cancel_akill", do_cancel_akill))
module_log("Unable to add cancel_akill callback");
} else if (strcmp(modname, "operserv/sline") == 0) {
if (!add_callback(mod, "send_sgline", do_send_sgline))
module_log("Unable to add send_sgline callback");
if (!add_callback(mod, "send_sqline", do_send_sqline))
module_log("Unable to add send_sqline callback");
if (!add_callback(mod, "send_szline", do_send_szline))
module_log("Unable to add send_szline callback");
if (!add_callback(mod, "cancel_sgline", do_cancel_sgline))
module_log("Unable to add cancel_sgline callback");
if (!add_callback(mod, "cancel_sqline", do_cancel_sqline))
module_log("Unable to add cancel_sqline callback");
if (!add_callback(mod, "cancel_szline", do_cancel_szline))
module_log("Unable to add cancel_szline callback");
} else if (strcmp(modname, "nickserv/main") == 0) {
if (!add_callback(mod, "identified", do_nick_identified))
module_log("Unable to add NickServ identified callback");
} else if (strcmp(modname, "chanserv/main") == 0) {
module_chanserv = mod;
p_s_ChanServ = get_module_symbol(mod, "s_ChanServ");
if (!p_s_ChanServ)
p_s_ChanServ = &ServerName;
if (!add_callback(mod, "check_modes", do_check_modes))
module_log("Unable to add ChanServ check_modes callback");
if (!add_callback(mod, "check_kick", do_check_kick))
module_log("Unable to add ChanServ check_kick callback");
if (!add_callback(mod, "SET MLOCK", do_set_mlock))
module_log("Unable to add ChanServ SET MLOCK callback");
}
return 0;
}
/*************************************************************************/
static int do_unload_module(Module *mod)
{
if (mod == module_operserv) {
module_operserv = NULL;
p_is_services_admin = NULL;
} else if (mod == module_chanserv) {
module_chanserv = NULL;
p_s_ChanServ = &ServerName;
}
return 0;
}
/*************************************************************************/
int init_module(void)
{
unsigned char c;
protocol_name = "Bahamut";
protocol_version = "1.8.0+";
protocol_features = PF_SZLINE | PF_MODETS_FIRST;
protocol_nickmax = 30;
if (!register_messages(bahamut_messages)) {
module_log("Unable to register messages");
exit_module(1);
return 0;
}
if (!add_callback(NULL, "load module", do_load_module)
|| !add_callback(NULL, "unload module", do_unload_module)
|| !add_callback(NULL, "receive message", do_receive_message)
|| !add_callback(NULL, "user servicestamp change",
do_user_servicestamp_change)
|| !add_callback(NULL, "channel MODE", do_channel_mode)
|| !add_callback(NULL, "user MODE", do_user_mode)
|| !add_callback(NULL, "set topic", do_set_topic)
) {
module_log("Unable to add callbacks");
exit_module(1);
return 0;
}
if (!init_banexcept()
|| !init_invitemask()
|| !init_sjoin()
|| !init_svsnick("SVSNICK")
) {
exit_module(1);
return 0;
}
init_modes();
irc_lowertable['['] = '[';
irc_lowertable['\\'] = '\\';
irc_lowertable[']'] = ']';
for (c = 0; c < 32; c++)
valid_chan_table[c] = 0;
valid_chan_table[160] = 0;
send_nick = do_send_nick;
send_nickchange = do_send_nickchange;
send_namechange = do_send_namechange;
send_server = do_send_server;
send_server_remote = do_send_server_remote;
wallops = do_wallops;
notice_all = do_notice_all;
send_channel_cmd = do_send_channel_cmd;
pseudoclient_modes = "";
enforcer_modes = "";
pseudoclient_oper = 0;
mapstring(OPER_BOUNCY_MODES, OPER_BOUNCY_MODES_U_LINE);
return 1;
}
/*************************************************************************/
int exit_module(int shutdown)
{
if (!shutdown) {
/* Do not allow removal */
return 0;
}
exit_svsnick();
exit_sjoin();
exit_invitemask();
exit_banexcept();
remove_callback(NULL, "set topic", do_set_topic);
remove_callback(NULL, "user MODE", do_user_mode);
remove_callback(NULL, "channel MODE", do_channel_mode);
remove_callback(NULL, "user servicestamp change",
do_user_servicestamp_change);
remove_callback(NULL, "receive message", do_receive_message);
remove_callback(NULL, "unload module", do_unload_module);
remove_callback(NULL, "load module", do_load_module);
unregister_messages(bahamut_messages);
return 1;
}
/*************************************************************************/
/*
* Local variables:
* c-file-style: "stroustrup"
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
* indent-tabs-mode: nil
* End:
*
* vim: expandtab shiftwidth=4:
*/