/* 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 420 2004-10-23 23:42:15Z trystan $ * */ #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(""); 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, "%s", 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, (long int) 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", (long int) 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) && match_wild(server, u2->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, "%s", 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, "%s", 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, "%s", 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, "%s", DefConOffMessage); } else { oper_global(NULL, langglobal, DefConLevel); } } if (GlobalOnDefconMore) { if ((DefConOffMessage) && DefConLevel == 5) { } else { oper_global(NULL, "%s", 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, "%s", DefConOffMessage); } else { oper_global(NULL, getstring(NULL, DEFCON_GLOBAL), DefConLevel); } } if (GlobalOnDefconMore && !DefConOffMessage) { oper_global(NULL, "%s", 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 /*************************************************************************/