mirror of
https://github.com/anope/anope.git
synced 2026-06-25 12:36:38 +02:00
a9c609678f
git-svn-id: svn://svn.anope.org/anope/trunk@36 31f1291d-b8d6-0310-a050-a5561fc1590b git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@26 5417fbe8-f217-4b02-8779-1006273d7864
5112 lines
154 KiB
C
5112 lines
154 KiB
C
/* OperServ functions.
|
|
*
|
|
* (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$
|
|
*
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "pseudo.h"
|
|
|
|
/*
|
|
* Disable the modules on OpenBSD (for now)
|
|
* there is work in progress for this.
|
|
*/
|
|
#ifdef __OpenBSD__
|
|
#ifdef USE_MODULES
|
|
#undef USE_MODULES
|
|
#endif /* USE_MODULES */
|
|
#endif /* __OpenBSD__ */
|
|
|
|
extern Module *mod_current_module;
|
|
extern int mod_current_op;
|
|
extern User *mod_current_user;
|
|
extern ModuleHash *MODULE_HASH[MAX_CMD_HASH];
|
|
/*************************************************************************/
|
|
|
|
struct clone {
|
|
char *host;
|
|
long time;
|
|
};
|
|
|
|
/* List of most recent users - statically initialized to zeros */
|
|
static struct clone clonelist[CLONE_DETECT_SIZE];
|
|
|
|
/* Which hosts have we warned about, and when? This is used to keep us
|
|
* from sending out notices over and over for clones from the same host. */
|
|
static struct clone warnings[CLONE_DETECT_SIZE];
|
|
|
|
/* List of Services administrators */
|
|
SList servadmins;
|
|
/* List of Services operators */
|
|
SList servopers;
|
|
/* AKILL, SGLINE, SQLINE and SZLINE lists */
|
|
SList akills, sglines, sqlines, szlines;
|
|
|
|
/*************************************************************************/
|
|
|
|
static void get_operserv_stats(long *nrec, long *memuse);
|
|
|
|
static int compare_adminlist_entries(SList * slist, void *item1,
|
|
void *item2);
|
|
static int compare_operlist_entries(SList * slist, void *item1,
|
|
void *item2);
|
|
static void free_adminlist_entry(SList * slist, void *item);
|
|
static void free_operlist_entry(SList * slist, void *item);
|
|
|
|
static int is_akill_entry_equal(SList * slist, void *item1, void *item2);
|
|
static void free_akill_entry(SList * slist, void *item);
|
|
#ifdef IRC_BAHAMUT
|
|
static int is_sgline_entry_equal(SList * slist, void *item1, void *item2);
|
|
static void free_sgline_entry(SList * slist, void *item);
|
|
#endif
|
|
static int is_sqline_entry_equal(SList * slist, void *item1, void *item2);
|
|
static void free_sqline_entry(SList * slist, void *item);
|
|
#ifdef IRC_BAHAMUT
|
|
static int is_szline_entry_equal(SList * slist, void *item1, void *item2);
|
|
static void free_szline_entry(SList * slist, void *item);
|
|
#endif
|
|
|
|
static int do_help(User * u);
|
|
static int do_global(User * u);
|
|
static int do_stats(User * u);
|
|
static int do_admin(User * u);
|
|
static int do_oper(User * u);
|
|
static int do_os_mode(User * u);
|
|
static int do_clearmodes(User * u);
|
|
static int do_os_kick(User * u);
|
|
static int do_akill(User * u);
|
|
static int do_sgline(User * u);
|
|
static int do_sqline(User * u);
|
|
static int do_szline(User * u);
|
|
static int do_set(User * u);
|
|
static int do_noop(User * u);
|
|
static int do_jupe(User * u);
|
|
static int do_raw(User * u);
|
|
static int do_update(User * u);
|
|
static int do_reload(User * u);
|
|
static int do_os_quit(User * u);
|
|
static int do_shutdown(User * u);
|
|
static int do_restart(User * u);
|
|
static int do_ignorelist(User * u);
|
|
static int do_clearignore(User * u);
|
|
static int do_killclones(User * u);
|
|
static int do_chanlist(User * u);
|
|
static int do_userlist(User * u);
|
|
static int do_ignoreuser(User * u);
|
|
static int do_staff(User * u);
|
|
static int do_defcon(User * u);
|
|
static int do_chankill(User * u);
|
|
static void defcon_sendlvls(User * u);
|
|
char *defconReverseModes(const char *modes);
|
|
int DefConModesSet = 0;
|
|
time_t DefContimer;
|
|
void runDefCon(void);
|
|
void resetDefCon(int level);
|
|
void oper_global(char *nick, char *fmt, ...);
|
|
|
|
#ifdef USE_MODULES
|
|
int do_modload(User * u);
|
|
int do_modunload(User * u);
|
|
int do_modlist(User * u);
|
|
int do_modinfo(User * u);
|
|
static int showModuleCmdLoaded(CommandHash * cmdList, char *mod_name,
|
|
User * u);
|
|
static int showModuleMsgLoaded(MessageHash * msgList, char *mod_name,
|
|
User * u);
|
|
#endif
|
|
|
|
|
|
#ifdef USE_OSSVS
|
|
#ifndef IRC_HYBRID
|
|
static int do_operumodes(User * u);
|
|
#endif
|
|
static int do_svsnick(User * u);
|
|
#endif
|
|
|
|
#if defined(IRC_UNREAL) && defined(USE_OSSVS)
|
|
static int do_operoline(User * u);
|
|
#endif
|
|
|
|
#ifdef DEBUG_COMMANDS
|
|
static void send_clone_lists(User * u);
|
|
static int do_matchwild(User * u);
|
|
#endif
|
|
|
|
/* OperServ restart needs access to this if were gonna avoid sending ourself a signal */
|
|
extern int do_restart_services(void);
|
|
void moduleAddOperServCmds(void);
|
|
/*************************************************************************/
|
|
|
|
/* Options for the lists */
|
|
SListOpts akopts = { 0, NULL, &is_akill_entry_equal, &free_akill_entry };
|
|
SListOpts saopts = { SLISTF_SORT, &compare_adminlist_entries, NULL,
|
|
&free_adminlist_entry
|
|
};
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
SListOpts sgopts = { 0, NULL, &is_sgline_entry_equal, &free_sgline_entry };
|
|
#endif
|
|
SListOpts soopts =
|
|
{ SLISTF_SORT, &compare_operlist_entries, NULL, &free_operlist_entry };
|
|
SListOpts sqopts =
|
|
{ SLISTF_SORT, NULL, &is_sqline_entry_equal, &free_sqline_entry };
|
|
#ifdef IRC_BAHAMUT
|
|
SListOpts szopts = { 0, NULL, &is_szline_entry_equal, &free_szline_entry };
|
|
#endif
|
|
|
|
/*************************************************************************/
|
|
/* *INDENT-OFF* */
|
|
void moduleAddOperServCmds(void) {
|
|
Command *c;
|
|
c = createCommand("HELP", do_help, NULL, -1, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("GLOBAL", do_global, NULL, OPER_HELP_GLOBAL, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("STATS", do_stats, NULL, OPER_HELP_STATS, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("UPTIME", do_stats, NULL, OPER_HELP_STATS, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
|
|
/* Anyone can use the LIST option to the ADMIN and OPER commands; those
|
|
* routines check privileges to ensure that only authorized users
|
|
* modify the list. */
|
|
c = createCommand("ADMIN", do_admin, NULL, OPER_HELP_ADMIN, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("OPER", do_oper, NULL, OPER_HELP_OPER, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("STAFF", do_staff, NULL, OPER_HELP_STAFF, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
/* Similarly, anyone can use *NEWS LIST, but *NEWS {ADD,DEL} are
|
|
* reserved for Services admins. */
|
|
c = createCommand("LOGONNEWS", do_logonnews, NULL, NEWS_HELP_LOGON, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("OPERNEWS", do_opernews, NULL, NEWS_HELP_OPER, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("RANDOMNEWS", do_randomnews, NULL, NEWS_HELP_RANDOM, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
|
|
/* Commands for Services opers: */
|
|
c = createCommand("MODE", do_os_mode, is_services_oper,OPER_HELP_MODE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("CLEARMODES", do_clearmodes, is_services_oper,OPER_HELP_CLEARMODES, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("KICK", do_os_kick, is_services_oper,OPER_HELP_KICK, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("KILLCLONES", do_killclones, is_services_oper,OPER_HELP_KILLCLONES, -1,-1,-1, -1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("AKILL", do_akill, is_services_oper,OPER_HELP_AKILL, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SGLINE", do_sgline, is_services_oper,OPER_HELP_SGLINE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SQLINE", do_sqline, is_services_oper,OPER_HELP_SQLINE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SZLINE", do_szline, is_services_oper,OPER_HELP_SZLINE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
|
|
/* Commands for Services admins: */
|
|
c = createCommand("SET", do_set, is_services_admin,OPER_HELP_SET, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SET READONLY", NULL, NULL,OPER_HELP_SET_READONLY, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SET LOGCHAN",NULL, NULL,OPER_HELP_SET_LOGCHAN, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SET DEBUG", NULL, NULL,OPER_HELP_SET_DEBUG, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SET NOEXPIRE",NULL, NULL,OPER_HELP_SET_NOEXPIRE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SET SUPERADMIN",NULL, NULL,OPER_HELP_SET_SUPERADMIN, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#ifdef USE_OSSVS
|
|
c = createCommand("SVSNICK", do_svsnick, is_services_admin,OPER_HELP_SVSNICK, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#ifndef IRC_HYBRID
|
|
c = createCommand("UMODE", do_operumodes, is_services_admin,OPER_HELP_UMODE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#endif
|
|
#endif
|
|
c = createCommand("NOOP", do_noop, is_services_admin,OPER_HELP_NOOP, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("JUPE", do_jupe, is_services_admin,OPER_HELP_JUPE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("RAW", do_raw, is_services_admin,OPER_HELP_RAW, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("IGNORE", do_ignoreuser, is_services_admin,OPER_HELP_IGNORE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#if defined(IRC_UNREAL) && defined(USE_OSSVS)
|
|
c = createCommand("OLINE", do_operoline, is_services_admin,OPER_HELP_OLINE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#endif
|
|
c = createCommand("UPDATE", do_update, is_services_admin,OPER_HELP_UPDATE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("RELOAD", do_reload, is_services_admin,OPER_HELP_RELOAD, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("QUIT", do_os_quit, is_services_admin,OPER_HELP_QUIT, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("SHUTDOWN", do_shutdown, is_services_admin,OPER_HELP_SHUTDOWN, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("RESTART", do_restart, is_services_admin,OPER_HELP_RESTART, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#ifndef STREAMLINED
|
|
c = createCommand("SESSION", do_session, is_services_admin,OPER_HELP_SESSION, -1,-1,-1, -1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("EXCEPTION", do_exception, is_services_admin,OPER_HELP_EXCEPTION, -1,-1,-1, -1); addCoreCommand(OPERSERV,c);
|
|
#endif
|
|
c = createCommand("CHANLIST", do_chanlist, is_services_admin,OPER_HELP_CHANLIST, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("USERLIST", do_userlist, is_services_admin,OPER_HELP_USERLIST, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("CACHE", do_cache, is_services_admin,OPER_HELP_CACHE, -1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("DEFCON", do_defcon, is_services_admin, OPER_HELP_DEFCON,-1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("CHANKILL", do_chankill, is_services_admin, OPER_HELP_CHANKILL,-1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
/* Commands for Services root: */
|
|
#ifdef USE_MODULES
|
|
c = createCommand("MODLOAD", do_modload, is_services_root, -1,-1,-1,-1,OPER_HELP_MODLOAD); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("MODUNLOAD", do_modunload, is_services_root, -1,-1,-1,-1,OPER_HELP_MODUNLOAD); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("MODLIST", do_modlist, is_services_root, -1,-1,-1,-1,OPER_HELP_MODLIST); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("MODINFO", do_modinfo, is_services_root, -1,-1,-1,-1,OPER_HELP_MODINFO); addCoreCommand(OPERSERV,c);
|
|
#endif
|
|
#ifdef DEBUG_COMMANDS
|
|
c = createCommand("LISTTIMERS", send_timeout_list, is_services_root, -1,-1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("MATCHWILD", do_matchwild, is_services_root, -1,-1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
c = createCommand("LISTCLONES", send_clone_lists, is_services_root, -1,-1,-1,-1,-1); addCoreCommand(OPERSERV,c);
|
|
#endif
|
|
}
|
|
|
|
/* *INDENT-ON* */
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* OperServ initialization. */
|
|
|
|
void os_init(void)
|
|
{
|
|
Command *cmd;
|
|
moduleAddOperServCmds();
|
|
cmd = findCommand(OPERSERV, "GLOBAL");
|
|
if (cmd)
|
|
cmd->help_param1 = s_GlobalNoticer;
|
|
cmd = findCommand(OPERSERV, "ADMIN");
|
|
if (cmd)
|
|
cmd->help_param1 = s_NickServ;
|
|
cmd = findCommand(OPERSERV, "OPER");
|
|
if (cmd)
|
|
cmd->help_param1 = s_NickServ;
|
|
|
|
/* Initialization of the lists */
|
|
slist_init(&servadmins);
|
|
servadmins.opts = &saopts;
|
|
slist_init(&servopers);
|
|
servopers.opts = &soopts;
|
|
|
|
slist_init(&akills);
|
|
akills.opts = &akopts;
|
|
slist_init(&sglines);
|
|
#ifdef IRC_BAHAMUT
|
|
sglines.opts = &sgopts;
|
|
#endif
|
|
slist_init(&sqlines);
|
|
sqlines.opts = &sqopts;
|
|
slist_init(&szlines);
|
|
#ifdef IRC_BAHAMUT
|
|
szlines.opts = &szopts;
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Main OperServ routine. */
|
|
|
|
void operserv(User * u, char *buf)
|
|
{
|
|
char *cmd;
|
|
char *s;
|
|
|
|
alog("%s: %s: %s", s_OperServ, u->nick, buf);
|
|
|
|
cmd = strtok(buf, " ");
|
|
if (!cmd) {
|
|
return;
|
|
} else if (stricmp(cmd, "\1PING") == 0) {
|
|
if (!(s = strtok(NULL, "")))
|
|
s = "\1";
|
|
notice(s_OperServ, u->nick, "\1PING %s", s);
|
|
} else {
|
|
mod_run_cmd(s_OperServ, u, OPERSERV, cmd);
|
|
}
|
|
}
|
|
|
|
static void get_operserv_stats(long *nrec, long *memuse)
|
|
{
|
|
int i;
|
|
long mem = 0, count = 0, mem2 = 0, count2 = 0;
|
|
Akill *ak;
|
|
SXLine *sx;
|
|
|
|
if (CheckClones) {
|
|
mem = sizeof(struct clone) * CLONE_DETECT_SIZE * 2;
|
|
for (i = 0; i < CLONE_DETECT_SIZE; i++) {
|
|
if (clonelist[i].host) {
|
|
count++;
|
|
mem += strlen(clonelist[i].host) + 1;
|
|
}
|
|
if (warnings[i].host) {
|
|
count++;
|
|
mem += strlen(warnings[i].host) + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
count += akills.count;
|
|
mem += akills.capacity * sizeof(void);
|
|
mem += akills.count * sizeof(Akill);
|
|
|
|
for (i = 0; i < akills.count; i++) {
|
|
ak = akills.list[i];
|
|
mem += strlen(ak->user) + 1;
|
|
mem += strlen(ak->host) + 1;
|
|
mem += strlen(ak->by) + 1;
|
|
mem += strlen(ak->reason) + 1;
|
|
}
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
|
|
count += sglines.count;
|
|
mem += sglines.capacity * sizeof(void);
|
|
mem += sglines.count * sizeof(SXLine);
|
|
|
|
for (i = 0; i < sglines.count; i++) {
|
|
sx = sglines.list[i];
|
|
mem += strlen(sx->mask) + 1;
|
|
mem += strlen(sx->by) + 1;
|
|
mem += strlen(sx->reason) + 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
count += sqlines.count;
|
|
mem += sqlines.capacity * sizeof(void);
|
|
mem += sqlines.count * sizeof(SXLine);
|
|
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
sx = sqlines.list[i];
|
|
mem += strlen(sx->mask) + 1;
|
|
mem += strlen(sx->by) + 1;
|
|
mem += strlen(sx->reason) + 1;
|
|
}
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
|
|
count += szlines.count;
|
|
mem += szlines.capacity * sizeof(void);
|
|
mem += szlines.count * sizeof(SXLine);
|
|
|
|
for (i = 0; i < szlines.count; i++) {
|
|
sx = szlines.list[i];
|
|
mem += strlen(sx->mask) + 1;
|
|
mem += strlen(sx->by) + 1;
|
|
mem += strlen(sx->reason) + 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
get_news_stats(&count2, &mem2);
|
|
count += count2;
|
|
mem += mem2;
|
|
get_exception_stats(&count2, &mem2);
|
|
count += count2;
|
|
mem += mem2;
|
|
|
|
*nrec = count;
|
|
*memuse = mem;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/**************************** Privilege checks ***************************/
|
|
/*************************************************************************/
|
|
|
|
/* Load old AKILL data. */
|
|
|
|
#define SAFE(x) do { \
|
|
if ((x) < 0) { \
|
|
if (!forceload) \
|
|
fatal("Read error on %s", AutokillDBName); \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
static void load_old_akill(void)
|
|
{
|
|
dbFILE *f;
|
|
int i, j;
|
|
int16 tmp16;
|
|
int32 tmp32;
|
|
char buf[NICKMAX], mask2[BUFSIZE], *mask, *s;
|
|
Akill *ak, *entry;
|
|
|
|
if (!
|
|
(f =
|
|
open_db("AKILL", AutokillDBName ? AutokillDBName : "akill.db",
|
|
"r", 9)))
|
|
return;
|
|
|
|
get_file_version(f);
|
|
|
|
read_int16(&tmp16, f);
|
|
slist_setcapacity(&akills, tmp16);
|
|
|
|
for (j = 0; j < akills.capacity; j++) {
|
|
ak = scalloc(sizeof(Akill), 1);
|
|
|
|
SAFE(read_string(&mask, f));
|
|
s = strchr(mask, '@');
|
|
*s = 0;
|
|
s++;
|
|
ak->user = sstrdup(mask);
|
|
ak->host = sstrdup(s);
|
|
SAFE(read_string(&ak->reason, f));
|
|
SAFE(read_buffer(buf, f));
|
|
if (!*buf)
|
|
ak->by = sstrdup("<unknown>");
|
|
else
|
|
ak->by = sstrdup(buf);
|
|
SAFE(read_int32(&tmp32, f));
|
|
ak->seton = tmp32 ? tmp32 : time(NULL);
|
|
SAFE(read_int32(&tmp32, f));
|
|
ak->expires = tmp32;
|
|
|
|
/* Sanity checks *sigh* */
|
|
|
|
/* No nicknames allowed! */
|
|
if (strchr(ak->user, '!')) {
|
|
s_rakill(ak->user, ak->host);
|
|
free(ak);
|
|
continue;
|
|
}
|
|
|
|
snprintf(mask2, sizeof(mask2), "%s@%s", ak->user, ak->host);
|
|
|
|
/* Is the mask already in the AKILL list? */
|
|
if (slist_indexof(&akills, mask2) != -1) {
|
|
free(ak);
|
|
continue;
|
|
}
|
|
|
|
/* Checks whether there is an AKILL that already covers
|
|
* the one we want to add, and whether there are AKILLs
|
|
* that would be covered by this one. Expiry time
|
|
* does *also* matter.
|
|
*/
|
|
|
|
if (akills.count > 0) {
|
|
|
|
for (i = akills.count - 1; i >= 0; i--) {
|
|
|
|
char amask[BUFSIZE];
|
|
|
|
entry = akills.list[i];
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
snprintf(amask, sizeof(amask), "%s@%s", entry->user,
|
|
entry->host);
|
|
|
|
if (match_wild_nocase(amask, mask2)
|
|
&& (entry->expires >= ak->expires
|
|
|| entry->expires == 0)) {
|
|
s_rakill(ak->user, ak->host);
|
|
free(ak);
|
|
ak = NULL;
|
|
break;
|
|
}
|
|
|
|
if (match_wild_nocase(mask2, amask)
|
|
&& (entry->expires <= ak->expires || ak->expires == 0))
|
|
slist_delete(&akills, i);
|
|
}
|
|
|
|
}
|
|
|
|
if (ak)
|
|
slist_add(&akills, ak);
|
|
}
|
|
|
|
close_db(f);
|
|
}
|
|
|
|
#undef SAFE
|
|
|
|
/* Load OperServ data. */
|
|
|
|
#define SAFE(x) do { \
|
|
if ((x) < 0) { \
|
|
if (!forceload) \
|
|
fatal("Read error on %s", OperDBName); \
|
|
failed = 1; \
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
void load_os_dbase(void)
|
|
{
|
|
dbFILE *f;
|
|
int16 i, n, ver, c;
|
|
HostCache *hc, **hclast, *hcprev;
|
|
int16 tmp16;
|
|
int32 tmp32;
|
|
char *s;
|
|
int failed = 0;
|
|
|
|
if (!(f = open_db(s_OperServ, OperDBName, "r", OPER_VERSION)))
|
|
return;
|
|
|
|
ver = get_file_version(f);
|
|
|
|
if (ver <= 9) {
|
|
NickAlias *na;
|
|
|
|
SAFE(read_int16(&n, f));
|
|
for (i = 0; i < n && !failed; i++) {
|
|
SAFE(read_string(&s, f));
|
|
if (s) {
|
|
na = findnick(s);
|
|
if (na) {
|
|
na->nc->flags |= NI_SERVICES_ADMIN;
|
|
if (slist_indexof(&servadmins, na) == -1)
|
|
slist_add(&servadmins, na);
|
|
}
|
|
free(s);
|
|
}
|
|
}
|
|
if (!failed)
|
|
SAFE(read_int16(&n, f));
|
|
for (i = 0; i < n && !failed; i++) {
|
|
SAFE(read_string(&s, f));
|
|
if (s) {
|
|
na = findnick(s);
|
|
if (na) {
|
|
na->nc->flags |= NI_SERVICES_OPER;
|
|
if (slist_indexof(&servopers, na) == -1)
|
|
slist_add(&servopers, na);
|
|
}
|
|
free(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ver >= 7) {
|
|
int32 tmp32;
|
|
SAFE(read_int32(&maxusercnt, f));
|
|
SAFE(read_int32(&tmp32, f));
|
|
maxusertime = tmp32;
|
|
}
|
|
|
|
if (ver <= 10)
|
|
load_old_akill();
|
|
else {
|
|
Akill *ak;
|
|
|
|
read_int16(&tmp16, f);
|
|
slist_setcapacity(&akills, tmp16);
|
|
|
|
for (i = 0; i < akills.capacity; i++) {
|
|
ak = scalloc(sizeof(Akill), 1);
|
|
|
|
SAFE(read_string(&ak->user, f));
|
|
SAFE(read_string(&ak->host, f));
|
|
SAFE(read_string(&ak->by, f));
|
|
SAFE(read_string(&ak->reason, f));
|
|
SAFE(read_int32(&tmp32, f));
|
|
ak->seton = tmp32;
|
|
SAFE(read_int32(&tmp32, f));
|
|
ak->expires = tmp32;
|
|
|
|
slist_add(&akills, ak);
|
|
}
|
|
}
|
|
|
|
if (ver >= 11) {
|
|
SXLine *sx;
|
|
|
|
read_int16(&tmp16, f);
|
|
slist_setcapacity(&sglines, tmp16);
|
|
|
|
for (i = 0; i < sglines.capacity; i++) {
|
|
sx = scalloc(sizeof(SXLine), 1);
|
|
|
|
SAFE(read_string(&sx->mask, f));
|
|
SAFE(read_string(&sx->by, f));
|
|
SAFE(read_string(&sx->reason, f));
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->seton = tmp32;
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->expires = tmp32;
|
|
|
|
slist_add(&sglines, sx);
|
|
}
|
|
|
|
if (ver >= 13) {
|
|
read_int16(&tmp16, f);
|
|
slist_setcapacity(&sqlines, tmp16);
|
|
|
|
for (i = 0; i < sqlines.capacity; i++) {
|
|
sx = scalloc(sizeof(SXLine), 1);
|
|
|
|
SAFE(read_string(&sx->mask, f));
|
|
SAFE(read_string(&sx->by, f));
|
|
SAFE(read_string(&sx->reason, f));
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->seton = tmp32;
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->expires = tmp32;
|
|
|
|
slist_add(&sqlines, sx);
|
|
}
|
|
}
|
|
|
|
read_int16(&tmp16, f);
|
|
slist_setcapacity(&szlines, tmp16);
|
|
|
|
for (i = 0; i < szlines.capacity; i++) {
|
|
sx = scalloc(sizeof(SXLine), 1);
|
|
|
|
SAFE(read_string(&sx->mask, f));
|
|
SAFE(read_string(&sx->by, f));
|
|
SAFE(read_string(&sx->reason, f));
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->seton = tmp32;
|
|
SAFE(read_int32(&tmp32, f));
|
|
sx->expires = tmp32;
|
|
|
|
slist_add(&szlines, sx);
|
|
}
|
|
}
|
|
|
|
if (ver >= 12) {
|
|
for (i = 0; i < 1024 && !failed; i++) {
|
|
hclast = &hcache[i];
|
|
hcprev = NULL;
|
|
|
|
while ((c = getc_db(f)) != 0) {
|
|
if (c != 1)
|
|
fatal("Invalid format in %s", OperDBName);
|
|
|
|
hc = scalloc(1, sizeof(HostCache));
|
|
|
|
SAFE(read_string(&hc->host, f));
|
|
SAFE(read_int16(&tmp16, f));
|
|
hc->status = tmp16;
|
|
SAFE(read_int32(&tmp32, f));
|
|
hc->used = tmp32;
|
|
|
|
*hclast = hc;
|
|
hclast = &hc->next;
|
|
hc->prev = hcprev;
|
|
hcprev = hc;
|
|
} /* while (getc_db(f) != 0) */
|
|
|
|
*hclast = NULL;
|
|
} /* for (i) */
|
|
}
|
|
|
|
close_db(f);
|
|
|
|
}
|
|
|
|
#undef SAFE
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Save OperServ data. */
|
|
|
|
#define SAFE(x) do { \
|
|
if ((x) < 0) { \
|
|
restore_db(f); \
|
|
log_perror("Write error on %s", OperDBName); \
|
|
if (time(NULL) - lastwarn > WarningTimeout) { \
|
|
wallops(NULL, "Write error on %s: %s", OperDBName, \
|
|
strerror(errno)); \
|
|
lastwarn = time(NULL); \
|
|
} \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
void save_os_dbase(void)
|
|
{
|
|
int i;
|
|
dbFILE *f;
|
|
static time_t lastwarn = 0;
|
|
Akill *ak;
|
|
SXLine *sx;
|
|
HostCache *hc;
|
|
|
|
if (!(f = open_db(s_OperServ, OperDBName, "w", OPER_VERSION)))
|
|
return;
|
|
SAFE(write_int32(maxusercnt, f));
|
|
SAFE(write_int32(maxusertime, f));
|
|
|
|
SAFE(write_int16(akills.count, f));
|
|
for (i = 0; i < akills.count; i++) {
|
|
ak = akills.list[i];
|
|
|
|
SAFE(write_string(ak->user, f));
|
|
SAFE(write_string(ak->host, f));
|
|
SAFE(write_string(ak->by, f));
|
|
SAFE(write_string(ak->reason, f));
|
|
SAFE(write_int32(ak->seton, f));
|
|
SAFE(write_int32(ak->expires, f));
|
|
}
|
|
|
|
SAFE(write_int16(sglines.count, f));
|
|
for (i = 0; i < sglines.count; i++) {
|
|
sx = sglines.list[i];
|
|
|
|
SAFE(write_string(sx->mask, f));
|
|
SAFE(write_string(sx->by, f));
|
|
SAFE(write_string(sx->reason, f));
|
|
SAFE(write_int32(sx->seton, f));
|
|
SAFE(write_int32(sx->expires, f));
|
|
}
|
|
|
|
SAFE(write_int16(sqlines.count, f));
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
sx = sqlines.list[i];
|
|
|
|
SAFE(write_string(sx->mask, f));
|
|
SAFE(write_string(sx->by, f));
|
|
SAFE(write_string(sx->reason, f));
|
|
SAFE(write_int32(sx->seton, f));
|
|
SAFE(write_int32(sx->expires, f));
|
|
}
|
|
|
|
SAFE(write_int16(szlines.count, f));
|
|
for (i = 0; i < szlines.count; i++) {
|
|
sx = szlines.list[i];
|
|
|
|
SAFE(write_string(sx->mask, f));
|
|
SAFE(write_string(sx->by, f));
|
|
SAFE(write_string(sx->reason, f));
|
|
SAFE(write_int32(sx->seton, f));
|
|
SAFE(write_int32(sx->expires, f));
|
|
}
|
|
|
|
for (i = 0; i < 1024; i++) {
|
|
for (hc = hcache[i]; hc; hc = hc->next) {
|
|
/* Don't save in-progress scans */
|
|
if (hc->status < HC_NORMAL)
|
|
continue;
|
|
|
|
SAFE(write_int8(1, f));
|
|
|
|
SAFE(write_string(hc->host, f));
|
|
SAFE(write_int16(hc->status, f));
|
|
SAFE(write_int32(hc->used, f));
|
|
|
|
} /* for (hc) */
|
|
SAFE(write_int8(0, f));
|
|
} /* for (i) */
|
|
|
|
close_db(f);
|
|
|
|
}
|
|
|
|
#undef SAFE
|
|
|
|
/*************************************************************************/
|
|
|
|
void save_os_rdb_dbase(void)
|
|
{
|
|
#ifdef USE_RDB
|
|
if (!rdb_open())
|
|
return;
|
|
rdb_save_os_db(maxusercnt, maxusertime, &akills, &sglines, &sqlines,
|
|
&szlines, hcache[0]);
|
|
rdb_close();
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Removes the nick structure from OperServ lists. */
|
|
|
|
void os_remove_nick(NickCore * nc)
|
|
{
|
|
slist_remove(&servadmins, nc);
|
|
slist_remove(&servopers, nc);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Does the given user have Services root privileges?
|
|
Now enhanced. */
|
|
|
|
int is_services_root(User * u)
|
|
{
|
|
if ((NSStrictPrivileges && !is_oper(u))
|
|
|| (!skeleton && !nick_identified(u)))
|
|
return 0;
|
|
if (skeleton || (u->na->nc->flags & NI_SERVICES_ROOT))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Does the given user have Services admin privileges? */
|
|
|
|
int is_services_admin(User * u)
|
|
{
|
|
if ((NSStrictPrivileges && !is_oper(u))
|
|
|| (!skeleton && !nick_identified(u)))
|
|
return 0;
|
|
if (skeleton
|
|
|| (u->na->nc->flags & (NI_SERVICES_ADMIN | NI_SERVICES_ROOT)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Does the given user have Services oper privileges? */
|
|
|
|
int is_services_oper(User * u)
|
|
{
|
|
if ((NSStrictPrivileges && !is_oper(u))
|
|
|| (!skeleton && !nick_identified(u)))
|
|
return 0;
|
|
if (skeleton
|
|
|| (u->na->nc->
|
|
flags & (NI_SERVICES_OPER | NI_SERVICES_ADMIN |
|
|
NI_SERVICES_ROOT)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Is the given nick a Services root nick? */
|
|
|
|
int nick_is_services_root(NickCore * nc)
|
|
{
|
|
if (nc->flags & (NI_SERVICES_ROOT))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Is the given nick a Services admin/root nick? */
|
|
|
|
int nick_is_services_admin(NickCore * nc)
|
|
{
|
|
if (nc->flags & (NI_SERVICES_ADMIN | NI_SERVICES_ROOT))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Is the given nick a Services oper/admin/root nick? */
|
|
|
|
int nick_is_services_oper(NickCore * nc)
|
|
{
|
|
if (nc->
|
|
flags & (NI_SERVICES_OPER | NI_SERVICES_ADMIN | NI_SERVICES_ROOT))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/**************************** Clone detection ****************************/
|
|
/*************************************************************************/
|
|
|
|
/* We just got a new user; does it look like a clone? If so, send out a
|
|
* wallops.
|
|
*/
|
|
|
|
void check_clones(User * user)
|
|
{
|
|
#ifndef STREAMLINED
|
|
int i, clone_count;
|
|
long last_time;
|
|
|
|
if (!CheckClones)
|
|
return;
|
|
|
|
if (clonelist[0].host)
|
|
free(clonelist[0].host);
|
|
i = CLONE_DETECT_SIZE - 1;
|
|
memmove(clonelist, clonelist + 1, sizeof(struct clone) * i);
|
|
clonelist[i].host = sstrdup(GetHost(user));
|
|
last_time = clonelist[i].time = time(NULL);
|
|
clone_count = 1;
|
|
while (--i >= 0 && clonelist[i].host) {
|
|
if (clonelist[i].time < last_time - CloneMaxDelay)
|
|
break;
|
|
if (stricmp(clonelist[i].host, GetHost(user)) == 0) {
|
|
++clone_count;
|
|
last_time = clonelist[i].time;
|
|
if (clone_count >= CloneMinUsers)
|
|
break;
|
|
}
|
|
}
|
|
if (clone_count >= CloneMinUsers) {
|
|
/* Okay, we have clones. Check first to see if we already know
|
|
* about them. */
|
|
for (i = CLONE_DETECT_SIZE - 1; i >= 0 && warnings[i].host; --i) {
|
|
if (stricmp(warnings[i].host, GetHost(user)) == 0)
|
|
break;
|
|
}
|
|
if (i < 0
|
|
|| warnings[i].time < user->my_signon - CloneWarningDelay) {
|
|
/* Send out the warning, and note it. */
|
|
wallops(s_OperServ,
|
|
"\2WARNING\2 - possible clones detected from %s",
|
|
GetHost(user));
|
|
alog("%s: possible clones detected from %s", s_OperServ,
|
|
GetHost(user));
|
|
i = CLONE_DETECT_SIZE - 1;
|
|
if (warnings[0].host)
|
|
free(warnings[0].host);
|
|
memmove(warnings, warnings + 1, sizeof(struct clone) * i);
|
|
warnings[i].host = sstrdup(GetHost(user));
|
|
warnings[i].time = clonelist[i].time;
|
|
if (KillClones)
|
|
kill_user(s_OperServ, user->nick, "Clone kill");
|
|
}
|
|
}
|
|
#endif /* !STREAMLINED */
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
#ifdef DEBUG_COMMANDS
|
|
|
|
/* Send clone arrays to given nick. */
|
|
|
|
static void send_clone_lists(User * u)
|
|
{
|
|
int i;
|
|
|
|
if (!CheckClones) {
|
|
notice(s_OperServ, u->nick, "CheckClones not enabled.");
|
|
return;
|
|
}
|
|
|
|
notice(s_OperServ, u->nick, "clonelist[]");
|
|
for (i = 0; i < CLONE_DETECT_SIZE; i++) {
|
|
if (clonelist[i].host)
|
|
notice(s_OperServ, u->nick, " %10ld %s", clonelist[i].time,
|
|
clonelist[i].host ? clonelist[i].host : "(null)");
|
|
}
|
|
notice(s_OperServ, u->nick, "warnings[]");
|
|
for (i = 0; i < CLONE_DETECT_SIZE; i++) {
|
|
if (clonelist[i].host)
|
|
notice(s_OperServ, u->nick, " %10ld %s", warnings[i].time,
|
|
warnings[i].host ? warnings[i].host : "(null)");
|
|
}
|
|
}
|
|
|
|
#endif /* DEBUG_COMMANDS */
|
|
|
|
/*************************************************************************/
|
|
/*********************** OperServ command functions **********************/
|
|
/*************************************************************************/
|
|
|
|
/* HELP command. */
|
|
|
|
static int do_help(User * u)
|
|
{
|
|
const char *cmd = strtok(NULL, "");
|
|
|
|
if (!cmd) {
|
|
notice_help(s_OperServ, u, OPER_HELP);
|
|
if (is_services_oper(u))
|
|
notice_help(s_OperServ, u, OPER_HELP_OPER_CMD);
|
|
if (is_services_admin(u))
|
|
notice_help(s_OperServ, u, OPER_HELP_ADMIN_CMD);
|
|
#ifdef USE_MODULES
|
|
if (is_services_root(u))
|
|
notice_help(s_OperServ, u, OPER_HELP_ROOT_CMD);
|
|
#endif
|
|
moduleDisplayHelp(5, u);
|
|
notice_help(s_OperServ, u, OPER_HELP_LOGGED);
|
|
} else {
|
|
mod_help_cmd(s_OperServ, u, OPERSERV, cmd);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Global notice sending via GlobalNoticer. */
|
|
/* Added name tag for globalmsg -Certus */
|
|
|
|
static int do_global(User * u)
|
|
{
|
|
char *msg = strtok(NULL, "");
|
|
|
|
if (!msg) {
|
|
syntax_error(s_OperServ, u, "GLOBAL", OPER_GLOBAL_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
if (WallOSGlobal)
|
|
wallops(s_OperServ, "\2%s\2 just used GLOBAL command.", u->nick);
|
|
oper_global(u->nick, msg);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
void oper_global(char *nick, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char msg[2048]; /* largest valid message is 512, this should cover any global */
|
|
int i;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(msg, sizeof(msg), fmt, args);
|
|
va_end(args);
|
|
|
|
#ifdef IRC_HYBRID
|
|
if (DomainNumber > 0) {
|
|
for (i = 0; i < DomainNumber; i++) {
|
|
if ((nick) && (!AnonymousGlobal)) {
|
|
send_cmd(s_GlobalNoticer, "NOTICE $$*.%s :[%s] %s",
|
|
NetworkDomains[i], nick, msg);
|
|
} else {
|
|
send_cmd(s_GlobalNoticer, "NOTICE $$*.%s :%s",
|
|
NetworkDomains[i], msg);
|
|
}
|
|
}
|
|
} else {
|
|
/* Go through all common top-level domains. If you have others,
|
|
* add them here.
|
|
*/
|
|
if ((nick) && (!AnonymousGlobal)) {
|
|
notice(s_GlobalNoticer, "$$*.com", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$$*.net", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$$*.org", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$$*.edu", "[%s] %s", nick, msg);
|
|
} else {
|
|
notice(s_GlobalNoticer, "$$*.com", "%s", msg);
|
|
notice(s_GlobalNoticer, "$$*.net", "%s", msg);
|
|
notice(s_GlobalNoticer, "$$*.org", "%s", msg);
|
|
notice(s_GlobalNoticer, "$$*.edu", "%s", msg);
|
|
}
|
|
}
|
|
#else
|
|
if (DomainNumber > 0) {
|
|
for (i = 0; i < DomainNumber; i++) {
|
|
if ((nick) && (!AnonymousGlobal)) {
|
|
send_cmd(s_GlobalNoticer, "NOTICE $*.%s :[%s] %s",
|
|
NetworkDomains[i], nick, msg);
|
|
} else {
|
|
send_cmd(s_GlobalNoticer, "NOTICE $*.%s :%s",
|
|
NetworkDomains[i], msg);
|
|
}
|
|
}
|
|
} else {
|
|
/* Go through all common top-level domains. If you have others,
|
|
* add them here.
|
|
*/
|
|
if ((nick) && (!AnonymousGlobal)) {
|
|
notice(s_GlobalNoticer, "$*.com", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$*.net", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$*.org", "[%s] %s", nick, msg);
|
|
notice(s_GlobalNoticer, "$*.edu", "[%s] %s", nick, msg);
|
|
} else {
|
|
notice(s_GlobalNoticer, "$*.com", "%s", msg);
|
|
notice(s_GlobalNoticer, "$*.net", "%s", msg);
|
|
notice(s_GlobalNoticer, "$*.org", "%s", msg);
|
|
notice(s_GlobalNoticer, "$*.edu", "%s", msg);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* STATS command. */
|
|
|
|
static int do_stats(User * u)
|
|
{
|
|
time_t uptime = time(NULL) - start_time;
|
|
char *extra = strtok(NULL, "");
|
|
int days = uptime / 86400, hours = (uptime / 3600) % 24,
|
|
mins = (uptime / 60) % 60, secs = uptime % 60;
|
|
struct tm *tm;
|
|
char timebuf[64];
|
|
|
|
if (extra && stricmp(extra, "ALL") != 0) {
|
|
if (stricmp(extra, "AKILL") == 0) {
|
|
int timeout;
|
|
/* AKILLs */
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_COUNT,
|
|
akills.count);
|
|
timeout = AutokillExpiry + 59;
|
|
if (timeout >= 172800)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_DAYS,
|
|
timeout / 86400);
|
|
else if (timeout >= 86400)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_DAY);
|
|
else if (timeout >= 7200)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_HOURS,
|
|
timeout / 3600);
|
|
else if (timeout >= 3600)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_HOUR);
|
|
else if (timeout >= 120)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_MINS,
|
|
timeout / 60);
|
|
else if (timeout >= 60)
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_MIN);
|
|
else
|
|
notice_lang(s_OperServ, u, OPER_STATS_AKILL_EXPIRE_NONE);
|
|
#ifdef IRC_BAHAMUT
|
|
/* SGLINEs */
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_COUNT,
|
|
sglines.count);
|
|
timeout = SGLineExpiry + 59;
|
|
if (timeout >= 172800)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_DAYS,
|
|
timeout / 86400);
|
|
else if (timeout >= 86400)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_DAY);
|
|
else if (timeout >= 7200)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_HOURS,
|
|
timeout / 3600);
|
|
else if (timeout >= 3600)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_HOUR);
|
|
else if (timeout >= 120)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_MINS,
|
|
timeout / 60);
|
|
else if (timeout >= 60)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_MIN);
|
|
else
|
|
notice_lang(s_OperServ, u, OPER_STATS_SGLINE_EXPIRE_NONE);
|
|
#endif
|
|
/* SQLINEs */
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_COUNT,
|
|
sqlines.count);
|
|
timeout = SQLineExpiry + 59;
|
|
if (timeout >= 172800)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_DAYS,
|
|
timeout / 86400);
|
|
else if (timeout >= 86400)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_DAY);
|
|
else if (timeout >= 7200)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_HOURS,
|
|
timeout / 3600);
|
|
else if (timeout >= 3600)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_HOUR);
|
|
else if (timeout >= 120)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_MINS,
|
|
timeout / 60);
|
|
else if (timeout >= 60)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_MIN);
|
|
else
|
|
notice_lang(s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_NONE);
|
|
#ifdef IRC_BAHAMUT
|
|
/* SZLINEs */
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_COUNT,
|
|
szlines.count);
|
|
timeout = SZLineExpiry + 59;
|
|
if (timeout >= 172800)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_DAYS,
|
|
timeout / 86400);
|
|
else if (timeout >= 86400)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_DAY);
|
|
else if (timeout >= 7200)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_HOURS,
|
|
timeout / 3600);
|
|
else if (timeout >= 3600)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_HOUR);
|
|
else if (timeout >= 120)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_MINS,
|
|
timeout / 60);
|
|
else if (timeout >= 60)
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_MIN);
|
|
else
|
|
notice_lang(s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_NONE);
|
|
#endif
|
|
return MOD_CONT;
|
|
} else if (!stricmp(extra, "RESET")) {
|
|
if (is_services_admin(u)) {
|
|
maxusercnt = usercnt;
|
|
notice_lang(s_OperServ, u, OPER_STATS_RESET);
|
|
} else {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
}
|
|
return MOD_CONT;
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UNKNOWN_OPTION, extra);
|
|
}
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_STATS_CURRENT_USERS, usercnt, opcnt);
|
|
tm = localtime(&maxusertime);
|
|
strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT,
|
|
tm);
|
|
notice_lang(s_OperServ, u, OPER_STATS_MAX_USERS, maxusercnt, timebuf);
|
|
if (days > 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_DHMS,
|
|
days, hours, mins, secs);
|
|
} else if (days == 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1DHMS,
|
|
days, hours, mins, secs);
|
|
} else {
|
|
if (hours > 1) {
|
|
if (mins != 1) {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_HMS,
|
|
hours, mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_HM1S,
|
|
hours, mins, secs);
|
|
}
|
|
} else {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_H1MS,
|
|
hours, mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_H1M1S,
|
|
hours, mins, secs);
|
|
}
|
|
}
|
|
} else if (hours == 1) {
|
|
if (mins != 1) {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1HMS,
|
|
hours, mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1HM1S,
|
|
hours, mins, secs);
|
|
}
|
|
} else {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1H1MS,
|
|
hours, mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1H1M1S,
|
|
hours, mins, secs);
|
|
}
|
|
}
|
|
} else {
|
|
if (mins != 1) {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_MS,
|
|
mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_M1S,
|
|
mins, secs);
|
|
}
|
|
} else {
|
|
if (secs != 1) {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1MS,
|
|
mins, secs);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_STATS_UPTIME_1M1S,
|
|
mins, secs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (extra && stricmp(extra, "ALL") == 0 && is_services_admin(u)) {
|
|
long count, mem;
|
|
|
|
notice_lang(s_OperServ, u, OPER_STATS_BYTES_READ,
|
|
total_read / 1024);
|
|
notice_lang(s_OperServ, u, OPER_STATS_BYTES_WRITTEN,
|
|
total_written / 1024);
|
|
|
|
get_user_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_USER_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_channel_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_CHANNEL_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_core_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_GROUPS_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_aliases_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_ALIASES_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_chanserv_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_CHANSERV_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_botserv_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_BOTSERV_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_operserv_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_OPERSERV_MEM, count,
|
|
(mem + 512) / 1024);
|
|
get_session_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_SESSIONS_MEM, count,
|
|
(mem + 512) / 1024);
|
|
#ifdef USE_THREADS
|
|
if (ProxyDetect) {
|
|
get_proxy_stats(&count, &mem);
|
|
notice_lang(s_OperServ, u, OPER_STATS_PROXY_MEM, count,
|
|
(mem + 512) / 1024);
|
|
}
|
|
#endif
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* make Services ignore users for a certain time */
|
|
|
|
static int do_ignoreuser(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
int t;
|
|
|
|
if (!cmd) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!stricmp(cmd, "ADD")) {
|
|
|
|
char *time = strtok(NULL, " ");
|
|
char *nick = strtok(NULL, " ");
|
|
char *rest = strtok(NULL, "");
|
|
|
|
if (!nick) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_SYNTAX);
|
|
return MOD_CONT;
|
|
} else if (!time) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_SYNTAX);
|
|
return MOD_CONT;
|
|
} else {
|
|
t = dotime(time);
|
|
rest = NULL;
|
|
|
|
if (t <= -1) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_VALID_TIME);
|
|
return MOD_CONT;
|
|
} else if (t == 0) {
|
|
t = 157248000; /* if 0 is given, we set time to 157248000 seconds == 5 years (let's hope the next restart will be before that time ;-)) */
|
|
add_ignore(nick, t);
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_PERM_DONE, nick);
|
|
} else {
|
|
add_ignore(nick, t);
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_TIME_DONE, nick,
|
|
time);
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
do_ignorelist(u);
|
|
}
|
|
|
|
else if (!stricmp(cmd, "DEL")) {
|
|
char *nick = strtok(NULL, " ");
|
|
if (!nick) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_SYNTAX);
|
|
} else {
|
|
if (get_ignore(nick) == 0) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_LIST_NOMATCH, nick);
|
|
return MOD_CONT;
|
|
} else {
|
|
delete_ignore(nick);
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_DEL_DONE, nick);
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
do_clearignore(u);
|
|
|
|
} else
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* deletes a nick from the ignore list */
|
|
|
|
void delete_ignore(const char *nick)
|
|
{
|
|
IgnoreData *ign, *prev;
|
|
IgnoreData **whichlist = &ignore[tolower(nick[0])];
|
|
|
|
for (ign = *whichlist, prev = NULL; ign; prev = ign, ign = ign->next) {
|
|
if (stricmp(ign->who, nick) == 0)
|
|
break;
|
|
}
|
|
if (prev)
|
|
prev->next = ign->next;
|
|
else
|
|
*whichlist = ign->next;
|
|
free(ign);
|
|
ign = NULL;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* shows the Services ignore list */
|
|
|
|
static int do_ignorelist(User * u)
|
|
{
|
|
int sent_header = 0;
|
|
IgnoreData *id;
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
for (id = ignore[i]; id; id = id->next) {
|
|
if (!sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_LIST);
|
|
sent_header = 1;
|
|
}
|
|
notice(s_OperServ, u->nick, "%s", id->who);
|
|
}
|
|
}
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/* Cleares the Services ignore list */
|
|
|
|
static int do_clearignore(User * u)
|
|
{
|
|
IgnoreData *id = NULL, *next = NULL;
|
|
int i;
|
|
for (i = 0; i < 256; i++) {
|
|
for (id = ignore[i]; id; id = next) {
|
|
next = id->next;
|
|
free(id);
|
|
if (!next) {
|
|
ignore[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
notice_lang(s_OperServ, u, OPER_IGNORE_LIST_CLEARED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
/* Channel mode changing (MODE command). */
|
|
|
|
static int do_os_mode(User * u)
|
|
{
|
|
int ac;
|
|
char **av;
|
|
char *chan = strtok(NULL, " "), *modes = strtok(NULL, "");
|
|
Channel *c;
|
|
|
|
if (!chan || !modes) {
|
|
syntax_error(s_OperServ, u, "MODE", OPER_MODE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!(c = findchan(chan))) {
|
|
notice_lang(s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
|
|
} else if (c->bouncy_modes) {
|
|
notice_lang(s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
|
|
return MOD_CONT;
|
|
#ifdef CMODE_A
|
|
} else if ((!is_services_admin(u)) && (c->mode & CMODE_A)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
#endif
|
|
} else {
|
|
send_cmd(s_OperServ, "MODE %s %s", chan, modes);
|
|
|
|
ac = split_buf(modes, &av, 1);
|
|
chan_set_modes(s_OperServ, c, ac, av, 0);
|
|
|
|
if (WallOSMode)
|
|
wallops(s_OperServ, "%s used MODE %s on %s", u->nick, modes,
|
|
chan);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
/**
|
|
* Change any user's UMODES
|
|
*
|
|
* modified to be part of the SuperAdmin directive -jester
|
|
* check user flag for SuperAdmin -rob
|
|
*/
|
|
#ifdef USE_OSSVS
|
|
#ifndef IRC_HYBRID
|
|
static int do_operumodes(User * u)
|
|
{
|
|
char *nick = strtok(NULL, " ");
|
|
char *modes = strtok(NULL, "");
|
|
|
|
User *u2;
|
|
|
|
/* Only allow this if SuperAdmin is enabled */
|
|
if (!u->isSuperAdmin) {
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_ONLY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!nick || !modes) {
|
|
syntax_error(s_OperServ, u, "UMODE", OPER_UMODE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**
|
|
* Only accept a +/- mode string
|
|
*-rob
|
|
**/
|
|
if ((modes[0] != '+') && (modes[0] != '-')) {
|
|
syntax_error(s_OperServ, u, "UMODE", OPER_UMODE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
if (!(u2 = finduser(nick))) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_IN_USE, nick);
|
|
} else {
|
|
send_cmd(s_OperServ, "MODE %s %s", nick, modes);
|
|
|
|
change_user_mode(u2, modes, NULL);
|
|
|
|
notice_lang(s_OperServ, u, OPER_UMODE_SUCCESS, nick);
|
|
notice_lang(s_OperServ, u2, OPER_UMODE_CHANGED, u->nick);
|
|
|
|
if (WallOSMode)
|
|
wallops(s_OperServ, "\2%s\2 used UMODE on %s", u->nick, nick);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
#endif
|
|
#endif
|
|
/**************************************************************************/
|
|
|
|
/**
|
|
* give Operflags to any user
|
|
*
|
|
* modified to be part of the SuperAdmin directive -jester
|
|
* check u-> for SuperAdmin -rob
|
|
*/
|
|
#if defined (IRC_UNREAL) && defined (USE_OSSVS)
|
|
|
|
static int do_operoline(User * u)
|
|
{
|
|
char *nick = strtok(NULL, " ");
|
|
char *flags = strtok(NULL, "");
|
|
User *u2 = NULL;
|
|
|
|
/* Only allow this if SuperAdmin is enabled */
|
|
if (!u->isSuperAdmin) {
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_ONLY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!nick || !flags) {
|
|
syntax_error(s_OperServ, u, "OLINE", OPER_OLINE_SYNTAX);
|
|
return MOD_CONT;
|
|
} else {
|
|
u2 = finduser(nick);
|
|
|
|
/* let's check whether the user is online */
|
|
|
|
if (!finduser(nick)) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_IN_USE, nick);
|
|
} else if (u2 && flags[0] == '+') {
|
|
send_cmd(s_OperServ, "SVSO %s %s", nick, flags);
|
|
send_cmd(s_OperServ, "MODE %s +o", nick);
|
|
change_user_mode(u2, "+o", NULL);
|
|
notice_lang(s_OperServ, u2, OPER_OLINE_IRCOP);
|
|
notice_lang(s_OperServ, u, OPER_OLINE_SUCCESS, flags, nick);
|
|
wallops(s_OperServ, "\2%s\2 used OLINE for %s", u->nick, nick);
|
|
} else if (u2 && flags[0] == '-') {
|
|
send_cmd(s_OperServ, "SVSO %s %s", nick, flags);
|
|
notice_lang(s_OperServ, u, OPER_OLINE_SUCCESS, flags, nick);
|
|
wallops(s_OperServ, "\2%s\2 used OLINE for %s", u->nick, nick);
|
|
} else
|
|
syntax_error(s_OperServ, u, "OLINE", OPER_OLINE_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
#endif
|
|
/*************************************************************************/
|
|
|
|
/* Clear all modes from a channel. */
|
|
|
|
static int do_clearmodes(User * u)
|
|
{
|
|
char *s;
|
|
int i;
|
|
char *argv[2];
|
|
char *chan = strtok(NULL, " ");
|
|
Channel *c;
|
|
int all = 0;
|
|
int count; /* For saving ban info */
|
|
char **bans; /* For saving ban info */
|
|
#if defined (IRC_ULTIMATE) || defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) ||defined (IRC_VIAGRA)
|
|
int exceptcount; /* For saving except info */
|
|
char **excepts; /* For saving except info */
|
|
#endif
|
|
struct c_userlist *cu, *next;
|
|
|
|
if (!chan) {
|
|
syntax_error(s_OperServ, u, "CLEARMODES", OPER_CLEARMODES_SYNTAX);
|
|
} else if (!(c = findchan(chan))) {
|
|
notice_lang(s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
|
|
} else if (c->bouncy_modes) {
|
|
notice_lang(s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
|
|
return MOD_CONT;
|
|
} else {
|
|
s = strtok(NULL, " ");
|
|
if (s) {
|
|
if (stricmp(s, "ALL") == 0) {
|
|
all = 1;
|
|
} else {
|
|
syntax_error(s_OperServ, u, "CLEARMODES",
|
|
OPER_CLEARMODES_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
}
|
|
|
|
if (WallOSClearmodes)
|
|
wallops(s_OperServ, "%s used CLEARMODES%s on %s", u->nick,
|
|
all ? " ALL" : "", chan);
|
|
|
|
if (all) {
|
|
/* Clear mode +o */
|
|
for (cu = c->users; cu; cu = next) {
|
|
next = cu->next;
|
|
|
|
if (!chan_has_user_status(c, cu->user, CUS_OP))
|
|
continue;
|
|
|
|
argv[0] = sstrdup("-o");
|
|
argv[1] = cu->user->nick;
|
|
|
|
send_cmd(s_OperServ, "MODE %s -o %s", c->name,
|
|
cu->user->nick);
|
|
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
|
|
|
free(argv[0]);
|
|
}
|
|
|
|
/* Clear mode +v */
|
|
for (cu = c->users; cu; cu = next) {
|
|
next = cu->next;
|
|
|
|
if (!chan_has_user_status(c, cu->user, CUS_VOICE))
|
|
continue;
|
|
|
|
argv[0] = sstrdup("-v");
|
|
argv[1] = sstrdup(cu->user->nick);
|
|
|
|
send_cmd(s_OperServ, "MODE %s -v %s", c->name,
|
|
cu->user->nick);
|
|
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
|
|
|
free(argv[0]);
|
|
}
|
|
#ifdef HAS_HALFOP
|
|
/* Clear mode +h */
|
|
for (cu = c->users; cu; cu = next) {
|
|
next = cu->next;
|
|
|
|
if (!chan_has_user_status(c, cu->user, CUS_HALFOP))
|
|
continue;
|
|
|
|
argv[0] = sstrdup("-h");
|
|
argv[1] = sstrdup(cu->user->nick);
|
|
|
|
send_cmd(s_OperServ, "MODE %s -h %s", c->name,
|
|
cu->user->nick);
|
|
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
|
|
|
free(argv[0]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Clear modes */
|
|
send_cmd(s_OperServ, "MODE %s %s %s", c->name, MODESTOREMOVE,
|
|
c->key ? c->key : "");
|
|
argv[0] = sstrdup(MODESTOREMOVE);
|
|
argv[1] = c->key ? c->key : NULL;
|
|
chan_set_modes(s_OperServ, c, c->key ? 2 : 1, argv, 0);
|
|
free(argv[0]);
|
|
|
|
/* Clear bans */
|
|
count = c->bancount;
|
|
bans = scalloc(sizeof(char *) * count, 1);
|
|
|
|
for (i = 0; i < count; i++)
|
|
bans[i] = sstrdup(c->bans[i]);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
argv[0] = sstrdup("-b");
|
|
argv[1] = bans[i];
|
|
send_cmd(s_OperServ, "MODE %s -b %s", c->name, argv[1]);
|
|
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
|
free(argv[1]);
|
|
free(argv[0]);
|
|
}
|
|
|
|
free(bans);
|
|
|
|
#if defined (IRC_ULTIMATE) || defined (IRC_UNREAL) || defined (IRC_ULTIMATE3) || defined (IRC_VIAGRA)
|
|
/* Clear excepts */
|
|
exceptcount = c->exceptcount;
|
|
excepts = scalloc(sizeof(char *) * exceptcount, 1);
|
|
|
|
for (i = 0; i < exceptcount; i++)
|
|
excepts[i] = sstrdup(c->excepts[i]);
|
|
|
|
for (i = 0; i < exceptcount; i++) {
|
|
argv[0] = sstrdup("-e");
|
|
argv[1] = excepts[i];
|
|
send_cmd(s_OperServ, "MODE %s -e %s", c->name, argv[1]);
|
|
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
|
free(argv[1]);
|
|
free(argv[0]);
|
|
}
|
|
|
|
free(excepts);
|
|
#endif
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_CLEARMODES_ALL_DONE, chan);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Kick a user from a channel (KICK command). */
|
|
|
|
static int do_os_kick(User * u)
|
|
{
|
|
char *argv[3];
|
|
char *chan, *nick, *s;
|
|
Channel *c;
|
|
|
|
chan = strtok(NULL, " ");
|
|
nick = strtok(NULL, " ");
|
|
s = strtok(NULL, "");
|
|
if (!chan || !nick || !s) {
|
|
syntax_error(s_OperServ, u, "KICK", OPER_KICK_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
if (!(c = findchan(chan))) {
|
|
notice_lang(s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
|
|
} else if (c->bouncy_modes) {
|
|
notice_lang(s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
|
|
return MOD_CONT;
|
|
}
|
|
send_cmd(s_OperServ, "KICK %s %s :%s (%s)", chan, nick, u->nick, s);
|
|
if (WallOSKick)
|
|
wallops(s_OperServ, "%s used KICK on %s/%s", u->nick, nick, chan);
|
|
argv[0] = sstrdup(chan);
|
|
argv[1] = sstrdup(nick);
|
|
argv[2] = sstrdup(s);
|
|
do_kick(s_OperServ, 3, argv);
|
|
free(argv[2]);
|
|
free(argv[1]);
|
|
free(argv[0]);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Forcefully change a user's nickname */
|
|
#ifdef USE_OSSVS
|
|
|
|
static int do_svsnick(User * u)
|
|
{
|
|
char *nick = strtok(NULL, " ");
|
|
char *newnick = strtok(NULL, " ");
|
|
|
|
NickAlias *na;
|
|
char *c;
|
|
|
|
/* Only allow this if SuperAdmin is enabled */
|
|
if (!u->isSuperAdmin) {
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_ONLY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!nick || !newnick) {
|
|
syntax_error(s_OperServ, u, "SVSNICK", OPER_SVSNICK_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/* Truncate long nicknames to NICKMAX-2 characters */
|
|
if (strlen(newnick) > (NICKMAX - 2)) {
|
|
notice_lang(s_NickServ, u, NICK_X_TRUNCATED,
|
|
newnick, NICKMAX - 2, newnick);
|
|
newnick[NICKMAX - 2] = '\0';
|
|
}
|
|
|
|
/* Check for valid characters */
|
|
if (*newnick == '-' || isdigit(*newnick)) {
|
|
notice_lang(s_OperServ, u, NICK_X_ILLEGAL, newnick);
|
|
return MOD_CONT;
|
|
}
|
|
#define isvalid(c) (((c) >= 'A' && (c) <= '~') || isdigit(c) || (c) == '-')
|
|
for (c = newnick; *c && (c - newnick) < NICKMAX; c++) {
|
|
if (!isvalid(*c) || isspace(*c)) {
|
|
notice_lang(s_OperServ, u, NICK_X_ILLEGAL, nick);
|
|
return MOD_CONT;
|
|
}
|
|
}
|
|
|
|
/* Check for a nick in use or a forbidden/suspended nick */
|
|
if (!finduser(nick)) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_IN_USE, nick);
|
|
} else if (finduser(newnick)) {
|
|
notice_lang(s_NickServ, u, NICK_X_IN_USE, newnick);
|
|
} else if ((na = findnick(newnick)) && (na->status & NS_VERBOTEN)) {
|
|
notice_lang(s_NickServ, u, NICK_X_FORBIDDEN, newnick);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SVSNICK_NEWNICK, nick, newnick);
|
|
wallops(s_OperServ, "%s used SVSNICK to change %s to %s",
|
|
u->nick, nick, newnick);
|
|
send_cmd(NULL, "SVSNICK %s %s :%ld", nick, newnick, time(NULL));
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
#endif
|
|
/*************************************************************************/
|
|
|
|
/* Adds an AKILL to the list. Returns >= 0 on success, -1 if it fails, -2
|
|
* if only the expiry time was changed.
|
|
* The success result is the number of AKILLs that were deleted to successfully add one.
|
|
*/
|
|
|
|
int add_akill(User * u, char *mask, const char *by, const time_t expires,
|
|
const char *reason)
|
|
{
|
|
int deleted = 0, i;
|
|
char *user, *mask2, *host;
|
|
Akill *entry;
|
|
|
|
/* Checks whether there is an AKILL that already covers
|
|
* the one we want to add, and whether there are AKILLs
|
|
* that would be covered by this one. The masks AND the
|
|
* expiry times are used to determine this, because some
|
|
* AKILLs may become useful when another one expires.
|
|
* If so, warn the user in the first case and cleanup
|
|
* the useless AKILLs in the second.
|
|
*/
|
|
|
|
if (akills.count > 0) {
|
|
|
|
for (i = akills.count - 1; i >= 0; i--) {
|
|
char amask[BUFSIZE];
|
|
|
|
entry = akills.list[i];
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
snprintf(amask, sizeof(amask), "%s@%s", entry->user,
|
|
entry->host);
|
|
|
|
if (!stricmp(amask, mask)) {
|
|
/* We change the AKILL expiry time if its current one is less than the new.
|
|
* This is preferable to be sure we don't change an important AKILL
|
|
* accidentely.
|
|
*/
|
|
if (entry->expires >= expires || entry->expires == 0) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_EXISTS,
|
|
mask);
|
|
return -1;
|
|
} else {
|
|
entry->expires = expires;
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_CHANGED,
|
|
amask);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if (match_wild_nocase(amask, mask)
|
|
&& (entry->expires >= expires || entry->expires == 0)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_ALREADY_COVERED,
|
|
mask, amask);
|
|
return -1;
|
|
}
|
|
|
|
if (match_wild_nocase(mask, amask)
|
|
&& (entry->expires <= expires || expires == 0)) {
|
|
slist_delete(&akills, i);
|
|
deleted++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* We can now check whether the list is full or not. */
|
|
if (slist_full(&akills)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_REACHED_LIMIT,
|
|
akills.limit);
|
|
return -1;
|
|
}
|
|
|
|
/* We can now (really) add the AKILL. */
|
|
mask2 = sstrdup(mask);
|
|
host = strchr(mask2, '@');
|
|
if (!host)
|
|
return -1;
|
|
user = mask2;
|
|
*host = 0;
|
|
host++;
|
|
|
|
entry = scalloc(sizeof(Akill), 1);
|
|
if (!entry)
|
|
return -1;
|
|
|
|
entry->user = sstrdup(user);
|
|
entry->host = sstrdup(host);
|
|
entry->by = sstrdup(by);
|
|
entry->reason = sstrdup(reason);
|
|
entry->seton = time(NULL);
|
|
entry->expires = expires;
|
|
|
|
slist_add(&akills, entry);
|
|
|
|
if (AkillOnAdd)
|
|
s_akill(entry->user, entry->host, entry->by, entry->seton,
|
|
entry->expires, entry->reason);
|
|
|
|
free(mask2);
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/* Does the user match any AKILLs? */
|
|
|
|
int check_akill(const char *nick, const char *username, const char *host,
|
|
const char *vhost, const char *ip)
|
|
{
|
|
int i;
|
|
Akill *ak;
|
|
|
|
/**
|
|
* If DefCon is set to NO new users - kill the user ;).
|
|
**/
|
|
if (checkDefCon(DEFCON_NO_NEW_CLIENTS)) {
|
|
kill_user(s_OperServ, nick, DefConAkillReason);
|
|
return 1;
|
|
}
|
|
|
|
if (akills.count == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < akills.count; i++) {
|
|
ak = akills.list[i];
|
|
if (!ak)
|
|
continue;
|
|
if (match_wild_nocase(ak->user, username)
|
|
&& (match_wild_nocase(ak->host, host)
|
|
|| (vhost && match_wild_nocase(ak->host, vhost)))) {
|
|
s_akill(ak->user, ak->host, ak->by, ak->seton, ak->expires,
|
|
ak->reason);
|
|
return 1;
|
|
}
|
|
#ifdef HAS_NICKIP
|
|
if (ip)
|
|
if (match_wild_nocase(ak->user, username)
|
|
&& match_wild_nocase(ak->host, ip)) {
|
|
s_akill(ak->user, ak->host, ak->by, ak->seton, ak->expires,
|
|
ak->reason);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Delete any expired autokills. */
|
|
|
|
void expire_akills(void)
|
|
{
|
|
int i;
|
|
time_t now = time(NULL);
|
|
Akill *ak;
|
|
|
|
for (i = akills.count - 1; i >= 0; i--) {
|
|
ak = akills.list[i];
|
|
|
|
if (!ak->expires || ak->expires > now)
|
|
continue;
|
|
|
|
if (WallAkillExpire)
|
|
wallops(s_OperServ, "AKILL on %s@%s has expired", ak->user,
|
|
ak->host);
|
|
slist_delete(&akills, i);
|
|
}
|
|
}
|
|
|
|
static void free_akill_entry(SList * slist, void *item)
|
|
{
|
|
Akill *ak = item;
|
|
|
|
/* Remove the AKILLs from all the servers */
|
|
s_rakill(ak->user, ak->host);
|
|
|
|
/* Free the structure */
|
|
free(ak->user);
|
|
free(ak->host);
|
|
free(ak->by);
|
|
free(ak->reason);
|
|
free(ak);
|
|
}
|
|
|
|
/* item1 is not an Akill pointer, but a char
|
|
*/
|
|
|
|
static int is_akill_entry_equal(SList * slist, void *item1, void *item2)
|
|
{
|
|
char *ak1 = item1, buf[BUFSIZE];
|
|
Akill *ak2 = item2;
|
|
|
|
if (!ak1 || !ak2)
|
|
return 0;
|
|
|
|
snprintf(buf, sizeof(buf), "%s@%s", ak2->user, ak2->host);
|
|
|
|
if (!stricmp(ak1, buf))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Lists an AKILL entry, prefixing it with the header if needed */
|
|
|
|
static int akill_list(int number, Akill * ak, User * u, int *sent_header)
|
|
{
|
|
char mask[BUFSIZE];
|
|
|
|
if (!ak)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
snprintf(mask, sizeof(mask), "%s@%s", ak->user, ak->host);
|
|
notice_lang(s_OperServ, u, OPER_AKILL_LIST_FORMAT, number, mask,
|
|
ak->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int akill_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return akill_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Lists an AKILL entry, prefixing it with the header if needed */
|
|
|
|
static int akill_view(int number, Akill * ak, User * u, int *sent_header)
|
|
{
|
|
char mask[BUFSIZE];
|
|
char timebuf[32], expirebuf[256];
|
|
struct tm tm;
|
|
|
|
if (!ak)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_VIEW_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
snprintf(mask, sizeof(mask), "%s@%s", ak->user, ak->host);
|
|
tm = *localtime(&ak->seton);
|
|
strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT,
|
|
&tm);
|
|
expire_left(u->na, expirebuf, sizeof(expirebuf), ak->expires);
|
|
notice_lang(s_OperServ, u, OPER_AKILL_VIEW_FORMAT, number, mask,
|
|
ak->by, timebuf, expirebuf, ak->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int akill_view_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return akill_view(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Manage the AKILL list. */
|
|
|
|
static int do_akill(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
char breason[BUFSIZE];
|
|
|
|
if (!cmd)
|
|
cmd = "";
|
|
|
|
if (!stricmp(cmd, "ADD")) {
|
|
int deleted = 0;
|
|
char *expiry, *mask, *reason;
|
|
time_t expires;
|
|
|
|
mask = strtok(NULL, " ");
|
|
if (mask && *mask == '+') {
|
|
expiry = mask;
|
|
mask = strtok(NULL, " ");
|
|
} else {
|
|
expiry = NULL;
|
|
}
|
|
|
|
expires = expiry ? dotime(expiry) : AutokillExpiry;
|
|
/* If the expiry given does not contain a final letter, it's in days,
|
|
* said the doc. Ah well.
|
|
*/
|
|
if (expiry && isdigit(expiry[strlen(expiry) - 1]))
|
|
expires *= 86400;
|
|
/* Do not allow less than a minute expiry time */
|
|
if (expires != 0 && expires < 60) {
|
|
notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
|
|
return MOD_CONT;
|
|
} else if (expires > 0) {
|
|
expires += time(NULL);
|
|
}
|
|
|
|
if (mask && (reason = strtok(NULL, ""))) {
|
|
/* We first do some sanity check on the proposed mask. */
|
|
if (strchr(mask, '!')) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_NICK);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!strchr(mask, '@')) {
|
|
notice_lang(s_OperServ, u, BAD_USERHOST_MASK);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (mask && strspn(mask, "~@.*?") == strlen(mask)) {
|
|
notice_lang(s_OperServ, u, USERHOST_MASK_TOO_WIDE, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**
|
|
* Changed sprintf() to snprintf()and increased the size of
|
|
* breason to match bufsize
|
|
* -Rob
|
|
**/
|
|
if (AddAkiller) {
|
|
snprintf(breason, sizeof(breason), "[%s] %s", u->nick,
|
|
reason);
|
|
reason = sstrdup(breason);
|
|
}
|
|
|
|
deleted = add_akill(u, mask, u->nick, expires, reason);
|
|
if (deleted < 0)
|
|
return MOD_CONT;
|
|
else if (deleted)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_DELETED_SEVERAL,
|
|
deleted);
|
|
notice_lang(s_OperServ, u, OPER_AKILL_ADDED, mask);
|
|
|
|
if (WallOSAkill) {
|
|
char buf[128];
|
|
|
|
if (!expires) {
|
|
strcpy(buf, "does not expire");
|
|
} else {
|
|
int wall_expiry = expires - time(NULL);
|
|
char *s = NULL;
|
|
|
|
if (wall_expiry >= 86400) {
|
|
wall_expiry /= 86400;
|
|
s = "day";
|
|
} else if (wall_expiry >= 3600) {
|
|
wall_expiry /= 3600;
|
|
s = "hour";
|
|
} else if (wall_expiry >= 60) {
|
|
wall_expiry /= 60;
|
|
s = "minute";
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "expires in %d %s%s",
|
|
wall_expiry, s,
|
|
(wall_expiry == 1) ? "" : "s");
|
|
}
|
|
|
|
wallops(s_OperServ, "%s added an AKILL for %s (%s) (%s)",
|
|
u->nick, mask, reason, buf);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else {
|
|
syntax_error(s_OperServ, u, "AKILL", OPER_AKILL_SYNTAX);
|
|
}
|
|
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
|
|
char *mask;
|
|
int res = 0;
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask) {
|
|
syntax_error(s_OperServ, u, "AKILL", OPER_AKILL_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (akills.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&akills, mask, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_DELETED_SEVERAL,
|
|
res);
|
|
}
|
|
} else {
|
|
if ((res = slist_indexof(&akills, mask)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NOT_FOUND, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&akills, res);
|
|
notice_lang(s_OperServ, u, OPER_AKILL_DELETED, mask);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (akills.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&akills, mask, &akill_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Akill");
|
|
}
|
|
} else {
|
|
int i;
|
|
char amask[BUFSIZE];
|
|
|
|
for (i = 0; i < akills.count; i++) {
|
|
snprintf(amask, sizeof(amask), "%s@%s",
|
|
((Akill *) akills.list[i])->user,
|
|
((Akill *) akills.list[i])->host);
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
akill_list(i + 1, akills.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_MATCH);
|
|
else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Akill");
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "VIEW")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (akills.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&akills, mask, &akill_view_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char amask[BUFSIZE];
|
|
|
|
for (i = 0; i < akills.count; i++) {
|
|
snprintf(amask, sizeof(amask), "%s@%s",
|
|
((Akill *) akills.list[i])->user,
|
|
((Akill *) akills.list[i])->host);
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
akill_view(i + 1, akills.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_AKILL_NO_MATCH);
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
slist_clear(&akills, 1);
|
|
notice_lang(s_OperServ, u, OPER_AKILL_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "AKILL", OPER_AKILL_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
|
|
/* Adds an SGLINE to the list. Returns >= 0 on success, -1 if it failed, -2 if
|
|
* only the expiry time changed.
|
|
* The success result is the number of SGLINEs that were deleted to successfully add one.
|
|
*/
|
|
|
|
int add_sgline(User * u, char *mask, const char *by, const time_t expires,
|
|
const char *reason)
|
|
{
|
|
int deleted = 0, i;
|
|
SXLine *entry;
|
|
|
|
/* Checks whether there is an SGLINE that already covers
|
|
* the one we want to add, and whether there are SGLINEs
|
|
* that would be covered by this one.
|
|
* If so, warn the user in the first case and cleanup
|
|
* the useless SGLINEs in the second.
|
|
*/
|
|
|
|
if (sglines.count > 0) {
|
|
|
|
for (i = sglines.count - 1; i >= 0; i--) {
|
|
entry = sglines.list[i];
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
if (!stricmp(entry->mask, mask)) {
|
|
if (entry->expires >= expires || entry->expires == 0) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_EXISTS,
|
|
mask);
|
|
return -1;
|
|
} else {
|
|
entry->expires = expires;
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_CHANGED,
|
|
entry->mask);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if (match_wild_nocase(entry->mask, mask)
|
|
&& (entry->expires >= expires || entry->expires == 0)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_ALREADY_COVERED,
|
|
mask, entry->mask);
|
|
return -1;
|
|
}
|
|
|
|
if (match_wild_nocase(mask, entry->mask)
|
|
&& (entry->expires <= expires || expires == 0)) {
|
|
slist_delete(&sglines, i);
|
|
deleted++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* We can now check whether the list is full or not. */
|
|
if (slist_full(&sglines)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_REACHED_LIMIT,
|
|
sglines.limit);
|
|
return -1;
|
|
}
|
|
|
|
/* We can now (really) add the SGLINE. */
|
|
entry = scalloc(sizeof(SXLine), 1);
|
|
if (!entry)
|
|
return -1;
|
|
|
|
entry->mask = sstrdup(mask);
|
|
entry->by = sstrdup(by);
|
|
entry->reason = sstrdup(reason);
|
|
entry->seton = time(NULL);
|
|
entry->expires = expires;
|
|
|
|
slist_add(&sglines, entry);
|
|
|
|
s_sgline(entry->mask, entry->reason);
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/* Does the user match any SGLINEs? */
|
|
|
|
int check_sgline(const char *nick, const char *realname)
|
|
{
|
|
int i;
|
|
SXLine *sx;
|
|
|
|
if (sglines.count == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < sglines.count; i++) {
|
|
sx = sglines.list[i];
|
|
if (!sx)
|
|
continue;
|
|
|
|
if (match_wild_nocase(sx->mask, realname)) {
|
|
s_sgline(sx->mask, sx->reason);
|
|
/* We kill nick since s_sgline can't */
|
|
send_cmd(ServerName, "SVSKILL %s :G-Lined: %s", nick,
|
|
sx->reason);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Delete any expired SGLINEs. */
|
|
|
|
void expire_sglines(void)
|
|
{
|
|
int i;
|
|
time_t now = time(NULL);
|
|
SXLine *sx;
|
|
|
|
for (i = sglines.count - 1; i >= 0; i--) {
|
|
sx = sglines.list[i];
|
|
|
|
if (!sx->expires || sx->expires > now)
|
|
continue;
|
|
|
|
if (WallSGLineExpire)
|
|
wallops(s_OperServ, "SGLINE on \2%s\2 has expired", sx->mask);
|
|
slist_delete(&sglines, i);
|
|
}
|
|
}
|
|
|
|
static void free_sgline_entry(SList * slist, void *item)
|
|
{
|
|
SXLine *sx = item;
|
|
|
|
/* Remove the SGLINE from all the servers */
|
|
s_unsgline(sx->mask);
|
|
|
|
/* Free the structure */
|
|
free(sx->mask);
|
|
free(sx->by);
|
|
free(sx->reason);
|
|
free(sx);
|
|
}
|
|
|
|
/* item1 is not an SXLine pointer, but a char */
|
|
|
|
static int is_sgline_entry_equal(SList * slist, void *item1, void *item2)
|
|
{
|
|
char *sx1 = item1;
|
|
SXLine *sx2 = item2;
|
|
|
|
if (!sx1 || !sx2)
|
|
return 0;
|
|
|
|
if (!stricmp(sx1, sx2->mask))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Lists an SGLINE entry, prefixing it with the header if needed */
|
|
|
|
static int sgline_list(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_LIST_FORMAT, number, sx->mask,
|
|
sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int sgline_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return sgline_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Lists an SGLINE entry, prefixing it with the header if needed */
|
|
|
|
static int sgline_view(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
char timebuf[32], expirebuf[256];
|
|
struct tm tm;
|
|
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_VIEW_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
tm = *localtime(&sx->seton);
|
|
strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT,
|
|
&tm);
|
|
expire_left(u->na, expirebuf, sizeof(expirebuf), sx->expires);
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_VIEW_FORMAT, number, sx->mask,
|
|
sx->by, timebuf, expirebuf, sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int sgline_view_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return sgline_view(number, item, u, sent_header);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Manage the SGLINE list. */
|
|
|
|
static int do_sgline(User * u)
|
|
{
|
|
#ifdef IRC_BAHAMUT
|
|
char *cmd = strtok(NULL, " ");
|
|
|
|
if (!cmd)
|
|
cmd = "";
|
|
|
|
if (!stricmp(cmd, "ADD")) {
|
|
int deleted = 0;
|
|
char *expiry, *mask, *reason;
|
|
time_t expires;
|
|
|
|
mask = strtok(NULL, ":");
|
|
if (mask && *mask == '+') {
|
|
expiry = mask;
|
|
mask = strchr(expiry, ' ');
|
|
if (mask) {
|
|
*mask = 0;
|
|
mask++;
|
|
}
|
|
} else {
|
|
expiry = NULL;
|
|
}
|
|
|
|
expires = expiry ? dotime(expiry) : SGLineExpiry;
|
|
/* If the expiry given does not contain a final letter, it's in days,
|
|
* said the doc. Ah well.
|
|
*/
|
|
if (expiry && isdigit(expiry[strlen(expiry) - 1]))
|
|
expires *= 86400;
|
|
/* Do not allow less than a minute expiry time */
|
|
if (expires != 0 && expires < 60) {
|
|
notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
|
|
return MOD_CONT;
|
|
} else if (expires > 0) {
|
|
expires += time(NULL);
|
|
}
|
|
|
|
if (mask && (reason = strtok(NULL, ""))) {
|
|
/* We first do some sanity check on the proposed mask. */
|
|
|
|
if (mask && strspn(mask, "*?") == strlen(mask)) {
|
|
notice_lang(s_OperServ, u, USERHOST_MASK_TOO_WIDE, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
deleted = add_sgline(u, mask, u->nick, expires, reason);
|
|
if (deleted < 0)
|
|
return MOD_CONT;
|
|
else if (deleted)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_DELETED_SEVERAL,
|
|
deleted);
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_ADDED, mask);
|
|
|
|
if (WallOSSGLine) {
|
|
char buf[128];
|
|
|
|
if (!expires) {
|
|
strcpy(buf, "does not expire");
|
|
} else {
|
|
int wall_expiry = expires - time(NULL);
|
|
char *s = NULL;
|
|
|
|
if (wall_expiry >= 86400) {
|
|
wall_expiry /= 86400;
|
|
s = "day";
|
|
} else if (wall_expiry >= 3600) {
|
|
wall_expiry /= 3600;
|
|
s = "hour";
|
|
} else if (wall_expiry >= 60) {
|
|
wall_expiry /= 60;
|
|
s = "minute";
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "expires in %d %s%s",
|
|
wall_expiry, s,
|
|
(wall_expiry == 1) ? "" : "s");
|
|
}
|
|
|
|
wallops(s_OperServ, "%s added an SGLINE for %s (%s)",
|
|
u->nick, mask, buf);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SGLINE", OPER_SGLINE_SYNTAX);
|
|
}
|
|
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
|
|
char *mask;
|
|
int res = 0;
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask) {
|
|
syntax_error(s_OperServ, u, "SGLINE", OPER_SGLINE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (sglines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&sglines, mask, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_DELETED_SEVERAL,
|
|
res);
|
|
}
|
|
} else {
|
|
if ((res = slist_indexof(&sglines, mask)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NOT_FOUND, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&sglines, res);
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_DELETED, mask);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (sglines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&sglines, mask, &sgline_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < sglines.count; i++) {
|
|
amask = ((SXLine *) sglines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
sgline_list(i + 1, sglines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NO_MATCH);
|
|
else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "SGLine");
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "VIEW")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (sglines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&sglines, mask, &sgline_view_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < sglines.count; i++) {
|
|
amask = ((SXLine *) sglines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
sgline_view(i + 1, sglines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_NO_MATCH);
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
slist_clear(&sglines, 1);
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SGLINE", OPER_SGLINE_SYNTAX);
|
|
}
|
|
#else
|
|
notice_lang(s_OperServ, u, OPER_SGLINE_UNSUPPORTED);
|
|
#endif
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Adds an SQLINE to the list. Returns >= 0 on success, -1 if it failed, -2 if
|
|
* only the expiry time changed.
|
|
* The success result is the number of SQLINEs that were deleted to successfully add one.
|
|
*/
|
|
|
|
int add_sqline(User * u, char *mask, const char *by, const time_t expires,
|
|
const char *reason)
|
|
{
|
|
int deleted = 0, i;
|
|
SXLine *entry;
|
|
|
|
/* Checks whether there is an SQLINE that already covers
|
|
* the one we want to add, and whether there are SQLINEs
|
|
* that would be covered by this one.
|
|
* If so, warn the user in the first case and cleanup
|
|
* the useless SQLINEs in the second.
|
|
*/
|
|
|
|
if (sqlines.count > 0) {
|
|
|
|
for (i = sqlines.count - 1; i >= 0; i--) {
|
|
entry = sqlines.list[i];
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
if ((*mask == '#' && *entry->mask != '#') ||
|
|
(*mask != '#' && *entry->mask == '#'))
|
|
continue;
|
|
|
|
if (!stricmp(entry->mask, mask)) {
|
|
if (entry->expires >= expires || entry->expires == 0) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_EXISTS,
|
|
mask);
|
|
return -1;
|
|
} else {
|
|
entry->expires = expires;
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_CHANGED,
|
|
entry->mask);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if (match_wild_nocase(entry->mask, mask)
|
|
&& (entry->expires >= expires || entry->expires == 0)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_ALREADY_COVERED,
|
|
mask, entry->mask);
|
|
return -1;
|
|
}
|
|
|
|
if (match_wild_nocase(mask, entry->mask)
|
|
&& (entry->expires <= expires || expires == 0)) {
|
|
slist_delete(&sqlines, i);
|
|
deleted++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* We can now check whether the list is full or not. */
|
|
if (slist_full(&sqlines)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_REACHED_LIMIT,
|
|
sqlines.limit);
|
|
return -1;
|
|
}
|
|
|
|
/* We can now (really) add the SQLINE. */
|
|
entry = scalloc(sizeof(SXLine), 1);
|
|
if (!entry)
|
|
return -1;
|
|
|
|
entry->mask = sstrdup(mask);
|
|
entry->by = sstrdup(by);
|
|
entry->reason = sstrdup(reason);
|
|
entry->seton = time(NULL);
|
|
entry->expires = expires;
|
|
|
|
slist_add(&sqlines, entry);
|
|
|
|
s_sqline(entry->mask, entry->reason);
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/* Does the user match any SQLINEs? */
|
|
|
|
int check_sqline(const char *nick, int nick_change)
|
|
{
|
|
int i;
|
|
SXLine *sx;
|
|
|
|
if (sqlines.count == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
sx = sqlines.list[i];
|
|
if (!sx)
|
|
continue;
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
if (*sx->mask == '#')
|
|
continue;
|
|
#endif
|
|
|
|
if (match_wild_nocase(sx->mask, nick)) {
|
|
s_sqline(sx->mask, sx->reason);
|
|
/* We kill nick since s_sqline can't */
|
|
#ifdef IRC_BAHAMUT
|
|
send_cmd(ServerName, "SVSKILL %s :Q-Lined: %s", nick,
|
|
sx->reason);
|
|
#else
|
|
if (!nick_change) {
|
|
send_cmd(s_OperServ, "KILL %s :Q-Lined: %s", nick,
|
|
sx->reason);
|
|
} else {
|
|
char reason[300];
|
|
snprintf(reason, sizeof(reason), "Q-Lined: %s",
|
|
sx->reason);
|
|
kill_user(s_OperServ, nick, reason);
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
int check_chan_sqline(const char *chan)
|
|
{
|
|
int i;
|
|
SXLine *sx;
|
|
|
|
if (sqlines.count == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
sx = sqlines.list[i];
|
|
if (!sx)
|
|
continue;
|
|
|
|
if (*sx->mask != '#')
|
|
continue;
|
|
|
|
if (match_wild_nocase(sx->mask, chan)) {
|
|
s_sqline(sx->mask, sx->reason);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Delete any expired SQLINEs. */
|
|
|
|
void expire_sqlines(void)
|
|
{
|
|
int i;
|
|
time_t now = time(NULL);
|
|
SXLine *sx;
|
|
|
|
for (i = sqlines.count - 1; i >= 0; i--) {
|
|
sx = sqlines.list[i];
|
|
|
|
if (!sx->expires || sx->expires > now)
|
|
continue;
|
|
|
|
if (WallSQLineExpire)
|
|
wallops(s_OperServ, "SQLINE on \2%s\2 has expired", sx->mask);
|
|
|
|
slist_delete(&sqlines, i);
|
|
}
|
|
}
|
|
|
|
static void free_sqline_entry(SList * slist, void *item)
|
|
{
|
|
SXLine *sx = item;
|
|
|
|
/* Remove the SQLINE from all the servers */
|
|
s_unsqline(sx->mask);
|
|
|
|
/* Free the structure */
|
|
free(sx->mask);
|
|
free(sx->by);
|
|
free(sx->reason);
|
|
free(sx);
|
|
}
|
|
|
|
/* item1 is not an SXLine pointer, but a char */
|
|
|
|
static int is_sqline_entry_equal(SList * slist, void *item1, void *item2)
|
|
{
|
|
char *sx1 = item1;
|
|
SXLine *sx2 = item2;
|
|
|
|
if (!sx1 || !sx2)
|
|
return 0;
|
|
|
|
if (!stricmp(sx1, sx2->mask))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Lists an SQLINE entry, prefixing it with the header if needed */
|
|
|
|
static int sqline_list(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_LIST_FORMAT, number, sx->mask,
|
|
sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int sqline_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return sqline_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Lists an SQLINE entry, prefixing it with the header if needed */
|
|
|
|
static int sqline_view(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
char timebuf[32], expirebuf[256];
|
|
struct tm tm;
|
|
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_VIEW_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
tm = *localtime(&sx->seton);
|
|
strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT,
|
|
&tm);
|
|
expire_left(u->na, expirebuf, sizeof(expirebuf), sx->expires);
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_VIEW_FORMAT, number, sx->mask,
|
|
sx->by, timebuf, expirebuf, sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int sqline_view_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return sqline_view(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Manage the SQLINE list. */
|
|
|
|
static int do_sqline(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
|
|
if (!cmd)
|
|
cmd = "";
|
|
|
|
if (!stricmp(cmd, "ADD")) {
|
|
int deleted = 0;
|
|
char *expiry, *mask, *reason;
|
|
time_t expires;
|
|
|
|
mask = strtok(NULL, " ");
|
|
if (mask && *mask == '+') {
|
|
expiry = mask;
|
|
mask = strtok(NULL, " ");
|
|
} else {
|
|
expiry = NULL;
|
|
}
|
|
|
|
expires = expiry ? dotime(expiry) : SQLineExpiry;
|
|
/* If the expiry given does not contain a final letter, it's in days,
|
|
* said the doc. Ah well.
|
|
*/
|
|
if (expiry && isdigit(expiry[strlen(expiry) - 1]))
|
|
expires *= 86400;
|
|
/* Do not allow less than a minute expiry time */
|
|
if (expires != 0 && expires < 60) {
|
|
notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
|
|
return MOD_CONT;
|
|
} else if (expires > 0) {
|
|
expires += time(NULL);
|
|
}
|
|
|
|
if (mask && (reason = strtok(NULL, ""))) {
|
|
|
|
/* We first do some sanity check on the proposed mask. */
|
|
if (strspn(mask, "*?") == strlen(mask)) {
|
|
notice_lang(s_OperServ, u, USERHOST_MASK_TOO_WIDE, mask);
|
|
return MOD_CONT;
|
|
}
|
|
#ifndef IRC_BAHAMUT
|
|
/* Channel SQLINEs are only supported on Bahamut servers */
|
|
if (*mask == '#') {
|
|
notice_lang(s_OperServ, u,
|
|
OPER_SQLINE_CHANNELS_UNSUPPORTED);
|
|
return MOD_CONT;
|
|
}
|
|
#endif
|
|
|
|
deleted = add_sqline(u, mask, u->nick, expires, reason);
|
|
if (deleted < 0)
|
|
return MOD_CONT;
|
|
else if (deleted)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_DELETED_SEVERAL,
|
|
deleted);
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_ADDED, mask);
|
|
|
|
if (WallOSSQLine) {
|
|
char buf[128];
|
|
|
|
if (!expires) {
|
|
strcpy(buf, "does not expire");
|
|
} else {
|
|
int wall_expiry = expires - time(NULL);
|
|
char *s = NULL;
|
|
|
|
if (wall_expiry >= 86400) {
|
|
wall_expiry /= 86400;
|
|
s = "day";
|
|
} else if (wall_expiry >= 3600) {
|
|
wall_expiry /= 3600;
|
|
s = "hour";
|
|
} else if (wall_expiry >= 60) {
|
|
wall_expiry /= 60;
|
|
s = "minute";
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "expires in %d %s%s",
|
|
wall_expiry, s,
|
|
(wall_expiry == 1) ? "" : "s");
|
|
}
|
|
|
|
wallops(s_OperServ, "%s added an SQLINE for %s (%s)",
|
|
u->nick, mask, buf);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SQLINE", OPER_SQLINE_SYNTAX);
|
|
}
|
|
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
|
|
char *mask;
|
|
int res = 0;
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask) {
|
|
syntax_error(s_OperServ, u, "SQLINE", OPER_SQLINE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (sqlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&sqlines, mask, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_DELETED_SEVERAL,
|
|
res);
|
|
}
|
|
} else {
|
|
if ((res = slist_indexof(&sqlines, mask)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NOT_FOUND, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&sqlines, res);
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_DELETED, mask);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (sqlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&sqlines, mask, &sqline_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
amask = ((SXLine *) sqlines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
sqline_list(i + 1, sqlines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NO_MATCH);
|
|
else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "SQLine");
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "VIEW")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (sqlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, "");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&sqlines, mask, &sqline_view_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < sqlines.count; i++) {
|
|
amask = ((SXLine *) sqlines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
sqline_view(i + 1, sqlines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_NO_MATCH);
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
slist_clear(&sqlines, 1);
|
|
notice_lang(s_OperServ, u, OPER_SQLINE_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SQLINE", OPER_SQLINE_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
#ifdef IRC_BAHAMUT
|
|
|
|
/* Adds an SZLINE to the list. Returns >= 0 on success, -1 on error, -2 if
|
|
* only the expiry time changed.
|
|
* The success result is the number of SZLINEs that were deleted to successfully add one.
|
|
*/
|
|
|
|
int add_szline(User * u, char *mask, const char *by, const time_t expires,
|
|
const char *reason)
|
|
{
|
|
int deleted = 0, i;
|
|
SXLine *entry;
|
|
|
|
/* Checks whether there is an SZLINE that already covers
|
|
* the one we want to add, and whether there are SZLINEs
|
|
* that would be covered by this one.
|
|
* If so, warn the user in the first case and cleanup
|
|
* the useless SZLINEs in the second.
|
|
*/
|
|
|
|
if (szlines.count > 0) {
|
|
|
|
for (i = szlines.count - 1; i >= 0; i--) {
|
|
entry = szlines.list[i];
|
|
|
|
if (!entry)
|
|
continue;
|
|
|
|
if (!stricmp(entry->mask, mask)) {
|
|
if (entry->expires >= expires || entry->expires == 0) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_EXISTS,
|
|
mask);
|
|
return -1;
|
|
} else {
|
|
entry->expires = expires;
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_EXISTS,
|
|
mask);
|
|
return -2;
|
|
}
|
|
}
|
|
|
|
if (match_wild_nocase(entry->mask, mask)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_ALREADY_COVERED,
|
|
mask, entry->mask);
|
|
return -1;
|
|
}
|
|
|
|
if (match_wild_nocase(mask, entry->mask)) {
|
|
slist_delete(&szlines, i);
|
|
deleted++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* We can now check whether the list is full or not. */
|
|
if (slist_full(&szlines)) {
|
|
if (u)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_REACHED_LIMIT,
|
|
szlines.limit);
|
|
return -1;
|
|
}
|
|
|
|
/* We can now (really) add the SZLINE. */
|
|
entry = scalloc(sizeof(SXLine), 1);
|
|
if (!entry)
|
|
return -1;
|
|
|
|
entry->mask = sstrdup(mask);
|
|
entry->by = sstrdup(by);
|
|
entry->reason = sstrdup(reason);
|
|
entry->seton = time(NULL);
|
|
entry->expires = expires;
|
|
|
|
slist_add(&szlines, entry);
|
|
s_szline(entry->mask, entry->reason);
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/* Delete any expired SZLINEs. */
|
|
|
|
void expire_szlines(void)
|
|
{
|
|
int i;
|
|
time_t now = time(NULL);
|
|
SXLine *sx;
|
|
|
|
for (i = szlines.count - 1; i >= 0; i--) {
|
|
sx = szlines.list[i];
|
|
|
|
if (!sx->expires || sx->expires > now)
|
|
continue;
|
|
|
|
if (WallSZLineExpire)
|
|
wallops(s_OperServ, "SZLINE on \2%s\2 has expired", sx->mask);
|
|
slist_delete(&szlines, i);
|
|
}
|
|
}
|
|
|
|
static void free_szline_entry(SList * slist, void *item)
|
|
{
|
|
SXLine *sx = item;
|
|
|
|
/* Remove the SZLINE from all the servers */
|
|
s_unszline(sx->mask);
|
|
|
|
/* Free the structure */
|
|
free(sx->mask);
|
|
free(sx->by);
|
|
free(sx->reason);
|
|
free(sx);
|
|
}
|
|
|
|
/* item1 is not an SXLine pointer, but a char
|
|
*/
|
|
|
|
static int is_szline_entry_equal(SList * slist, void *item1, void *item2)
|
|
{
|
|
char *sx1 = item1;
|
|
SXLine *sx2 = item2;
|
|
|
|
if (!sx1 || !sx2)
|
|
return 0;
|
|
|
|
if (!stricmp(sx1, sx2->mask))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Lists an SZLINE entry, prefixing it with the header if needed */
|
|
|
|
static int szline_list(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_LIST_FORMAT, number, sx->mask,
|
|
sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int szline_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return szline_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Lists an SZLINE entry, prefixing it with the header if needed */
|
|
|
|
static int szline_view(int number, SXLine * sx, User * u, int *sent_header)
|
|
{
|
|
char timebuf[32], expirebuf[256];
|
|
struct tm tm;
|
|
|
|
if (!sx)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_VIEW_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
tm = *localtime(&sx->seton);
|
|
strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT,
|
|
&tm);
|
|
expire_left(u->na, expirebuf, sizeof(expirebuf), sx->expires);
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_VIEW_FORMAT, number, sx->mask,
|
|
sx->by, timebuf, expirebuf, sx->reason);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int szline_view_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return szline_view(number, item, u, sent_header);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Manage the SZLINE list. */
|
|
|
|
static int do_szline(User * u)
|
|
{
|
|
#ifdef IRC_BAHAMUT
|
|
char *cmd = strtok(NULL, " ");
|
|
|
|
if (!cmd)
|
|
cmd = "";
|
|
|
|
if (!stricmp(cmd, "ADD")) {
|
|
int deleted = 0;
|
|
char *expiry, *mask, *reason;
|
|
time_t expires;
|
|
|
|
mask = strtok(NULL, " ");
|
|
if (mask && *mask == '+') {
|
|
expiry = mask;
|
|
mask = strtok(NULL, " ");
|
|
} else {
|
|
expiry = NULL;
|
|
}
|
|
|
|
expires = expiry ? dotime(expiry) : SZLineExpiry;
|
|
/* If the expiry given does not contain a final letter, it's in days,
|
|
* said the doc. Ah well.
|
|
*/
|
|
if (expiry && isdigit(expiry[strlen(expiry) - 1]))
|
|
expires *= 86400;
|
|
/* Do not allow less than a minute expiry time */
|
|
if (expires != 0 && expires < 60) {
|
|
notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
|
|
return MOD_CONT;
|
|
} else if (expires > 0) {
|
|
expires += time(NULL);
|
|
}
|
|
|
|
if (mask && (reason = strtok(NULL, ""))) {
|
|
/* We first do some sanity check on the proposed mask. */
|
|
|
|
if (strchr(mask, '!') || strchr(mask, '@')) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_ONLY_IPS);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (strspn(mask, "*?") == strlen(mask)) {
|
|
notice_lang(s_OperServ, u, USERHOST_MASK_TOO_WIDE, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
deleted = add_szline(u, mask, u->nick, expires, reason);
|
|
if (deleted < 0)
|
|
return MOD_CONT;
|
|
else if (deleted)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_DELETED_SEVERAL,
|
|
deleted);
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_ADDED, mask);
|
|
|
|
if (WallOSSZLine) {
|
|
char buf[128];
|
|
|
|
if (!expires) {
|
|
strcpy(buf, "does not expire");
|
|
} else {
|
|
int wall_expiry = expires - time(NULL);
|
|
char *s = NULL;
|
|
|
|
if (wall_expiry >= 86400) {
|
|
wall_expiry /= 86400;
|
|
s = "day";
|
|
} else if (wall_expiry >= 3600) {
|
|
wall_expiry /= 3600;
|
|
s = "hour";
|
|
} else if (wall_expiry >= 60) {
|
|
wall_expiry /= 60;
|
|
s = "minute";
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "expires in %d %s%s",
|
|
wall_expiry, s,
|
|
(wall_expiry == 1) ? "" : "s");
|
|
}
|
|
|
|
wallops(s_OperServ, "%s added an SZLINE for %s (%s)",
|
|
u->nick, mask, buf);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SZLINE", OPER_SZLINE_SYNTAX);
|
|
}
|
|
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
|
|
char *mask;
|
|
int res = 0;
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask) {
|
|
syntax_error(s_OperServ, u, "SZLINE", OPER_SZLINE_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (szlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&szlines, mask, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_DELETED_SEVERAL,
|
|
res);
|
|
}
|
|
} else {
|
|
if ((res = slist_indexof(&szlines, mask)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NOT_FOUND, mask);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&szlines, res);
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_DELETED, mask);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (szlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&szlines, mask, &szline_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < szlines.count; i++) {
|
|
amask = ((SXLine *) szlines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
szline_list(i + 1, szlines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NO_MATCH);
|
|
}
|
|
} else if (!stricmp(cmd, "VIEW")) {
|
|
char *mask;
|
|
int res, sent_header = 0;
|
|
|
|
if (szlines.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
mask = strtok(NULL, " ");
|
|
|
|
if (!mask || (isdigit(*mask)
|
|
&& strspn(mask, "1234567890,-") == strlen(mask))) {
|
|
res =
|
|
slist_enum(&szlines, mask, &szline_view_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NO_MATCH);
|
|
return MOD_CONT;
|
|
}
|
|
} else {
|
|
int i;
|
|
char *amask;
|
|
|
|
for (i = 0; i < szlines.count; i++) {
|
|
amask = ((SXLine *) szlines.list[i])->mask;
|
|
if (!stricmp(mask, amask)
|
|
|| match_wild_nocase(mask, amask))
|
|
szline_view(i + 1, szlines.list[i], u, &sent_header);
|
|
}
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_NO_MATCH);
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
slist_clear(&szlines, 1);
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "SZLINE", OPER_SZLINE_SYNTAX);
|
|
}
|
|
#else
|
|
notice_lang(s_OperServ, u, OPER_SZLINE_UNSUPPORTED);
|
|
#endif
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_chanlist(User * u)
|
|
{
|
|
char *pattern = strtok(NULL, " ");
|
|
char *opt = strtok(NULL, " ");
|
|
|
|
int modes = 0;
|
|
User *u2;
|
|
|
|
if (opt && !stricmp(opt, "SECRET"))
|
|
modes |= (CMODE_s | CMODE_p);
|
|
|
|
if (pattern && (u2 = finduser(pattern))) {
|
|
struct u_chanlist *uc;
|
|
|
|
notice_lang(s_OperServ, u, OPER_CHANLIST_HEADER_USER, u2->nick);
|
|
|
|
for (uc = u2->chans; uc; uc = uc->next) {
|
|
if (modes && !(uc->chan->mode & modes))
|
|
continue;
|
|
notice_lang(s_OperServ, u, OPER_CHANLIST_RECORD,
|
|
uc->chan->name, uc->chan->usercount,
|
|
chan_get_modes(uc->chan, 1, 1),
|
|
(uc->chan->topic ? uc->chan->topic : ""));
|
|
}
|
|
} else {
|
|
int i;
|
|
Channel *c;
|
|
|
|
notice_lang(s_OperServ, u, OPER_CHANLIST_HEADER);
|
|
|
|
for (i = 0; i < 1024; i++) {
|
|
for (c = chanlist[i]; c; c = c->next) {
|
|
if (pattern && !match_wild_nocase(pattern, c->name))
|
|
continue;
|
|
if (modes && !(c->mode & modes))
|
|
continue;
|
|
notice_lang(s_OperServ, u, OPER_CHANLIST_RECORD, c->name,
|
|
c->usercount, chan_get_modes(c, 1, 1),
|
|
(c->topic ? c->topic : ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_CHANLIST_END);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_userlist(User * u)
|
|
{
|
|
char *pattern = strtok(NULL, " ");
|
|
char *opt = strtok(NULL, " ");
|
|
|
|
Channel *c;
|
|
int modes = 0;
|
|
|
|
if (opt && !stricmp(opt, "INVISIBLE"))
|
|
modes |= UMODE_i;
|
|
|
|
if (pattern && (c = findchan(pattern))) {
|
|
struct c_userlist *cu;
|
|
|
|
notice_lang(s_OperServ, u, OPER_USERLIST_HEADER_CHAN, pattern);
|
|
|
|
for (cu = c->users; cu; cu = cu->next) {
|
|
if (modes && !(cu->user->mode & modes))
|
|
continue;
|
|
notice_lang(s_OperServ, u, OPER_USERLIST_RECORD,
|
|
cu->user->nick, GetIdent(cu->user),
|
|
GetHost(cu->user));
|
|
}
|
|
} else {
|
|
char mask[BUFSIZE];
|
|
int i;
|
|
User *u2;
|
|
|
|
notice_lang(s_OperServ, u, OPER_USERLIST_HEADER);
|
|
|
|
for (i = 0; i < 1024; i++) {
|
|
for (u2 = userlist[i]; u2; u2 = u2->next) {
|
|
if (pattern) {
|
|
snprintf(mask, sizeof(mask), "%s!%s@%s", u2->nick,
|
|
GetIdent(u2), GetHost(u2));
|
|
if (!match_wild_nocase(pattern, mask))
|
|
continue;
|
|
if (modes && !(u2->mode & modes))
|
|
continue;
|
|
}
|
|
notice_lang(s_OperServ, u, OPER_USERLIST_RECORD, u2->nick,
|
|
GetIdent(u2), GetHost(u2));
|
|
}
|
|
}
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_USERLIST_END);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Callback function used to sort the admin list */
|
|
|
|
static int compare_adminlist_entries(SList * slist, void *item1,
|
|
void *item2)
|
|
{
|
|
NickCore *nc1 = item1, *nc2 = item2;
|
|
if (!nc1 || !nc2)
|
|
return -1; /* To tell to continue */
|
|
return stricmp(nc1->display, nc2->display);
|
|
}
|
|
|
|
/* Callback function used when an admin list entry is deleted */
|
|
|
|
static void free_adminlist_entry(SList * slist, void *item)
|
|
{
|
|
NickCore *nc = item;
|
|
nc->flags &= ~NI_SERVICES_ADMIN;
|
|
}
|
|
|
|
/* Lists an admin entry, prefixing it with the header if needed */
|
|
|
|
static int admin_list(int number, NickCore * nc, User * u,
|
|
int *sent_header)
|
|
{
|
|
if (!nc)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_LIST_FORMAT, number,
|
|
nc->display);
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int admin_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return admin_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/* Services admin list viewing/modification. */
|
|
|
|
static int do_admin(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
char *nick = strtok(NULL, " ");
|
|
NickAlias *na;
|
|
int res = 0;
|
|
|
|
if (skeleton) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_SKELETON);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!cmd || (!nick && stricmp(cmd, "LIST") && stricmp(cmd, "CLEAR"))) {
|
|
syntax_error(s_OperServ, u, "ADMIN", OPER_ADMIN_SYNTAX);
|
|
} else if (!stricmp(cmd, "ADD")) {
|
|
if (!is_services_root(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!(na = findnick(nick))) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_REGISTERED, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->status & NS_VERBOTEN) {
|
|
notice_lang(s_OperServ, u, NICK_X_FORBIDDEN, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->nc->flags & NI_SERVICES_ADMIN
|
|
|| slist_indexof(&servadmins, na->nc) != -1) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_EXISTS, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
res = slist_add(&servadmins, na->nc);
|
|
if (res == -2) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_REACHED_LIMIT, nick);
|
|
return MOD_CONT;
|
|
} else {
|
|
na->nc->flags |= NI_SERVICES_ADMIN;
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_ADDED, nick);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
if (!is_services_root(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (servadmins.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&servadmins, nick, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_DELETED_SEVERAL,
|
|
res);
|
|
}
|
|
} else {
|
|
if (!(na = findnick(nick))) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_REGISTERED, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->status & NS_VERBOTEN) {
|
|
notice_lang(s_OperServ, u, NICK_X_FORBIDDEN, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!(na->nc->flags & NI_SERVICES_ADMIN)
|
|
|| (res = slist_indexof(&servadmins, na->nc)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_NOT_FOUND, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&servadmins, res);
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_DELETED, nick);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
int sent_header = 0;
|
|
|
|
if (servadmins.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!nick || (isdigit(*nick)
|
|
&& strspn(nick, "1234567890,-") == strlen(nick))) {
|
|
res =
|
|
slist_enum(&servadmins, nick, &admin_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Admin");
|
|
}
|
|
} else {
|
|
int i;
|
|
|
|
for (i = 0; i < servadmins.count; i++)
|
|
if (!stricmp
|
|
(nick, ((NickCore *) servadmins.list[i])->display)
|
|
|| match_wild_nocase(nick,
|
|
((NickCore *) servadmins.
|
|
list[i])->display))
|
|
admin_list(i + 1, servadmins.list[i], u, &sent_header);
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_NO_MATCH);
|
|
else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Admin");
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
if (!is_services_root(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (servadmins.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_clear(&servadmins, 1);
|
|
notice_lang(s_OperServ, u, OPER_ADMIN_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "ADMIN", OPER_ADMIN_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Callback function used to sort the oper list */
|
|
|
|
static int compare_operlist_entries(SList * slist, void *item1,
|
|
void *item2)
|
|
{
|
|
NickCore *nc1 = item1, *nc2 = item2;
|
|
if (!nc1 || !nc2)
|
|
return -1; /* To tell to continue */
|
|
return stricmp(nc1->display, nc2->display);
|
|
}
|
|
|
|
/* Callback function used when an oper list entry is deleted */
|
|
|
|
static void free_operlist_entry(SList * slist, void *item)
|
|
{
|
|
NickCore *nc = item;
|
|
nc->flags &= ~NI_SERVICES_OPER;
|
|
}
|
|
|
|
/* Lists an oper entry, prefixing it with the header if needed */
|
|
|
|
static int oper_list(int number, NickCore * nc, User * u, int *sent_header)
|
|
{
|
|
if (!nc)
|
|
return 0;
|
|
|
|
if (!*sent_header) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_LIST_HEADER);
|
|
*sent_header = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_OPER_LIST_FORMAT, number, nc->display);
|
|
return 1;
|
|
}
|
|
|
|
/* Callback for enumeration purposes */
|
|
|
|
static int oper_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
int *sent_header = va_arg(args, int *);
|
|
|
|
return oper_list(number, item, u, sent_header);
|
|
}
|
|
|
|
/**
|
|
* Display an Opers list Entry
|
|
**/
|
|
static int opers_list(int number, NickCore * nc, User * u, char *level)
|
|
{
|
|
User *au = NULL;
|
|
NickAlias *na;
|
|
int found;
|
|
int i;
|
|
|
|
if (!nc)
|
|
return 0;
|
|
|
|
found = 0;
|
|
if ((au = finduser(nc->display))) { /* see if user is online */
|
|
found = 1;
|
|
notice_lang(s_OperServ, u, OPER_STAFF_FORMAT, '*', level,
|
|
nc->display);
|
|
} else {
|
|
for (i = 0; i < nc->aliases.count; i++) { /* check all aliases */
|
|
na = nc->aliases.list[i];
|
|
if ((au = finduser(na->nick))) { /* see if user is online */
|
|
found = 1;
|
|
notice_lang(s_OperServ, u, OPER_STAFF_AFORMAT, '*', level,
|
|
nc->display, na->nick);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
notice_lang(s_OperServ, u, OPER_STAFF_FORMAT, ' ', level,
|
|
nc->display);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Function for the enumerator to call
|
|
**/
|
|
static int opers_list_callback(SList * slist, int number, void *item,
|
|
va_list args)
|
|
{
|
|
User *u = va_arg(args, User *);
|
|
char *level = va_arg(args, char *);
|
|
|
|
return opers_list(number, item, u, level);
|
|
}
|
|
|
|
/**
|
|
* Display all Services Opers/Admins with Level + Online Status
|
|
* /msg OperServ opers
|
|
**/
|
|
static int do_staff(User * u)
|
|
{
|
|
int idx = 0;
|
|
User *au = NULL;
|
|
NickCore *nc;
|
|
NickAlias *na;
|
|
int found;
|
|
int i;
|
|
|
|
notice_lang(s_OperServ, u, OPER_STAFF_LIST_HEADER);
|
|
slist_enum(&servopers, NULL, &opers_list_callback, u, "OPER");
|
|
slist_enum(&servadmins, NULL, &opers_list_callback, u, "ADMN");
|
|
|
|
for (idx = 0; idx < RootNumber; idx++) {
|
|
found = 0;
|
|
if ((au = finduser(ServicesRoots[idx]))) { /* see if user is online */
|
|
found = 1;
|
|
notice_lang(s_OperServ, u, OPER_STAFF_FORMAT, '*', "ROOT",
|
|
ServicesRoots[idx]);
|
|
} else if ((nc = findcore(ServicesRoots[idx]))) {
|
|
for (i = 0; i < nc->aliases.count; i++) { /* check all aliases */
|
|
na = nc->aliases.list[i];
|
|
if ((au = finduser(na->nick))) { /* see if user is online */
|
|
found = 1;
|
|
notice_lang(s_OperServ, u, OPER_STAFF_AFORMAT,
|
|
'*', "ROOT", ServicesRoots[idx], na->nick);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
notice_lang(s_OperServ, u, OPER_STAFF_FORMAT, ' ', "ROOT",
|
|
ServicesRoots[idx]);
|
|
|
|
}
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Staff");
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/* Services operator list viewing/modification. */
|
|
|
|
static int do_oper(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
char *nick = strtok(NULL, " ");
|
|
NickAlias *na;
|
|
int res = 0;
|
|
|
|
if (skeleton) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_SKELETON);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!cmd || (!nick && stricmp(cmd, "LIST") && stricmp(cmd, "CLEAR"))) {
|
|
syntax_error(s_OperServ, u, "OPER", OPER_OPER_SYNTAX);
|
|
} else if (!stricmp(cmd, "ADD")) {
|
|
if (!is_services_admin(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!(na = findnick(nick))) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_REGISTERED, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->status & NS_VERBOTEN) {
|
|
notice_lang(s_OperServ, u, NICK_X_FORBIDDEN, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->nc->flags & NI_SERVICES_OPER
|
|
|| slist_indexof(&servopers, na->nc) != -1) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_EXISTS, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
res = slist_add(&servopers, na->nc);
|
|
if (res == -2) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_REACHED_LIMIT, nick);
|
|
return MOD_CONT;
|
|
} else {
|
|
na->nc->flags |= NI_SERVICES_OPER;
|
|
notice_lang(s_OperServ, u, OPER_OPER_ADDED, nick);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
} else if (!stricmp(cmd, "DEL")) {
|
|
if (!is_services_admin(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
|
|
/* Deleting a range */
|
|
res = slist_delete_range(&servopers, nick, NULL);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else if (res == 1) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_DELETED_ONE);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_OPER_DELETED_SEVERAL, res);
|
|
}
|
|
} else {
|
|
if (!(na = findnick(nick))) {
|
|
notice_lang(s_OperServ, u, NICK_X_NOT_REGISTERED, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (na->status & NS_VERBOTEN) {
|
|
notice_lang(s_OperServ, u, NICK_X_FORBIDDEN, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!(na->nc->flags & NI_SERVICES_OPER)
|
|
|| (res = slist_indexof(&servopers, na->nc)) == -1) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_NOT_FOUND, nick);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_delete(&servopers, res);
|
|
notice_lang(s_OperServ, u, OPER_OPER_DELETED, nick);
|
|
}
|
|
|
|
if (readonly)
|
|
notice_lang(s_OperServ, u, READ_ONLY_MODE);
|
|
} else if (!stricmp(cmd, "LIST")) {
|
|
int sent_header = 0;
|
|
|
|
if (servopers.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!nick || (isdigit(*nick)
|
|
&& strspn(nick, "1234567890,-") == strlen(nick))) {
|
|
res =
|
|
slist_enum(&servopers, nick, &oper_list_callback, u,
|
|
&sent_header);
|
|
if (res == 0) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_NO_MATCH);
|
|
return MOD_CONT;
|
|
} else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Oper");
|
|
}
|
|
} else {
|
|
int i;
|
|
|
|
for (i = 0; i < servopers.count; i++)
|
|
if (!stricmp
|
|
(nick, ((NickCore *) servopers.list[i])->display)
|
|
|| match_wild_nocase(nick,
|
|
((NickCore *) servopers.list[i])->
|
|
display))
|
|
oper_list(i + 1, servopers.list[i], u, &sent_header);
|
|
|
|
if (!sent_header)
|
|
notice_lang(s_OperServ, u, OPER_OPER_NO_MATCH);
|
|
else {
|
|
notice_lang(s_OperServ, u, END_OF_ANY_LIST, "Oper");
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "CLEAR")) {
|
|
if (!is_services_admin(u)) {
|
|
notice_lang(s_OperServ, u, PERMISSION_DENIED);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (servopers.count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_OPER_LIST_EMPTY);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
slist_clear(&servopers, 1);
|
|
notice_lang(s_OperServ, u, OPER_OPER_CLEAR);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "OPER", OPER_OPER_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Set various Services runtime options. */
|
|
|
|
static int do_set(User * u)
|
|
{
|
|
char *option = strtok(NULL, " ");
|
|
char *setting = strtok(NULL, " ");
|
|
|
|
if (!option || !setting) {
|
|
syntax_error(s_OperServ, u, "SET", OPER_SET_SYNTAX);
|
|
|
|
} else if (stricmp(option, "IGNORE") == 0) {
|
|
if (stricmp(setting, "on") == 0) {
|
|
allow_ignore = 1;
|
|
notice_lang(s_OperServ, u, OPER_SET_IGNORE_ON);
|
|
} else if (stricmp(setting, "off") == 0) {
|
|
allow_ignore = 0;
|
|
notice_lang(s_OperServ, u, OPER_SET_IGNORE_OFF);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_IGNORE_ERROR);
|
|
}
|
|
|
|
} else if (stricmp(option, "READONLY") == 0) {
|
|
if (stricmp(setting, "on") == 0) {
|
|
readonly = 1;
|
|
alog("Read-only mode activated");
|
|
close_log();
|
|
notice_lang(s_OperServ, u, OPER_SET_READONLY_ON);
|
|
} else if (stricmp(setting, "off") == 0) {
|
|
readonly = 0;
|
|
open_log();
|
|
alog("Read-only mode deactivated");
|
|
notice_lang(s_OperServ, u, OPER_SET_READONLY_OFF);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_READONLY_ERROR);
|
|
}
|
|
|
|
} else if (stricmp(option, "LOGCHAN") == 0) {
|
|
/* Unlike the other SET commands where only stricmp is necessary,
|
|
* we also have to ensure that LogChannel is defined or we can't
|
|
* send to it.
|
|
*
|
|
* -jester
|
|
*/
|
|
if (LogChannel && (stricmp(setting, "on") == 0)) {
|
|
#ifdef IRC_HYBRID
|
|
send_cmd(NULL, "SJOIN %ld %s + :%s", time(NULL), LogChannel,
|
|
s_GlobalNoticer);
|
|
#endif
|
|
logchan = 1;
|
|
alog("Now sending log messages to %s", LogChannel);
|
|
notice_lang(s_OperServ, u, OPER_SET_LOGCHAN_ON, LogChannel);
|
|
} else if (LogChannel && (stricmp(setting, "off") == 0)) {
|
|
#ifdef IRC_HYBRID
|
|
send_cmd(s_GlobalNoticer, "PART %s :Parting", LogChannel);
|
|
#endif
|
|
logchan = 0;
|
|
alog("No longer sending log messages to a channel");
|
|
notice_lang(s_OperServ, u, OPER_SET_LOGCHAN_OFF);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_LOGCHAN_ERROR);
|
|
}
|
|
/**
|
|
* Allow the user to turn super admin on/off
|
|
*
|
|
* Rob
|
|
**/
|
|
} else if (stricmp(option, "SUPERADMIN") == 0) {
|
|
if (SuperAdmin && (stricmp(setting, "on") == 0)) {
|
|
u->isSuperAdmin = 1;
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_ON);
|
|
alog("%s: %s is a SuperAdmin ", s_OperServ, u->nick);
|
|
wallops(s_OperServ, getstring2(NULL, OPER_SUPER_ADMIN_WALL_ON),
|
|
u->nick);
|
|
} else if (SuperAdmin && (stricmp(setting, "off") == 0)) {
|
|
u->isSuperAdmin = 0;
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_OFF);
|
|
alog("%s: %s is no longer a SuperAdmin", s_OperServ, u->nick);
|
|
wallops(s_OperServ,
|
|
getstring2(NULL, OPER_SUPER_ADMIN_WALL_OFF), u->nick);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SUPER_ADMIN_SYNTAX);
|
|
}
|
|
} else if (stricmp(option, "DEBUG") == 0) {
|
|
if (stricmp(setting, "on") == 0) {
|
|
debug = 1;
|
|
alog("Debug mode activated");
|
|
notice_lang(s_OperServ, u, OPER_SET_DEBUG_ON);
|
|
} else if (stricmp(setting, "off") == 0 ||
|
|
(*setting == '0' && atoi(setting) == 0)) {
|
|
alog("Debug mode deactivated");
|
|
debug = 0;
|
|
notice_lang(s_OperServ, u, OPER_SET_DEBUG_OFF);
|
|
} else if (isdigit(*setting) && atoi(setting) > 0) {
|
|
debug = atoi(setting);
|
|
alog("Debug mode activated (level %d)", debug);
|
|
notice_lang(s_OperServ, u, OPER_SET_DEBUG_LEVEL, debug);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_DEBUG_ERROR);
|
|
}
|
|
|
|
} else if (stricmp(option, "NOEXPIRE") == 0) {
|
|
if (stricmp(setting, "ON") == 0) {
|
|
noexpire = 1;
|
|
alog("No expire mode activated");
|
|
notice_lang(s_OperServ, u, OPER_SET_NOEXPIRE_ON);
|
|
} else if (stricmp(setting, "OFF") == 0) {
|
|
noexpire = 0;
|
|
alog("No expire mode deactivated");
|
|
notice_lang(s_OperServ, u, OPER_SET_NOEXPIRE_OFF);
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_NOEXPIRE_ERROR);
|
|
}
|
|
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_SET_UNKNOWN_OPTION, option);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_noop(User * u)
|
|
{
|
|
char *cmd = strtok(NULL, " ");
|
|
char *server = strtok(NULL, " ");
|
|
|
|
if (!cmd || !server) {
|
|
syntax_error(s_OperServ, u, "NOOP", OPER_NOOP_SYNTAX);
|
|
} else if (!stricmp(cmd, "SET")) {
|
|
User *u2;
|
|
User *u3 = NULL;
|
|
char reason[NICKMAX + 32];
|
|
|
|
/* Remove the O:lines */
|
|
s_svsnoop(server, 1);
|
|
|
|
snprintf(reason, sizeof(reason), "NOOP command used by %s",
|
|
u->nick);
|
|
if (WallOSNoOp)
|
|
wallops(s_OperServ, "\2%s\2 used NOOP on \2%s\2", u->nick,
|
|
server);
|
|
notice_lang(s_OperServ, u, OPER_NOOP_SET, server);
|
|
|
|
/* Kill all the IRCops of the server */
|
|
for (u2 = firstuser(); u2; u2 = u3) {
|
|
u3 = nextuser();
|
|
if ((u2) && is_oper(u2) && (u2->server)
|
|
&& !stricmp(u2->server, server)) {
|
|
#ifdef IRC_BAHAMUT
|
|
send_cmd(ServerName, "SVSKILL %s :%s", u2->nick, reason);
|
|
#else
|
|
kill_user(s_OperServ, u2->nick, reason);
|
|
#endif
|
|
}
|
|
}
|
|
} else if (!stricmp(cmd, "REVOKE")) {
|
|
s_svsnoop(server, 0);
|
|
notice_lang(s_OperServ, u, OPER_NOOP_REVOKE, server);
|
|
} else {
|
|
syntax_error(s_OperServ, u, "NOOP", OPER_NOOP_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_jupe(User * u)
|
|
{
|
|
char *jserver = strtok(NULL, " ");
|
|
char *reason = strtok(NULL, "");
|
|
char rbuf[256];
|
|
|
|
if (!jserver) {
|
|
syntax_error(s_OperServ, u, "JUPE", OPER_JUPE_SYNTAX);
|
|
} else {
|
|
if (!isValidHost(jserver, 3)) {
|
|
notice_lang(s_OperServ, u, OPER_JUPE_HOST_ERROR);
|
|
} else {
|
|
snprintf(rbuf, sizeof(rbuf), "Juped by %s%s%s", u->nick,
|
|
reason ? ": " : "", reason ? reason : "");
|
|
|
|
send_cmd(NULL, "SQUIT %s :%s", jserver, rbuf);
|
|
#ifdef IRC_PTLINK
|
|
send_cmd(NULL, "SERVER %s 1 Anope.Services%s :%s",
|
|
jserver, version_number, rbuf);
|
|
#else
|
|
send_cmd(NULL, "SERVER %s 2 :%s", jserver, rbuf);
|
|
#endif
|
|
|
|
if (WallOSJupe)
|
|
wallops(s_OperServ, "\2%s\2 used JUPE on \2%s\2", u->nick,
|
|
jserver);
|
|
}
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_raw(User * u)
|
|
{
|
|
|
|
if (!DisableRaw) {
|
|
|
|
char *text = strtok(NULL, "");
|
|
|
|
if (!text)
|
|
syntax_error(s_OperServ, u, "RAW", OPER_RAW_SYNTAX);
|
|
else {
|
|
send_cmd(NULL, "%s", text);
|
|
if (WallOSRaw) {
|
|
char *kw = strtok(text, " ");
|
|
while (kw && *kw == ':')
|
|
kw = strtok(NULL, " ");
|
|
wallops(s_OperServ, "\2%s\2 used RAW command for \2%s\2",
|
|
u->nick,
|
|
(kw ? kw : "\2non RFC compliant message\2"));
|
|
}
|
|
alog("%s used RAW command for %s", u->nick, text);
|
|
}
|
|
} else {
|
|
notice_lang(s_OperServ, u, RAW_DISABLED);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_update(User * u)
|
|
{
|
|
notice_lang(s_OperServ, u, OPER_UPDATING);
|
|
save_data = 1;
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_reload(User * u)
|
|
{
|
|
if (!read_config(1)) {
|
|
quitmsg = calloc(28 + strlen(u->nick), 1);
|
|
if (!quitmsg)
|
|
quitmsg =
|
|
"Error during the reload of the configuration file, but out of memory!";
|
|
else
|
|
sprintf(quitmsg,
|
|
"Error during the reload of the configuration file!");
|
|
quitting = 1;
|
|
}
|
|
|
|
notice_lang(s_OperServ, u, OPER_RELOAD);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_os_quit(User * u)
|
|
{
|
|
quitmsg = calloc(28 + strlen(u->nick), 1);
|
|
if (!quitmsg)
|
|
quitmsg = "QUIT command received, but out of memory!";
|
|
else
|
|
sprintf(quitmsg, "QUIT command received from %s", u->nick);
|
|
|
|
if (GlobalOnCycle) {
|
|
oper_global(NULL, GlobalOnCycleMessage);
|
|
}
|
|
quitting = 1;
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_shutdown(User * u)
|
|
{
|
|
quitmsg = calloc(32 + strlen(u->nick), 1);
|
|
if (!quitmsg)
|
|
quitmsg = "SHUTDOWN command received, but out of memory!";
|
|
else
|
|
sprintf(quitmsg, "SHUTDOWN command received from %s", u->nick);
|
|
|
|
if (GlobalOnCycle) {
|
|
oper_global(NULL, GlobalOnCycleMessage);
|
|
}
|
|
save_data = 1;
|
|
delayed_quit = 1;
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_restart(User * u)
|
|
{
|
|
#ifdef SERVICES_BIN
|
|
quitmsg = calloc(31 + strlen(u->nick), 1);
|
|
if (!quitmsg)
|
|
quitmsg = "RESTART command received, but out of memory!";
|
|
else
|
|
sprintf(quitmsg, "RESTART command received from %s", u->nick);
|
|
|
|
if (GlobalOnCycle) {
|
|
oper_global(NULL, GlobalOnCycleMessage);
|
|
}
|
|
/* raise(SIGHUP); */
|
|
do_restart_services();
|
|
#else
|
|
notice_lang(s_OperServ, u, OPER_CANNOT_RESTART);
|
|
#endif
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
#ifdef DEBUG_COMMANDS
|
|
|
|
static int do_matchwild(User * u)
|
|
{
|
|
char *pat = strtok(NULL, " ");
|
|
char *str = strtok(NULL, " ");
|
|
if (pat && str)
|
|
notice(s_OperServ, u->nick, "%d", match_wild(pat, str));
|
|
else
|
|
notice(s_OperServ, u->nick, "Syntax error.");
|
|
return MOD_CONT;
|
|
}
|
|
|
|
#endif /* DEBUG_COMMANDS */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Kill all users matching a certain host. The host is obtained from the
|
|
* supplied nick. The raw hostmsk is not supplied with the command in an effort
|
|
* to prevent abuse and mistakes from being made - which might cause *.com to
|
|
* be killed. It also makes it very quick and simple to use - which is usually
|
|
* what you want when someone starts loading numerous clones. In addition to
|
|
* killing the clones, we add a temporary AKILL to prevent them from
|
|
* immediately reconnecting.
|
|
* Syntax: KILLCLONES nick
|
|
* -TheShadow (29 Mar 1999)
|
|
*/
|
|
|
|
static int do_killclones(User * u)
|
|
{
|
|
char *clonenick = strtok(NULL, " ");
|
|
int count = 0;
|
|
User *cloneuser, *user, *tempuser;
|
|
char *clonemask, *akillmask;
|
|
char killreason[NICKMAX + 32];
|
|
char akillreason[] = "Temporary KILLCLONES akill.";
|
|
|
|
if (!clonenick) {
|
|
notice_lang(s_OperServ, u, OPER_KILLCLONES_SYNTAX);
|
|
|
|
} else if (!(cloneuser = finduser(clonenick))) {
|
|
notice_lang(s_OperServ, u, OPER_KILLCLONES_UNKNOWN_NICK,
|
|
clonenick);
|
|
|
|
} else {
|
|
clonemask = smalloc(strlen(cloneuser->host) + 5);
|
|
sprintf(clonemask, "*!*@%s", cloneuser->host);
|
|
|
|
akillmask = smalloc(strlen(cloneuser->host) + 3);
|
|
sprintf(akillmask, "*@%s", cloneuser->host);
|
|
|
|
user = firstuser();
|
|
while (user) {
|
|
if (match_usermask(clonemask, user) != 0) {
|
|
tempuser = nextuser();
|
|
count++;
|
|
snprintf(killreason, sizeof(killreason),
|
|
"Cloning [%d]", count);
|
|
kill_user(NULL, user->nick, killreason);
|
|
user = tempuser;
|
|
} else {
|
|
user = nextuser();
|
|
}
|
|
}
|
|
|
|
add_akill(u, akillmask, u->nick,
|
|
time(NULL) + KillClonesAkillExpire, akillreason);
|
|
|
|
wallops(s_OperServ, "\2%s\2 used KILLCLONES for \2%s\2 killing "
|
|
"\2%d\2 clones. A temporary AKILL has been added "
|
|
"for \2%s\2.", u->nick, clonemask, count, akillmask);
|
|
|
|
alog("%s: KILLCLONES: %d clone(s) matching %s killed.",
|
|
s_OperServ, count, clonemask);
|
|
|
|
free(akillmask);
|
|
free(clonemask);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**
|
|
* Defcon - A method of impelemting various stages of securty, the hope is this will help serives
|
|
* protect a network during an attack, allowing admins to choose the precautions taken at each
|
|
* level.
|
|
*
|
|
* /msg OperServ DefCon [level]
|
|
*
|
|
**/
|
|
|
|
static int do_defcon(User * u)
|
|
{
|
|
char *lvl = strtok(NULL, " ");
|
|
int newLevel = 0;
|
|
char *langglobal;
|
|
langglobal = getstring(NULL, DEFCON_GLOBAL);
|
|
|
|
if (!DefConLevel) { /* If we dont have a .conf setting! */
|
|
notice_lang(s_OperServ, u, OPER_DEFCON_NO_CONF);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
if (!lvl) {
|
|
notice_lang(s_OperServ, u, OPER_DEFCON_CHANGED, DefConLevel);
|
|
defcon_sendlvls(u);
|
|
return MOD_CONT;
|
|
}
|
|
newLevel = atoi(lvl);
|
|
if (newLevel < 1 || newLevel > 5) {
|
|
notice_lang(s_OperServ, u, OPER_DEFCON_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
|
|
DefConLevel = newLevel;
|
|
DefContimer = time(NULL);
|
|
notice_lang(s_OperServ, u, OPER_DEFCON_CHANGED, DefConLevel);
|
|
defcon_sendlvls(u);
|
|
alog("Defcon level changed to %d by Oper %s", newLevel, u->nick);
|
|
wallops(s_OperServ, getstring2(NULL, OPER_DEFCON_WALL), u->nick,
|
|
newLevel);
|
|
/* Global notice the user what is happening. Also any Message that
|
|
the Admin would like to add. Set in config file. */
|
|
if (GlobalOnDefcon) {
|
|
if ((DefConLevel == 5) && (DefConOffMessage)) {
|
|
oper_global(NULL, DefConOffMessage);
|
|
} else {
|
|
oper_global(NULL, langglobal, DefConLevel);
|
|
}
|
|
}
|
|
if (GlobalOnDefconMore) {
|
|
if ((DefConOffMessage) && DefConLevel == 5) {
|
|
} else {
|
|
oper_global(NULL, DefconMessage);
|
|
}
|
|
}
|
|
/* Run any defcon functions, e.g. FORCE CHAN MODE */
|
|
runDefCon();
|
|
return MOD_CONT;
|
|
}
|
|
|
|
/**
|
|
* Reverse the mode string, used for remove DEFCON chan modes.
|
|
**/
|
|
char *defconReverseModes(const char *modes)
|
|
{
|
|
char *newmodes = NULL;
|
|
int i = 0;
|
|
if (!modes) {
|
|
return NULL;
|
|
}
|
|
if (!(newmodes = malloc(sizeof(char) * strlen(modes) + 1))) {
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < strlen(modes); i++) {
|
|
if (modes[i] == '+')
|
|
newmodes[i] = '-';
|
|
else if (modes[i] == '-')
|
|
newmodes[i] = '+';
|
|
else
|
|
newmodes[i] = modes[i];
|
|
}
|
|
newmodes[i] = '\0';
|
|
return newmodes;
|
|
}
|
|
|
|
/**
|
|
* Returns 1 if the passed level is part of the CURRENT defcon, else 0 is returned
|
|
**/
|
|
int checkDefCon(int level)
|
|
{
|
|
return DefCon[DefConLevel] & level;
|
|
}
|
|
|
|
/**
|
|
* Run DefCon level specific Functions.
|
|
**/
|
|
void runDefCon(void)
|
|
{
|
|
char *newmodes;
|
|
if (checkDefCon(DEFCON_FORCE_CHAN_MODES)) {
|
|
if (DefConChanModes && !DefConModesSet) {
|
|
if (DefConChanModes[0] == '+' || DefConChanModes[0] == '-') {
|
|
alog("DEFCON: setting %s on all chan's", DefConChanModes);
|
|
do_mass_mode(DefConChanModes);
|
|
DefConModesSet = 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (DefConChanModes && (DefConModesSet != 0)) {
|
|
if (DefConChanModes[0] == '+' || DefConChanModes[0] == '-') {
|
|
if ((newmodes = defconReverseModes(DefConChanModes))) {
|
|
alog("DEFCON: setting %s on all chan's", newmodes);
|
|
do_mass_mode(newmodes);
|
|
}
|
|
DefConModesSet = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Automaticaly re-set the DefCon level if the time limit has expired.
|
|
**/
|
|
void resetDefCon(int level)
|
|
{
|
|
if (DefConLevel != level) {
|
|
if ((DefContimer)
|
|
&& (time(NULL) - DefContimer >= dotime(DefConTimeOut))) {
|
|
DefConLevel = level;
|
|
alog("Defcon level timeout, returning to lvl %d", level);
|
|
wallops(s_OperServ, getstring2(NULL, OPER_DEFCON_WALL),
|
|
s_OperServ, level);
|
|
if (GlobalOnDefcon) {
|
|
if (DefConOffMessage) {
|
|
oper_global(NULL, DefConOffMessage);
|
|
} else {
|
|
oper_global(NULL, getstring(NULL, DEFCON_GLOBAL),
|
|
DefConLevel);
|
|
}
|
|
}
|
|
if (GlobalOnDefconMore && !DefConOffMessage) {
|
|
oper_global(NULL, DefconMessage);
|
|
}
|
|
runDefCon();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send a message to the oper about which precautions are "active" for this level
|
|
**/
|
|
static void defcon_sendlvls(User * u)
|
|
{
|
|
if (checkDefCon(DEFCON_NO_NEW_CHANNELS)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_CHANNELS);
|
|
}
|
|
if (checkDefCon(DEFCON_NO_NEW_NICKS)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_NICKS);
|
|
}
|
|
if (checkDefCon(DEFCON_NO_MLOCK_CHANGE)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_NO_MLOCK_CHANGE);
|
|
}
|
|
if (checkDefCon(DEFCON_FORCE_CHAN_MODES) && (DefConChanModes)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_FORCE_CHAN_MODES,
|
|
DefConChanModes);
|
|
}
|
|
if (checkDefCon(DEFCON_REDUCE_SESSION)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_REDUCE_SESSION,
|
|
DefConSessionLimit);
|
|
}
|
|
if (checkDefCon(DEFCON_NO_NEW_CLIENTS)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_CLIENTS);
|
|
}
|
|
if (checkDefCon(DEFCON_OPER_ONLY)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_OPER_ONLY);
|
|
}
|
|
if (checkDefCon(DEFCON_SILENT_OPER_ONLY)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_SILENT_OPER_ONLY);
|
|
}
|
|
if (checkDefCon(DEFCON_AKILL_NEW_CLIENTS)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_AKILL_NEW_CLIENTS);
|
|
}
|
|
if (checkDefCon(DEFCON_NO_NEW_MEMOS)) {
|
|
notice_lang(s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_MEMOS);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ChanKill - Akill an entire channel (got botnet?)
|
|
*
|
|
* /msg OperServ ChanKill +expire #channel reason
|
|
*
|
|
**/
|
|
|
|
static int do_chankill(User * u)
|
|
{
|
|
char *expiry, *channel, *reason;
|
|
time_t expires;
|
|
char breason[BUFSIZE];
|
|
char mask[USERMAX + HOSTMAX + 2];
|
|
struct c_userlist *cu, *next;
|
|
Channel *c;
|
|
|
|
channel = strtok(NULL, " ");
|
|
if (channel && *channel == '+') {
|
|
expiry = channel;
|
|
channel = strtok(NULL, " ");
|
|
} else {
|
|
expiry = NULL;
|
|
}
|
|
|
|
expires = expiry ? dotime(expiry) : ChankillExpiry;
|
|
if (expiry && isdigit(expiry[strlen(expiry) - 1]))
|
|
expires *= 86400;
|
|
if (expires != 0 && expires < 60) {
|
|
notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
|
|
return MOD_CONT;
|
|
} else if (expires > 0) {
|
|
expires += time(NULL);
|
|
}
|
|
|
|
if (channel && (reason = strtok(NULL, ""))) {
|
|
|
|
if (AddAkiller) {
|
|
snprintf(breason, sizeof(breason), "[%s] %s", u->nick, reason);
|
|
reason = sstrdup(breason);
|
|
}
|
|
|
|
if ((c = findchan(channel))) {
|
|
for (cu = c->users; cu; cu = next) {
|
|
next = cu->next;
|
|
if (is_oper(cu->user)) {
|
|
continue;
|
|
}
|
|
strncpy(mask, "*@", 3); /* Use *@" for the akill's, */
|
|
strncat(mask, cu->user->host, HOSTMAX);
|
|
add_akill(NULL, mask, s_OperServ, expires, reason);
|
|
check_akill(cu->user->nick, cu->user->username,
|
|
cu->user->host, NULL, NULL);
|
|
}
|
|
if (WallOSAkill) {
|
|
wallops(s_OperServ, "%s used CHANKILL on %s (%s)", u->nick,
|
|
channel, reason);
|
|
}
|
|
} else {
|
|
notice_lang(s_OperServ, u, CHAN_X_NOT_IN_USE, channel);
|
|
}
|
|
} else {
|
|
syntax_error(s_OperServ, u, "CHANKILL", OPER_CHANKILL_SYNTAX);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
#ifdef USE_MODULES
|
|
|
|
int do_modload(User * u)
|
|
{
|
|
char *name;
|
|
Module *m;
|
|
|
|
name = strtok(NULL, "");
|
|
if (!name) {
|
|
syntax_error(s_OperServ, u, "MODLOAD", OPER_MODULE_LOAD_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
m = findModule(name);
|
|
if (!m) {
|
|
m = createModule(name);
|
|
mod_current_module = m;
|
|
mod_current_user = u;
|
|
mod_current_op = 1;
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_LOAD_FAIL, name);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
int do_modunload(User * u)
|
|
{
|
|
char *name;
|
|
Module *m;
|
|
|
|
name = strtok(NULL, "");
|
|
if (!name) {
|
|
syntax_error(s_OperServ, u, "MODUNLOAD",
|
|
OPER_MODULE_UNLOAD_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
m = findModule(name);
|
|
if (m) {
|
|
mod_current_user = u;
|
|
mod_current_module = m;
|
|
mod_current_op = 2;
|
|
} else {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_REMOVE_FAIL, name);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
int do_modlist(User * u)
|
|
{
|
|
int idx;
|
|
int count = 0;
|
|
struct tm tm;
|
|
char timebuf[64];
|
|
ModuleHash *current = NULL;
|
|
|
|
for (idx = 0; idx != MAX_CMD_HASH; idx++) {
|
|
for (current = MODULE_HASH[idx]; current; current = current->next) {
|
|
tm = *localtime(¤t->m->time);
|
|
|
|
strftime_lang(timebuf, sizeof(timebuf), u,
|
|
STRFTIME_DATE_TIME_FORMAT, &tm);
|
|
notice_lang(s_OperServ, u, OPER_MODULE_LIST, current->name);
|
|
count++;
|
|
}
|
|
}
|
|
if (count == 0) {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_NO_LIST);
|
|
}
|
|
|
|
return MOD_CONT;
|
|
}
|
|
|
|
int do_modinfo(User * u)
|
|
{
|
|
char *file;
|
|
struct tm tm;
|
|
char timebuf[64];
|
|
Module *m;
|
|
int idx = 0;
|
|
int display = 0;
|
|
|
|
file = strtok(NULL, "");
|
|
if (!file) {
|
|
syntax_error(s_OperServ, u, "MODINFO", OPER_MODULE_INFO_SYNTAX);
|
|
return MOD_CONT;
|
|
}
|
|
m = findModule(file);
|
|
if (m) {
|
|
tm = *localtime(&m->time);
|
|
strftime_lang(timebuf, sizeof(timebuf), u,
|
|
STRFTIME_DATE_TIME_FORMAT, &tm);
|
|
notice_lang(s_OperServ, u, OPER_MODULE_INFO_LIST, m->name,
|
|
m->version ? m->version : "?",
|
|
m->author ? m->author : "?", timebuf);
|
|
for (idx = 0; idx < MAX_CMD_HASH; idx++) {
|
|
display += showModuleCmdLoaded(HOSTSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(OPERSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(NICKSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(CHANSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(BOTSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(MEMOSERV[idx], m->name, u);
|
|
display += showModuleCmdLoaded(HELPSERV[idx], m->name, u);
|
|
display += showModuleMsgLoaded(IRCD[idx], m->name, u);
|
|
|
|
}
|
|
}
|
|
if (display == 0) {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_NO_INFO, file);
|
|
}
|
|
return MOD_CONT;
|
|
}
|
|
|
|
static int showModuleCmdLoaded(CommandHash * cmdList, char *mod_name,
|
|
User * u)
|
|
{
|
|
Command *c;
|
|
CommandHash *current;
|
|
int display = 0;
|
|
|
|
for (current = cmdList; current; current = current->next) {
|
|
for (c = current->c; c; c = c->next) {
|
|
if ((c->mod_name) && (stricmp(c->mod_name, mod_name) == 0)) {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_CMD_LIST,
|
|
c->service, c->name);
|
|
display++;
|
|
}
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
|
|
static int showModuleMsgLoaded(MessageHash * msgList, char *mod_name,
|
|
User * u)
|
|
{
|
|
Message *msg;
|
|
MessageHash *mcurrent;
|
|
int display = 0;
|
|
for (mcurrent = msgList; mcurrent; mcurrent = mcurrent->next) {
|
|
for (msg = mcurrent->m; msg; msg = msg->next) {
|
|
if ((msg->mod_name) && (stricmp(msg->mod_name, mod_name) == 0)) {
|
|
notice_lang(s_OperServ, u, OPER_MODULE_MSG_LIST,
|
|
msg->name);
|
|
display++;
|
|
}
|
|
}
|
|
}
|
|
return display;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*************************************************************************/
|