mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-01 11:06:38 +02:00
607 lines
18 KiB
C
607 lines
18 KiB
C
/*
|
|
* Stores active TKLines (G:Lines etc) inside a .db file for persistency
|
|
* (C) Copyright 2019 Gottem and the UnrealIRCd team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
#define TKL_DB_VERSION 1100
|
|
#define TKL_DB_SAVE_EVERY 893
|
|
|
|
// Some macros
|
|
#ifndef _WIN32
|
|
#define OpenFile(fd, file, flags) fd = open(file, flags, S_IRUSR | S_IWUSR)
|
|
#else
|
|
#define OpenFile(fd, file, flags) fd = open(file, flags, S_IREAD | S_IWRITE)
|
|
#endif
|
|
|
|
#define FreeTKLRead() \
|
|
do { \
|
|
/* Some of these might be NULL */ \
|
|
if(tkltype) MyFree(tkltype); \
|
|
if(usermask) MyFree(usermask); \
|
|
if(hostmask) MyFree(hostmask); \
|
|
if(reason) MyFree(reason); \
|
|
if(setby) MyFree(setby); \
|
|
if(spamf_check) MyFree(spamf_check); \
|
|
if(spamf_matchtype) MyFree(spamf_matchtype); \
|
|
} while(0)
|
|
|
|
#define R_SAFE(x) \
|
|
do { \
|
|
if((x)) { \
|
|
close(fd); \
|
|
config_warn("[tkldb] Read error from the persistent storage file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \
|
|
FreeTKLRead(); \
|
|
return -1; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define W_SAFE(x) \
|
|
do { \
|
|
if((x)) { \
|
|
close(fd); \
|
|
config_warn("[tkldb] Write error from the persistent storage tempfile '%s': %s (DATABASE NOT SAVED)", tmpfname, strerror(errno)); \
|
|
return -1; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define IsMDErr(x, y, z) \
|
|
do { \
|
|
if(!(x)) { \
|
|
config_error("A critical error occurred when registering ModData for %s: %s", MOD_HEADER(y).name, ModuleGetErrorStr((z)->handle)); \
|
|
return MOD_FAILED; \
|
|
} \
|
|
} while(0)
|
|
|
|
// Forward declarations
|
|
void tkldb_moddata_free(ModData *md);
|
|
void setcfg(void);
|
|
void freecfg(void);
|
|
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
|
|
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type);
|
|
EVENT(write_tkldb_evt);
|
|
int write_tkldb(void);
|
|
int write_tkline(int fd, const char *tmpfname, aTKline *tkl);
|
|
int read_tkldb(void);
|
|
static inline int read_data(int fd, void *buf, size_t len);
|
|
static inline int write_data(int fd, void *buf, size_t len);
|
|
static int write_str(int fd, char *x);
|
|
static int read_str(int fd, char **x);
|
|
|
|
// Globals
|
|
static ModDataInfo *tkldb_md;
|
|
static unsigned tkl_db_version = TKL_DB_VERSION;
|
|
struct cfgstruct {
|
|
char *database;
|
|
};
|
|
static struct cfgstruct cfg;
|
|
|
|
// Stored .db might still work with flags instead of actual values (will be corrected on next write)
|
|
// This backport stuff will eventually be removed ;]
|
|
unsigned backport_tkl1000;
|
|
|
|
ModuleHeader MOD_HEADER(tkldb) = {
|
|
"tkldb",
|
|
"v1.10",
|
|
"Stores active TKL entries persistently/across IRCd restarts",
|
|
"3.2-b8-1",
|
|
NULL
|
|
};
|
|
|
|
MOD_TEST(tkldb) {
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkldb_configtest);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_INIT(tkldb) {
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
// MOD_INIT is also called on rehash, so we gotta make sure to not re-add TKLines every time
|
|
// There might be a cleaner way to do this though (i.e. without moddata) :D
|
|
setcfg();
|
|
if(!(tkldb_md = findmoddata_byname("tkldb_inited", MODDATATYPE_CLIENT))) {
|
|
ModDataInfo mreq;
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.type = MODDATATYPE_CLIENT;
|
|
mreq.name = "tkldb_inited";
|
|
mreq.free = tkldb_moddata_free;
|
|
mreq.serialize = NULL;
|
|
mreq.unserialize = NULL;
|
|
mreq.sync = 0;
|
|
tkldb_md = ModDataAdd(modinfo->handle, mreq);
|
|
IsMDErr(tkldb_md, tkldb, modinfo);
|
|
if(read_tkldb() != 0)
|
|
return MOD_FAILED;
|
|
moddata_client((&me), tkldb_md).i = 1;
|
|
}
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkldb_configrun);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD(tkldb) {
|
|
EventAddEx(modinfo->handle, "tkldb_write_tkldb", TKL_DB_SAVE_EVERY, 0, write_tkldb_evt, NULL);
|
|
if(ModuleGetError(modinfo->handle) != MODERR_NOERROR) {
|
|
config_error("A critical error occurred when loading module %s: %s", MOD_HEADER(tkldb).name, ModuleGetErrorStr(modinfo->handle));
|
|
return MOD_FAILED;
|
|
}
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD(tkldb) {
|
|
write_tkldb();
|
|
freecfg();
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
void tkldb_moddata_free(ModData *md) {
|
|
if(md->i)
|
|
md->i = 0;
|
|
}
|
|
|
|
void setcfg(void) {
|
|
// Default: data/tkl.db
|
|
cfg.database = strdup("tkl.db");
|
|
convert_to_absolute_path(&cfg.database, PERMDATADIR);
|
|
}
|
|
|
|
void freecfg(void) {
|
|
MyFree(cfg.database);
|
|
}
|
|
|
|
int tkldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) {
|
|
int errors = 0;
|
|
ConfigEntry *cep;
|
|
|
|
// We are only interested in set::tkldb::database
|
|
if(type != CONFIG_SET)
|
|
return 0;
|
|
|
|
if(!ce || strcmp(ce->ce_varname, "tkldb"))
|
|
return 0;
|
|
|
|
for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
|
|
if(!cep->ce_vardata) {
|
|
config_error("%s:%i: blank set::tkldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
|
errors++;
|
|
continue;
|
|
}
|
|
if(!strcmp(cep->ce_varname, "database")) {
|
|
convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR);
|
|
continue;
|
|
}
|
|
config_error("%s:%i: unknown directive set::tkldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
|
|
errors++;
|
|
}
|
|
|
|
*errs = errors;
|
|
return errors ? -1 : 1;
|
|
}
|
|
|
|
int tkldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type) {
|
|
ConfigEntry *cep;
|
|
|
|
// We are only interested in set::tkldb::database
|
|
if(type != CONFIG_SET)
|
|
return 0;
|
|
|
|
if(!ce || strcmp(ce->ce_varname, "tkldb"))
|
|
return 0;
|
|
|
|
for(cep = ce->ce_entries; cep; cep = cep->ce_next) {
|
|
if(!strcmp(cep->ce_varname, "database"))
|
|
safestrdup(cfg.database, cep->ce_vardata);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
EVENT(write_tkldb_evt) {
|
|
write_tkldb();
|
|
}
|
|
|
|
int write_tkldb(void) {
|
|
char tmpfname[512];
|
|
int fd;
|
|
uint64_t tklcount;
|
|
int index, index2;
|
|
int err;
|
|
aTKline *tkl;
|
|
|
|
// Write to a tempfile first, then rename it if everything succeeded
|
|
snprintf(tmpfname, sizeof(tmpfname), "%s.tmp", cfg.database);
|
|
OpenFile(fd, tmpfname, O_CREAT | O_WRONLY | O_TRUNC);
|
|
if(fd == -1) {
|
|
config_warn("[tkldb] Unable to open the persistent storage tempfile '%s' for writing: %s", tmpfname, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
W_SAFE(write_data(fd, &tkl_db_version, sizeof(tkl_db_version)));
|
|
tklcount = 0;
|
|
|
|
// *@IP Z:Lines have a special hash thingy
|
|
for(index = 0; index < TKLIPHASHLEN1; index++) {
|
|
for(index2 = 0; index2 < TKLIPHASHLEN2; index2++) {
|
|
for(tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next)
|
|
tklcount++;
|
|
}
|
|
}
|
|
for(index = 0; index < TKLISTLEN; index++) {
|
|
for(tkl = tklines[index]; tkl; tkl = tkl->next) {
|
|
// Local spamfilter means it was added through the conf or is built-in, so let's not even write those
|
|
if((tkl->type & TKL_SPAMF) && !(tkl->type & TKL_GLOBAL))
|
|
continue;
|
|
tklcount++;
|
|
}
|
|
}
|
|
W_SAFE(write_data(fd, &tklcount, sizeof(tklcount)));
|
|
|
|
err = 0;
|
|
for(index = 0; !err && index < TKLIPHASHLEN1; index++) {
|
|
for(index2 = 0; !err && index2 < TKLIPHASHLEN2; index2++) {
|
|
for(tkl = tklines_ip_hash[index][index2]; !err && tkl; tkl = tkl->next)
|
|
err = write_tkline(fd, tmpfname, tkl);
|
|
}
|
|
}
|
|
for(index = 0; !err && index < TKLISTLEN; index++) {
|
|
for(tkl = tklines[index]; !err && tkl; tkl = tkl->next) {
|
|
// Local spamfilter means it was added through the conf or is built-in, so let's not even write those
|
|
if((tkl->type & TKL_SPAMF) && !(tkl->type & TKL_GLOBAL)) // Also need to skip this here
|
|
continue;
|
|
err = write_tkline(fd, tmpfname, tkl);
|
|
}
|
|
}
|
|
if(err != 0)
|
|
return -1;
|
|
|
|
// Everything seems to have gone well, attempt to rename the tempfile
|
|
close(fd);
|
|
if(rename(tmpfname, cfg.database) < 0) {
|
|
config_warn("[tkldb] Error renaming '%s' to '%s': %s (DATABASE NOT SAVED)", tmpfname, cfg.database, strerror(errno));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_tkline(int fd, const char *tmpfname, aTKline *tkl) {
|
|
// Since we can't just write 'tkl' in its entirety, we have to get the relevant variables instead
|
|
// These will be used to reconstruct the proper internal m_tkl() call ;]
|
|
char tkltype[2];
|
|
char usermask_subtype[256]; // Might need to prefix something to usermask
|
|
uint64_t expire_at, set_at, spamf_tkl_duration; // To prevent 32 vs 64 bit incompatibilies regarding the TS data type(def)
|
|
|
|
tkltype[0] = tkl_typetochar(tkl->type);
|
|
tkltype[1] = '\0';
|
|
W_SAFE(write_str(fd, tkltype)); // TKL char
|
|
W_SAFE(write_data(fd, &tkl->subtype, sizeof(tkl->subtype))); // Unsigned short (only used for spamfilters/softbans but set to 0 for everything else regardless)
|
|
|
|
// Might be a softban
|
|
if(!tkl->ptr.spamf && (tkl->subtype & TKL_SUBTYPE_SOFT)) {
|
|
snprintf(usermask_subtype, sizeof(usermask_subtype), "%%%s", tkl->usermask);
|
|
W_SAFE(write_str(fd, usermask_subtype)); // User mask (targets for spamfilter, like 'cpnNPqdatu'), includes % for softbans
|
|
}
|
|
else
|
|
W_SAFE(write_str(fd, tkl->usermask));
|
|
|
|
W_SAFE(write_str(fd, tkl->hostmask)); // Host mask (action for spamfilter, like 'block')
|
|
W_SAFE(write_str(fd, tkl->reason)); // Ban reason (TKL time for spamfilters, in case of a gline action)
|
|
W_SAFE(write_str(fd, tkl->setby));
|
|
|
|
expire_at = tkl->expire_at;
|
|
set_at = tkl->set_at;
|
|
W_SAFE(write_data(fd, &expire_at, sizeof(expire_at)));
|
|
W_SAFE(write_data(fd, &set_at, sizeof(set_at)));
|
|
|
|
if(tkl->ptr.spamf) {
|
|
W_SAFE(write_str(fd, "SPAMF")); // Write a string so we know to expect more when reading the DB
|
|
W_SAFE(write_data(fd, &tkl->ptr.spamf->action, sizeof(tkl->ptr.spamf->action))); // Unsigned short (block, GZ:Line, etc; also refer to BAN_ACT_*)
|
|
W_SAFE(write_str(fd, tkl->ptr.spamf->tkl_reason));
|
|
|
|
spamf_tkl_duration = tkl->ptr.spamf->tkl_duration;
|
|
W_SAFE(write_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration)));
|
|
W_SAFE(write_str(fd, tkl->ptr.spamf->expr->str)); // Actual expression/regex/etc
|
|
|
|
// Expression type (simple/PCRE), see also enum MatchType
|
|
if(tkl->ptr.spamf->expr->type == MATCH_PCRE_REGEX)
|
|
W_SAFE(write_str(fd, "regex"));
|
|
else
|
|
W_SAFE(write_str(fd, "simple"));
|
|
}
|
|
else
|
|
W_SAFE(write_str(fd, "NOSPAMF"));
|
|
return 0;
|
|
}
|
|
|
|
int read_tkldb(void) {
|
|
int fd;
|
|
uint64_t i;
|
|
uint64_t tklcount = 0;
|
|
size_t tklcount_tkl1000 = 0;
|
|
uint64_t num = 0;
|
|
uint64_t rewrite = 0;
|
|
unsigned version;
|
|
|
|
// Variables for all TKL types
|
|
char *tkltype = NULL;
|
|
char *usermask = NULL;
|
|
char *hostmask = NULL;
|
|
char *reason = NULL;
|
|
char *setby = NULL;
|
|
|
|
// Some stuff related to spamfilters
|
|
char *spamf_check = NULL;
|
|
char *spamf_matchtype = NULL;
|
|
|
|
ircd_log(LOG_ERROR, "[tkldb] Reading stored X:Lines from '%s'", cfg.database);
|
|
sendto_realops("[tkldb] Reading stored X:Lines from '%s'", cfg.database); // Probably won't be seen ever, but just in case ;]
|
|
OpenFile(fd, cfg.database, O_RDONLY);
|
|
if(fd == -1) {
|
|
if(errno != ENOENT)
|
|
config_warn("[tkldb] Unable to open the persistent storage file '%s' for reading: %s", cfg.database, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
R_SAFE(read_data(fd, &version, sizeof(version)));
|
|
if(version > tkl_db_version) { // Older DBs should still work with newer versions of this module
|
|
config_warn("[tkldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, tkl_db_version, version);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
backport_tkl1000 = (version <= 1000 ? 1 : 0);
|
|
if(backport_tkl1000) {
|
|
R_SAFE(read_data(fd, &tklcount_tkl1000, sizeof(tklcount_tkl1000)));
|
|
tklcount = tklcount_tkl1000;
|
|
}
|
|
else
|
|
R_SAFE(read_data(fd, &tklcount, sizeof(tklcount)));
|
|
|
|
for(i = 1; i <= tklcount; i++) {
|
|
int type;
|
|
unsigned short subtype;
|
|
int parc = 0;
|
|
|
|
// Variables for all TKL types
|
|
usermask = NULL;
|
|
hostmask = NULL;
|
|
reason = NULL;
|
|
setby = NULL;
|
|
char tklflag;
|
|
tkltype = NULL;
|
|
uint64_t expire_at, set_at;
|
|
TS expire_at_tkl1000, set_at_tkl1000;
|
|
char setTime[100], expTime[100], spamfTime[100];
|
|
|
|
// Some stuff related to spamfilters
|
|
spamf_check = NULL;
|
|
int spamf = 0;
|
|
unsigned short spamf_action;
|
|
char *spamf_tkl_reason = NULL;
|
|
TS spamf_tkl_duration_tkl1000;
|
|
uint64_t spamf_tkl_duration;
|
|
char *spamf_expr = NULL;
|
|
MatchType matchtype;
|
|
spamf_matchtype = NULL;
|
|
|
|
int doadd = 1;
|
|
aTKline *tkl;
|
|
|
|
char *tkllayer[13] = { // Args for m_tkl()
|
|
me.name, // 0: Server name
|
|
"+", // 1: Direction
|
|
NULL, // 2: Type, like G
|
|
NULL, // 3: User mask (targets for spamfilter)
|
|
NULL, // 4: Host mask (action for spamfilter)
|
|
NULL, // 5: Set by who
|
|
NULL, // 6: Expiration time
|
|
NULL, // 7: Set-at time
|
|
NULL, // 8: Reason (TKL time for spamfilters in case of a gline action etc)
|
|
NULL, // 9: Spamfilter only: TKL reason (w/ underscores and all)
|
|
NULL, // 10: Spamfilter only: Match type (simple/regex)
|
|
NULL, // 11: Spamfilter only: Match string/regex
|
|
NULL, // 12: Some functions rely on the post-last entry being NULL =]
|
|
};
|
|
|
|
if(backport_tkl1000) {
|
|
R_SAFE(read_data(fd, &type, sizeof(type)));
|
|
tkltype = malloc(sizeof(char) * 2);
|
|
tklflag = tkl_typetochar(type);
|
|
tkltype[0] = tklflag;
|
|
tkltype[1] = '\0';
|
|
}
|
|
else {
|
|
R_SAFE(read_str(fd, &tkltype)); // No need for tkl_typetochar() on read anymore
|
|
tklflag = tkltype[0];
|
|
}
|
|
|
|
R_SAFE(read_data(fd, &subtype, sizeof(subtype)));
|
|
R_SAFE(read_str(fd, &usermask));
|
|
R_SAFE(read_str(fd, &hostmask));
|
|
R_SAFE(read_str(fd, &reason));
|
|
R_SAFE(read_str(fd, &setby));
|
|
|
|
if(backport_tkl1000) {
|
|
R_SAFE(read_data(fd, &expire_at_tkl1000, sizeof(expire_at_tkl1000)));
|
|
R_SAFE(read_data(fd, &set_at_tkl1000, sizeof(set_at_tkl1000)));
|
|
expire_at = expire_at_tkl1000;
|
|
set_at = set_at_tkl1000;
|
|
}
|
|
else {
|
|
R_SAFE(read_data(fd, &expire_at, sizeof(expire_at)));
|
|
R_SAFE(read_data(fd, &set_at, sizeof(set_at)));
|
|
}
|
|
|
|
R_SAFE(read_str(fd, &spamf_check));
|
|
if(!strcmp(spamf_check, "SPAMF")) {
|
|
spamf = 1;
|
|
R_SAFE(read_data(fd, &spamf_action, sizeof(spamf_action)));
|
|
R_SAFE(read_str(fd, &spamf_tkl_reason));
|
|
|
|
if(backport_tkl1000) {
|
|
R_SAFE(read_data(fd, &spamf_tkl_duration_tkl1000, sizeof(spamf_tkl_duration_tkl1000)));
|
|
spamf_tkl_duration = spamf_tkl_duration_tkl1000;
|
|
}
|
|
else
|
|
R_SAFE(read_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration)));
|
|
|
|
R_SAFE(read_str(fd, &spamf_expr));
|
|
if(backport_tkl1000) {
|
|
R_SAFE(read_data(fd, &matchtype, sizeof(matchtype)));
|
|
if(matchtype == MATCH_PCRE_REGEX)
|
|
spamf_matchtype = strdup("regex");
|
|
else
|
|
spamf_matchtype = strdup("simple");
|
|
}
|
|
else {
|
|
// We have better compatibility by just using the strings, since its MATCH_* counterpart might just change in value someday
|
|
R_SAFE(read_str(fd, &spamf_matchtype));
|
|
if(!strcmp(spamf_matchtype, "regex"))
|
|
matchtype = MATCH_PCRE_REGEX;
|
|
else
|
|
matchtype = MATCH_SIMPLE;
|
|
}
|
|
}
|
|
|
|
// Should probably not re-add if it should be expired to begin with
|
|
if(expire_at != 0 && expire_at <= TStime()) {
|
|
ircd_log(LOG_ERROR, "[tkldb] Not re-adding %c:Line '%s@%s' [%s] because it should be expired", tklflag, usermask, hostmask, reason);
|
|
sendto_realops("[tkldb] Not re-adding %c:Line '%s@%s' [%s] because it should be expired", tklflag, usermask, hostmask, reason); // Probably won't be seen ever, but just in case ;]
|
|
rewrite++;
|
|
FreeTKLRead();
|
|
continue;
|
|
}
|
|
|
|
ircsnprintf(setTime, sizeof(setTime), "%li", set_at);
|
|
ircsnprintf(expTime, sizeof(expTime), "%li", expire_at);
|
|
|
|
if(spamf && tklflag == 'f') // Just in case someone modifies the DB to contain local spamfilters, as well as for v1000
|
|
doadd = 0;
|
|
|
|
// Build TKL args
|
|
// All of these except [8] are the same for all (only odd one is spamfilter)
|
|
parc = 9;
|
|
tkllayer[2] = tkltype;
|
|
tkllayer[3] = usermask;
|
|
tkllayer[4] = hostmask;
|
|
tkllayer[5] = setby;
|
|
tkllayer[6] = expTime;
|
|
tkllayer[7] = setTime;
|
|
tkllayer[8] = reason;
|
|
|
|
if(spamf) {
|
|
parc = 12;
|
|
// Make sure this particular TKLine isn't already active somehow
|
|
for(tkl = tklines[tkl_hash(tklflag)]; doadd && tkl; tkl = tkl->next) {
|
|
// We can assume it's the same spamfilter if all of the following match: spamfilter expression, targets, TKL reason, action, matchtype and TKL duration
|
|
if(!strcmp(tkl->ptr.spamf->expr->str, spamf_expr) && !strcmp(tkl->usermask, usermask) && !strcmp(tkl->ptr.spamf->tkl_reason, spamf_tkl_reason) &&
|
|
tkl->ptr.spamf->action == spamf_action && tkl->ptr.spamf->expr->type == matchtype && tkl->ptr.spamf->tkl_duration == spamf_tkl_duration) {
|
|
doadd = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(doadd) {
|
|
ircsnprintf(spamfTime, sizeof(spamfTime), "%li", spamf_tkl_duration);
|
|
tkllayer[8] = spamfTime;
|
|
tkllayer[9] = spamf_tkl_reason;
|
|
tkllayer[10] = spamf_matchtype;
|
|
tkllayer[11] = spamf_expr;
|
|
}
|
|
}
|
|
else {
|
|
for(tkl = tklines[tkl_hash(tklflag)]; tkl; tkl = tkl->next) {
|
|
if(!strcmp(tkl->usermask, usermask) && !strcmp(tkl->hostmask, hostmask) && !strcmp(tkl->reason, reason) && tkl->expire_at == expire_at) {
|
|
doadd = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(doadd) {
|
|
m_tkl(&me, &me, NULL, parc, tkllayer);
|
|
num++;
|
|
}
|
|
FreeTKLRead();
|
|
}
|
|
close(fd);
|
|
|
|
if(num) {
|
|
ircd_log(LOG_ERROR, "[tkldb] Re-added %li X:Lines", num);
|
|
sendto_realops("[tkldb] Re-added %li X:Lines", num); // Probably won't be seen ever, but just in case ;]
|
|
}
|
|
|
|
if(rewrite) {
|
|
ircd_log(LOG_ERROR, "[tkldb] Rewriting DB file due to %li skipped/expired X:Line%s", rewrite, (rewrite > 1 ? "s" : ""));
|
|
sendto_realops("[tkldb] Rewriting DB file due to %li skipped/expired X:Line%s", rewrite, (rewrite > 1 ? "s" : "")); // Probably won't be seen ever, but just in case ;]
|
|
return write_tkldb();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int read_data(int fd, void *buf, size_t len) {
|
|
if((size_t)read(fd, buf, len) < len)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static inline int write_data(int fd, void *buf, size_t len) {
|
|
if((size_t)write(fd, buf, len) < len)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int write_str(int fd, char *x) {
|
|
uint64_t len = (x ? strlen(x) : 0);
|
|
if(write_data(fd, &len, sizeof(len)))
|
|
return -1;
|
|
if(len) {
|
|
if(write_data(fd, x, sizeof(char) * len))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int read_str(int fd, char **x) {
|
|
uint64_t len;
|
|
size_t len_tkl1000; // len used to be of type size_t, but this has portability problems
|
|
size_t size;
|
|
if(backport_tkl1000) {
|
|
if(read_data(fd, &len_tkl1000, sizeof(len_tkl1000)))
|
|
return -1;
|
|
len = len_tkl1000;
|
|
}
|
|
else {
|
|
if(read_data(fd, &len, sizeof(len)))
|
|
return -1;
|
|
}
|
|
if(!len) {
|
|
*x = NULL;
|
|
return 0;
|
|
}
|
|
|
|
size = sizeof(char) * len;
|
|
*x = (char *)MyMalloc(size + sizeof(char));
|
|
if(read_data(fd, *x, size)) {
|
|
MyFree(*x);
|
|
*x = NULL;
|
|
return -1;
|
|
}
|
|
(*x)[len] = 0;
|
|
return 0;
|
|
}
|