diff options
Diffstat (limited to 'operserv.c')
-rw-r--r-- | operserv.c | 5099 |
1 files changed, 5099 insertions, 0 deletions
diff --git a/operserv.c b/operserv.c new file mode 100644 index 000000000..c93a94368 --- /dev/null +++ b/operserv.c @@ -0,0 +1,5099 @@ +/* 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: operserv.c,v 1.89 2004/03/14 22:44:47 dane Exp $ + * + */ + +#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 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 + +/*************************************************************************/ |