/* OperServ functions. * * (C) 2003-2009 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. * * Based on the original code of Epona by Lara. * Based on the original code of Services by Andy Church. * * $Id$ * */ #include "services.h" #include "pseudo.h" /*************************************************************************/ /* AKILL, SGLINE, SQLINE and SZLINE lists */ SList akills, sglines, sqlines, szlines; /*************************************************************************/ static int is_akill_entry_equal(SList * slist, void *item1, void *item2); static void free_akill_entry(SList * slist, void *item); static int is_sgline_entry_equal(SList * slist, void *item1, void *item2); static void free_sgline_entry(SList * slist, void *item); static int is_sqline_entry_equal(SList * slist, void *item1, void *item2); static void free_sqline_entry(SList * slist, void *item); static int is_szline_entry_equal(SList * slist, void *item1, void *item2); static void free_szline_entry(SList * slist, void *item); time_t DefContimer; int DefConModesSet = 0; char *defconReverseModes(const char *modes); uint32 DefConModesOn; /* Modes to be enabled during DefCon */ uint32 DefConModesOff; /* Modes to be disabled during DefCon */ ChannelInfo DefConModesCI; /* ChannelInfo containg params for locked modes * during DefCon; I would've done this nicer if i * could, but all damn mode functions require a * ChannelInfo struct! --gdex */ void moduleAddOperServCmds(); /*************************************************************************/ /* Options for the lists */ SListOpts akopts = { 0, NULL, &is_akill_entry_equal, &free_akill_entry }; SListOpts sgopts = { 0, NULL, &is_sgline_entry_equal, &free_sgline_entry }; SListOpts sqopts = { SLISTF_SORT, NULL, &is_sqline_entry_equal, &free_sqline_entry }; SListOpts szopts = { 0, NULL, &is_szline_entry_equal, &free_szline_entry }; /*************************************************************************/ /* *INDENT-OFF* */ void moduleAddOperServCmds() { ModuleManager::LoadModuleList(OperServCoreNumber, OperServCoreModules); } /* *INDENT-ON* */ /*************************************************************************/ /*************************************************************************/ /* OperServ initialization. */ void os_init() { moduleAddOperServCmds(); slist_init(&akills); akills.opts = &akopts; if (ircd->sgline) { slist_init(&sglines); sglines.opts = &sgopts; } if (ircd->sqline) { slist_init(&sqlines); sqlines.opts = &sqopts; } if (ircd->szline) { slist_init(&szlines); szlines.opts = &szopts; } } /*************************************************************************/ /* Main OperServ routine. */ void operserv(User * u, char *buf) { const char *cmd; const 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 = ""; } ircdproto->SendCTCP(findbot(s_OperServ), u->nick, "PING %s", s); } else { mod_run_cmd(s_OperServ, u, OPERSERV, cmd); } } /*************************************************************************/ /**************************** Privilege checks ***************************/ /*************************************************************************/ /* 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() { dbFILE *f; int16 i, ver; uint16 tmp16; uint32 tmp32; int failed = 0; if (!(f = open_db(s_OperServ, OperDBName, "r", OPER_VERSION))) return; ver = get_file_version(f); if (ver != 13) { close_db(f); fatal("Read error on %s", ChanDBName); return; } SAFE(read_int32(&maxusercnt, f)); SAFE(read_int32(&tmp32, f)); maxusertime = tmp32; Akill *ak; read_int16(&tmp16, f); slist_setcapacity(&akills, tmp16); for (i = 0; i < akills.capacity; i++) { ak = new Akill; 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); } SXLine *sx; read_int16(&tmp16, f); slist_setcapacity(&sglines, tmp16); for (i = 0; i < sglines.capacity; i++) { sx = new SXLine; 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); } read_int16(&tmp16, f); slist_setcapacity(&sqlines, tmp16); for (i = 0; i < sqlines.capacity; i++) { sx = new SXLine; 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 = new SXLine; 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); } 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) { \ ircdproto->SendGlobops(NULL, "Write error on %s: %s", OperDBName, \ strerror(errno)); \ lastwarn = time(NULL); \ } \ return; \ } \ } while (0) void save_os_dbase() { int i; dbFILE *f; static time_t lastwarn = 0; Akill *ak; SXLine *sx; 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 = static_cast(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 = static_cast(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 = static_cast(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 = static_cast(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)); } close_db(f); } #undef SAFE /*************************************************************************/ /*********************** OperServ command functions **********************/ /*************************************************************************/ /*************************************************************************/ Server *server_global(Server * s, char *msg) { Server *sl; while (s) { /* Do not send the notice to ourselves our juped servers */ if (!(s->flags & (SERVER_ISME | SERVER_JUPED))) notice_server(s_GlobalNoticer, s, "%s", msg); if (s->links) { sl = server_global(s->links, msg); if (sl) s = sl; else s = s->next; } else { s = s->next; } } return s; } void oper_global(char *nick, const char *fmt, ...) { va_list args; char msg[2048]; /* largest valid message is 512, this should cover any global */ char dmsg[2048]; /* largest valid message is 512, this should cover any global */ va_start(args, fmt); vsnprintf(msg, sizeof(msg), fmt, args); va_end(args); /* I don't like the way this is coded... */ if ((nick) && (!AnonymousGlobal)) { snprintf(dmsg, sizeof(dmsg), "[%s] %s", nick, msg); server_global(servlist, dmsg); } else { server_global(servlist, msg); } } /**************************************************************************/ /************************************************************************/ /*************************************************************************/ /* 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, const char *mask, const char *by, const time_t expires, const char *reason) { int deleted = 0, i; char *user, *mask2, *host; Akill *entry; if (!mask) { return -1; } /* 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 = static_cast(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 (Anope::Match(mask, amask, false) && (entry->expires >= expires || entry->expires == 0)) { if (u) notice_lang(s_OperServ, u, OPER_AKILL_ALREADY_COVERED, mask, amask); return -1; } if (Anope::Match(amask, mask, false) && (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) { delete [] mask2; return -1; } user = mask2; *host = 0; host++; entry = new Akill; if (!entry) { delete [] mask2; 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) ircdproto->SendAkill(entry->user, entry->host, entry->by, entry->seton, entry->expires, entry->reason); delete [] 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 = static_cast(akills.list[i]); if (!ak) continue; if (Anope::Match(username, ak->user, false) && Anope::Match(host, ak->host, false)) { ircdproto->SendAkill(ak->user, ak->host, ak->by, ak->seton, ak->expires, ak->reason); return 1; } if (ircd->vhost) { if (vhost) { if (Anope::Match(username, ak->user, false) && Anope::Match(vhost, ak->host, false)) { ircdproto->SendAkill(ak->user, ak->host, ak->by, ak->seton, ak->expires, ak->reason); return 1; } } } if (ircd->nickip) { if (ip) { if (Anope::Match(username, ak->user, false) && Anope::Match(ip, ak->host, false)) { ircdproto->SendAkill(ak->user, ak->host, ak->by, ak->seton, ak->expires, ak->reason); return 1; } } } } return 0; } /* Delete any expired autokills. */ void expire_akills() { int i; time_t now = time(NULL); Akill *ak; for (i = akills.count - 1; i >= 0; i--) { ak = static_cast(akills.list[i]); if (!ak->expires || ak->expires > now) continue; if (WallAkillExpire) ircdproto->SendGlobops(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 = static_cast(item); /* Remove the AKILLs from all the servers */ ircdproto->SendAkillDel(ak->user, ak->host); /* Free the structure */ delete [] ak->user; delete [] ak->host; delete [] ak->by; delete [] ak->reason; delete ak; } /* item1 is not an Akill pointer, but a char */ static int is_akill_entry_equal(SList * slist, void *item1, void *item2) { char *ak1 = static_cast(item1), buf[BUFSIZE]; Akill *ak2 = static_cast(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; } /*************************************************************************/ /* 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, const char *mask, const char *by, time_t expires, const char *reason) { int deleted = 0, i; SXLine *entry; User *u2, *next; char buf[BUFSIZE]; *buf = '\0'; /* 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 (!mask) { return -1; } if (sglines.count > 0) { for (i = sglines.count - 1; i >= 0; i--) { entry = static_cast(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 (Anope::Match(mask, entry->mask, false ) && (entry->expires >= expires || entry->expires == 0)) { if (u) notice_lang(s_OperServ, u, OPER_SGLINE_ALREADY_COVERED, mask, entry->mask); return -1; } if (Anope::Match(entry->mask, mask, false) && (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 = new SXLine; 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); ircdproto->SendSGLine(entry->mask, entry->reason); if (KillonSGline && !ircd->sglineenforce) { snprintf(buf, (BUFSIZE - 1), "G-Lined: %s", entry->reason); u2 = firstuser(); while (u2) { next = nextuser(); if (!is_oper(u2)) { if (Anope::Match(u2->realname, entry->mask, false)) { kill_user(ServerName, u2->nick, buf); } } u2 = next; } } 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 = static_cast(sglines.list[i]); if (!sx) continue; if (Anope::Match(realname, sx->mask, false)) { ircdproto->SendSGLine(sx->mask, sx->reason); /* We kill nick since s_sgline can't */ ircdproto->SendSVSKill(ServerName, nick, "G-Lined: %s", sx->reason); return 1; } } return 0; } /* Delete any expired SGLINEs. */ void expire_sglines() { int i; time_t now = time(NULL); SXLine *sx; for (i = sglines.count - 1; i >= 0; i--) { sx = static_cast(sglines.list[i]); if (!sx->expires || sx->expires > now) continue; if (WallSGLineExpire) ircdproto->SendGlobops(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 = static_cast(item); /* Remove the SGLINE from all the servers */ ircdproto->SendSGLineDel(sx->mask); /* Free the structure */ delete [] sx->mask; delete [] sx->by; delete [] sx->reason; delete sx; } /* item1 is not an SXLine pointer, but a char */ static int is_sgline_entry_equal(SList * slist, void *item1, void *item2) { char *sx1 = static_cast(item1); SXLine *sx2 = static_cast(item2); if (!sx1 || !sx2) return 0; if (!stricmp(sx1, sx2->mask)) return 1; else return 0; } /*************************************************************************/ /* 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, const char *mask, const char *by, time_t expires, const char *reason) { int deleted = 0, i; User *u2, *next; SXLine *entry; char buf[BUFSIZE]; *buf = '\0'; /* 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 (!mask) { return -1; } if (sqlines.count > 0) { for (i = sqlines.count - 1; i >= 0; i--) { entry = static_cast(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 (Anope::Match(mask, entry->mask, false) && (entry->expires >= expires || entry->expires == 0)) { if (u) notice_lang(s_OperServ, u, OPER_SQLINE_ALREADY_COVERED, mask, entry->mask); return -1; } if (Anope::Match(entry->mask, mask, false) && (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 = new SXLine; 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); sqline(entry->mask, entry->reason); if (KillonSQline) { snprintf(buf, (BUFSIZE - 1), "Q-Lined: %s", entry->reason); u2 = firstuser(); while (u2) { next = nextuser(); if (!is_oper(u2)) { if (Anope::Match(u2->nick, entry->mask, false)) { kill_user(ServerName, u2->nick, buf); } } u2 = next; } } return deleted; } /* Does the user match any SQLINEs? */ int check_sqline(const char *nick, int nick_change) { int i; SXLine *sx; char reason[300]; if (sqlines.count == 0) return 0; for (i = 0; i < sqlines.count; i++) { sx = static_cast(sqlines.list[i]); if (!sx) continue; if (ircd->chansqline) { if (*sx->mask == '#') continue; } if (Anope::Match(nick, sx->mask, false)) { sqline(sx->mask, sx->reason); /* We kill nick since s_sqline can't */ snprintf(reason, sizeof(reason), "Q-Lined: %s", sx->reason); kill_user(s_OperServ, nick, reason); return 1; } } return 0; } 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 = static_cast(sqlines.list[i]); if (!sx) continue; if (*sx->mask != '#') continue; if (Anope::Match(chan, sx->mask, false)) { sqline(sx->mask, sx->reason); return 1; } } return 0; } /* Delete any expired SQLINEs. */ void expire_sqlines() { int i; time_t now = time(NULL); SXLine *sx; for (i = sqlines.count - 1; i >= 0; i--) { sx = static_cast(sqlines.list[i]); if (!sx->expires || sx->expires > now) continue; if (WallSQLineExpire) ircdproto->SendGlobops(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 = static_cast(item); /* Remove the SQLINE from all the servers */ ircdproto->SendSQLineDel(sx->mask); /* Free the structure */ delete [] sx->mask; delete [] sx->by; delete [] sx->reason; delete sx; } /* item1 is not an SXLine pointer, but a char */ static int is_sqline_entry_equal(SList * slist, void *item1, void *item2) { char *sx1 = static_cast(item1); SXLine *sx2 = static_cast(item2); if (!sx1 || !sx2) return 0; if (!stricmp(sx1, sx2->mask)) return 1; else return 0; } /*************************************************************************/ /* 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, const char *mask, const char *by, time_t expires, const char *reason) { int deleted = 0, i; SXLine *entry; if (!mask) { return -1; } /* 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 = static_cast(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 (Anope::Match(mask, entry->mask, false)) { if (u) notice_lang(s_OperServ, u, OPER_SZLINE_ALREADY_COVERED, mask, entry->mask); return -1; } if (Anope::Match(entry->mask, mask, false)) { 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 = new SXLine; 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); ircdproto->SendSZLine(entry->mask, entry->reason, entry->by); return deleted; } /* Check and enforce any Zlines that we have */ int check_szline(const char *nick, char *ip) { int i; SXLine *sx; if (szlines.count == 0) { return 0; } if (!ip) { return 0; } for (i = 0; i < szlines.count; i++) { sx = static_cast(szlines.list[i]); if (!sx) { continue; } if (Anope::Match(ip, sx->mask, false)) { ircdproto->SendSZLine(sx->mask, sx->reason, sx->by); return 1; } } return 0; } /* Delete any expired SZLINEs. */ void expire_szlines() { int i; time_t now = time(NULL); SXLine *sx; for (i = szlines.count - 1; i >= 0; i--) { sx = static_cast(szlines.list[i]); if (!sx->expires || sx->expires > now) continue; if (WallSZLineExpire) ircdproto->SendGlobops(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 = static_cast(item); /* Remove the SZLINE from all the servers */ ircdproto->SendSZLineDel(sx->mask); /* Free the structure */ delete [] sx->mask; delete [] sx->by; delete [] sx->reason; delete sx; } /* item1 is not an SXLine pointer, but a char */ static int is_szline_entry_equal(SList * slist, void *item1, void *item2) { char *sx1 = static_cast(item1); SXLine *sx2 = static_cast(item2); if (!sx1 || !sx2) return 0; if (!stricmp(sx1, sx2->mask)) return 1; else return 0; } /*************************************************************************/ /** * Returns 1 if the passed level is part of the CURRENT defcon, else 0 is returned **/ int checkDefCon(int level) { return DefCon[DefConLevel] & level; } /** * Automaticaly re-set the DefCon level if the time limit has expired. **/ void resetDefCon(int level) { char strLevel[5]; snprintf(strLevel, 4, "%d", level); if (DefConLevel != level) { if ((DefContimer) && (time(NULL) - DefContimer >= DefConTimeOut)) { DefConLevel = level; FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(strLevel)); alog("Defcon level timeout, returning to lvl %d", level); ircdproto->SendGlobops(s_OperServ, getstring(OPER_DEFCON_WALL), s_OperServ, level); if (GlobalOnDefcon) { if (DefConOffMessage) { oper_global(NULL, "%s", DefConOffMessage); } else { oper_global(NULL, getstring(DEFCON_GLOBAL), DefConLevel); } } if (GlobalOnDefconMore && !DefConOffMessage) { oper_global(NULL, "%s", DefconMessage); } runDefCon(); } } } /** * Run DefCon level specific Functions. **/ void runDefCon() { 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); DefConModesSet = 1; do_mass_mode(DefConChanModes); } } } else { if (DefConChanModes && (DefConModesSet != 0)) { if (DefConChanModes[0] == '+' || DefConChanModes[0] == '-') { DefConModesSet = 0; if ((newmodes = defconReverseModes(DefConChanModes))) { alog("DEFCON: setting %s on all chan's", newmodes); do_mass_mode(newmodes); delete [] newmodes; } } } } } /** * Reverse the mode string, used for remove DEFCON chan modes. **/ char *defconReverseModes(const char *modes) { char *newmodes = NULL; unsigned i = 0; if (!modes) { return NULL; } if (!(newmodes = new 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; } /* Parse the defcon mlock mode string and set the correct global vars. * * @param str mode string to parse * @return 1 if accepted, 0 if failed */ int defconParseModeString(const char *str) { int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */ unsigned char mode; CBMode *cbm; char *str_copy = sstrdup(str); /* We need this copy as str is const -GD */ char *param; /* Store parameters during mode parsing */ /* Reinitialize everything */ DefConModesOn = 0; DefConModesOff = 0; DefConModesCI.mlock_limit = 0; DefConModesCI.mlock_key = NULL; DefConModesCI.mlock_flood = NULL; DefConModesCI.mlock_redirect = NULL; /* Initialize strtok() internal buffer */ strtok(str_copy, " "); /* Loop while there are modes to set */ while ((mode = *str++) && (mode != ' ')) { switch (mode) { case '+': add = 1; continue; case '-': add = 0; continue; default: if (add < 0) continue; } if (static_cast(mode) < 128 && (cbm = &cbmodes[static_cast(mode)])->flag != 0) { if (cbm->flags & CBM_NO_MLOCK) { alog("DefConChanModes mode character '%c' cannot be locked", mode); delete [] str_copy; return 0; } else if (add) { DefConModesOn |= cbm->flag; DefConModesOff &= ~cbm->flag; if (cbm->cssetvalue) { if (!(param = strtok(NULL, " "))) { alog("DefConChanModes mode character '%c' has no parameter while one is expected", mode); delete [] str_copy; return 0; } cbm->cssetvalue(&DefConModesCI, param); } } else { DefConModesOff |= cbm->flag; if (DefConModesOn & cbm->flag) { DefConModesOn &= ~cbm->flag; if (cbm->cssetvalue) { cbm->cssetvalue(&DefConModesCI, NULL); } } } } else { alog("DefConChanModes unknown mode character '%c'", mode); delete [] str_copy; return 0; } } /* while (*param) */ delete [] str_copy; if (ircd->Lmode) { /* We can't mlock +L if +l is not mlocked as well. */ if ((DefConModesOn & ircd->chan_lmode) && !(DefConModesOn & anope_get_limit_mode())) { DefConModesOn &= ~ircd->chan_lmode; delete [] DefConModesCI.mlock_redirect; DefConModesCI.mlock_redirect = NULL; alog("DefConChanModes must lock mode +l as well to lock mode +L"); return 0; } } /* Some ircd we can't set NOKNOCK without INVITE */ /* So check if we need there is a NOKNOCK MODE and that we need INVITEONLY */ if (ircd->noknock && ircd->knock_needs_i) { if ((DefConModesOn & ircd->noknock) && !(DefConModesOn & anope_get_invite_mode())) { DefConModesOn &= ~ircd->noknock; alog("DefConChanModes must lock mode +i as well to lock mode +K"); return 0; } } /* Everything is set fine, return 1 */ return 1; } /*************************************************************************/