diff --git a/src/modules/channeldb.c b/src/modules/channeldb.c index 442ce00b4..265448812 100644 --- a/src/modules/channeldb.c +++ b/src/modules/channeldb.c @@ -35,14 +35,14 @@ ModuleHeader MOD_HEADER = { do { \ sendto_realops_and_log("[channeldb] Error writing to temporary database file " \ "'%s': %s (DATABASE NOT SAVED)", \ - fname, strerror(errno)); \ + fname, unrealdb_get_error_string()); \ } while(0) #define W_SAFE(x) \ do { \ if (!(x)) { \ WARN_WRITE_ERROR(tmpfname); \ - fclose(fd); \ + unrealdb_close(db); \ return 0; \ } \ } while(0) @@ -55,31 +55,39 @@ ModuleHeader MOD_HEADER = { } \ } while(0) +/* Structs */ +struct cfgstruct { + char *database; + char *db_secret; +}; + /* Forward declarations */ void channeldb_moddata_free(ModData *md); -void setcfg(void); -void freecfg(void); -int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); -int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type); +void setcfg(struct cfgstruct *cfg); +void freecfg(struct cfgstruct *cfg); +int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); +int channeldb_config_posttest(int *errs); +int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type); EVENT(write_channeldb_evt); int write_channeldb(void); -int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel); +int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel); int read_channeldb(void); static void set_channel_mode(Channel *channel, char *modes, char *parameters); /* Global variables */ static uint32_t channeldb_version = CHANNELDB_VERSION; -struct cfgstruct { - char *database; -}; static struct cfgstruct cfg; +static struct cfgstruct test; static long channeldb_next_event = 0; MOD_TEST() { memset(&cfg, 0, sizeof(cfg)); - HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_configtest); + memset(&test, 0, sizeof(test)); + setcfg(&test); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, channeldb_config_test); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, channeldb_config_posttest); return MOD_SUCCESS; } @@ -89,9 +97,9 @@ MOD_INIT() LoadPersistentLong(modinfo, channeldb_next_event); - setcfg(); + setcfg(&cfg); - HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_configrun); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, channeldb_config_run); return MOD_SUCCESS; } @@ -122,7 +130,8 @@ MOD_LOAD() MOD_UNLOAD() { - freecfg(); + freecfg(&test); + freecfg(&cfg); SavePersistentLong(modinfo, channeldb_next_event); return MOD_SUCCESS; } @@ -133,19 +142,20 @@ void channeldb_moddata_free(ModData *md) md->i = 0; } -void setcfg(void) +void setcfg(struct cfgstruct *cfg) { // Default: data/channel.db - safe_strdup(cfg.database, "channel.db"); - convert_to_absolute_path(&cfg.database, PERMDATADIR); + safe_strdup(cfg->database, "channel.db"); + convert_to_absolute_path(&cfg->database, PERMDATADIR); } -void freecfg(void) +void freecfg(struct cfgstruct *cfg) { - safe_free(cfg.database); + safe_free(cfg->database); + safe_free(cfg->db_secret); } -int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) +int channeldb_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { int errors = 0; ConfigEntry *cep; @@ -159,16 +169,45 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) for (cep = ce->ce_entries; cep; cep = cep->ce_next) { - if (!cep->ce_vardata) { + if (!cep->ce_vardata) + { config_error("%s:%i: blank set::channeldb::%s without value", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); errors++; - continue; - } - if (!strcmp(cep->ce_varname, "database")) { + } else + if (!strcmp(cep->ce_varname, "database")) + { convert_to_absolute_path(&cep->ce_vardata, PERMDATADIR); - continue; + safe_strdup(test.database, cep->ce_vardata); + } else + if (!strcmp(cep->ce_varname, "db-secret")) + { + char *err; + if ((err = unrealdb_test_secret(cep->ce_vardata))) + { + config_error("%s:%i: set::channeldb::db-secret: %s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, err); + errors++; + continue; + } + safe_strdup(test.db_secret, cep->ce_vardata); + } else + { + config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + errors++; } - config_error("%s:%i: unknown directive set::channeldb::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); + } + + *errs = errors; + return errors ? -1 : 1; +} + +int channeldb_config_posttest(int *errs) +{ + int errors = 0; + char *errstr; + + if (test.database && ((errstr = unrealdb_test_db(test.database, test.db_secret)))) + { + config_error("[channeldb] %s", errstr); errors++; } @@ -176,7 +215,7 @@ int channeldb_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) return errors ? -1 : 1; } -int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type) +int channeldb_config_run(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep; @@ -191,6 +230,8 @@ int channeldb_configrun(ConfigFile *cf, ConfigEntry *ce, int type) { if (!strcmp(cep->ce_varname, "database")) safe_strdup(cfg.database, cep->ce_vardata); + else if (!strcmp(cep->ce_varname, "db-secret")) + safe_strdup(cfg.db_secret, cep->ce_vardata); } return 1; } @@ -206,7 +247,7 @@ EVENT(write_channeldb_evt) int write_channeldb(void) { char tmpfname[512]; - FILE *fd; + UnrealDB *db; Channel *channel; int cnt = 0; #ifdef BENCHMARK @@ -217,33 +258,33 @@ int write_channeldb(void) // Write to a tempfile first, then rename it if everything succeeded snprintf(tmpfname, sizeof(tmpfname), "%s.%x.tmp", cfg.database, getrandom32()); - fd = fopen(tmpfname, "wb"); - if (!fd) + db = unrealdb_open(tmpfname, UNREALDB_MODE_WRITE, cfg.db_secret); + if (!db) { WARN_WRITE_ERROR(tmpfname); return 0; } - W_SAFE(write_data(fd, &channeldb_version, sizeof(channeldb_version))); + W_SAFE(unrealdb_write_data(db, &channeldb_version, sizeof(channeldb_version))); /* First, count +P channels and write the count to the database */ for (channel = channels; channel; channel=channel->nextch) if (has_channel_mode(channel, 'P')) cnt++; - W_SAFE(write_int64(fd, cnt)); + W_SAFE(unrealdb_write_int64(db, cnt)); for (channel = channels; channel; channel=channel->nextch) { /* We only care about +P (persistent) channels */ if (has_channel_mode(channel, 'P')) { - if (!write_channel_entry(fd, tmpfname, channel)) + if (!write_channel_entry(db, tmpfname, channel)) return 0; } } // Everything seems to have gone well, attempt to close and rename the tempfile - if (fclose(fd) != 0) + if (!unrealdb_close(db)) { WARN_WRITE_ERROR(tmpfname); return 0; @@ -266,7 +307,7 @@ int write_channeldb(void) return 1; } -int write_listmode(FILE *fd, const char *tmpfname, Ban *lst) +int write_listmode(UnrealDB *db, const char *tmpfname, Ban *lst) { Ban *l; int cnt = 0; @@ -274,50 +315,50 @@ int write_listmode(FILE *fd, const char *tmpfname, Ban *lst) /* First count and write the list count */ for (l = lst; l; l = l->next) cnt++; - W_SAFE(write_int32(fd, cnt)); + W_SAFE(unrealdb_write_int32(db, cnt)); for (l = lst; l; l = l->next) { /* The entry, setby, seton */ - W_SAFE(write_str(fd, l->banstr)); - W_SAFE(write_str(fd, l->who)); - W_SAFE(write_int64(fd, l->when)); + W_SAFE(unrealdb_write_str(db, l->banstr)); + W_SAFE(unrealdb_write_str(db, l->who)); + W_SAFE(unrealdb_write_int64(db, l->when)); } return 1; } -int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel) +int write_channel_entry(UnrealDB *db, const char *tmpfname, Channel *channel) { - W_SAFE(write_int32(fd, MAGIC_CHANNEL_START)); + W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_START)); /* Channel name */ - W_SAFE(write_str(fd, channel->chname)); + W_SAFE(unrealdb_write_str(db, channel->chname)); /* Channel creation time */ - W_SAFE(write_int64(fd, channel->creationtime)); + W_SAFE(unrealdb_write_int64(db, channel->creationtime)); /* Topic (topic, setby, seton) */ - W_SAFE(write_str(fd, channel->topic)); - W_SAFE(write_str(fd, channel->topic_nick)); - W_SAFE(write_int64(fd, channel->topic_time)); + W_SAFE(unrealdb_write_str(db, channel->topic)); + W_SAFE(unrealdb_write_str(db, channel->topic_nick)); + W_SAFE(unrealdb_write_int64(db, channel->topic_time)); /* Basic channel modes (eg: +sntkl key 55) */ channel_modes(&me, modebuf, parabuf, sizeof(modebuf), sizeof(parabuf), channel); - W_SAFE(write_str(fd, modebuf)); - W_SAFE(write_str(fd, parabuf)); + W_SAFE(unrealdb_write_str(db, modebuf)); + W_SAFE(unrealdb_write_str(db, parabuf)); /* Mode lock */ - W_SAFE(write_str(fd, channel->mode_lock)); + W_SAFE(unrealdb_write_str(db, channel->mode_lock)); /* List modes (bans, exempts, invex) */ - if (!write_listmode(fd, tmpfname, channel->banlist)) + if (!write_listmode(db, tmpfname, channel->banlist)) return 0; - if (!write_listmode(fd, tmpfname, channel->exlist)) + if (!write_listmode(db, tmpfname, channel->exlist)) return 0; - if (!write_listmode(fd, tmpfname, channel->invexlist)) + if (!write_listmode(db, tmpfname, channel->invexlist)) return 0; - W_SAFE(write_int32(fd, MAGIC_CHANNEL_END)); + W_SAFE(unrealdb_write_int32(db, MAGIC_CHANNEL_END)); return 1; } #define R_SAFE(x) \ do { \ if (!(x)) { \ - config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \ + config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \ if (e) \ { \ safe_free(e->banstr); \ @@ -328,21 +369,21 @@ int write_channel_entry(FILE *fd, const char *tmpfname, Channel *channel) } \ } while(0) -int read_listmode(FILE *fd, Ban **lst) +int read_listmode(UnrealDB *db, Ban **lst) { uint32_t total; uint64_t when; int i; Ban *e = NULL; - R_SAFE(read_data(fd, &total, sizeof(total))); + R_SAFE(unrealdb_read_data(db, &total, sizeof(total))); for (i = 0; i < total; i++) { e = safe_alloc(sizeof(Ban)); - R_SAFE(read_str(fd, &e->banstr)); - R_SAFE(read_str(fd, &e->who)); - R_SAFE(read_data(fd, &when, sizeof(when))); + R_SAFE(unrealdb_read_str(db, &e->banstr)); + R_SAFE(unrealdb_read_str(db, &e->who)); + R_SAFE(unrealdb_read_data(db, &when, sizeof(when))); e->when = when; e->next = *lst; *lst = e; @@ -366,8 +407,8 @@ int read_listmode(FILE *fd, Ban **lst) #define R_SAFE(x) \ do { \ if (!(x)) { \ - config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, strerror(errno)); \ - fclose(fd); \ + config_warn("[channeldb] Read error from database file '%s' (possible corruption): %s", cfg.database, unrealdb_get_error_string()); \ + unrealdb_close(db); \ FreeChannelEntry(); \ return 0; \ } \ @@ -375,7 +416,7 @@ int read_listmode(FILE *fd, Ban **lst) int read_channeldb(void) { - FILE *fd; + UnrealDB *db; uint32_t version; int added = 0; int i; @@ -397,29 +438,41 @@ int read_channeldb(void) gettimeofday(&tv_alpha, NULL); #endif - fd = fopen(cfg.database, "rb"); - if (!fd) + db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, cfg.db_secret); + if (!db) { - if (errno == ENOENT) + if (unrealdb_get_error_code() == UNREALDB_ERROR_FILENOTFOUND) { /* Database does not exist. Could be first boot */ config_warn("[channeldb] No database present at '%s', will start a new one", cfg.database); return 1; - } else { - config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, strerror(errno)); + } else + if (unrealdb_get_error_code() == UNREALDB_ERROR_NOTCRYPTED) + { + /* Re-open as unencrypted */ + db = unrealdb_open(cfg.database, UNREALDB_MODE_READ, NULL); + if (!db) + { + /* This should actually never happen, unless some weird I/O error */ + config_warn("[channeldb] Unable to open the database file '%s': %s", cfg.database, unrealdb_get_error_string()); + return 0; + } + } else + { + config_warn("[channeldb] Unable to open the database file '%s' for reading: %s", cfg.database, unrealdb_get_error_string()); return 0; } } - R_SAFE(read_data(fd, &version, sizeof(version))); + R_SAFE(unrealdb_read_data(db, &version, sizeof(version))); if (version > channeldb_version) { config_warn("[channeldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, channeldb_version, version); - fclose(fd); + unrealdb_close(db); return 0; } - R_SAFE(read_data(fd, &count, sizeof(count))); + R_SAFE(unrealdb_read_data(db, &count, sizeof(count))); for (i=1; i <= count; i++) { @@ -434,20 +487,20 @@ int read_channeldb(void) mode_lock = NULL; Channel *channel; - R_SAFE(read_data(fd, &magic, sizeof(magic))); + R_SAFE(unrealdb_read_data(db, &magic, sizeof(magic))); if (magic != MAGIC_CHANNEL_START) { config_error("[channeldb] Corrupt database (%s) - channel magic start is 0x%x. Further reading aborted.", cfg.database, magic); break; } - R_SAFE(read_str(fd, &chname)); - R_SAFE(read_data(fd, &creationtime, sizeof(creationtime))); - R_SAFE(read_str(fd, &topic)); - R_SAFE(read_str(fd, &topic_nick)); - R_SAFE(read_data(fd, &topic_time, sizeof(topic_time))); - R_SAFE(read_str(fd, &modes1)); - R_SAFE(read_str(fd, &modes2)); - R_SAFE(read_str(fd, &mode_lock)); + R_SAFE(unrealdb_read_str(db, &chname)); + R_SAFE(unrealdb_read_data(db, &creationtime, sizeof(creationtime))); + R_SAFE(unrealdb_read_str(db, &topic)); + R_SAFE(unrealdb_read_str(db, &topic_nick)); + R_SAFE(unrealdb_read_data(db, &topic_time, sizeof(topic_time))); + R_SAFE(unrealdb_read_str(db, &modes1)); + R_SAFE(unrealdb_read_str(db, &modes2)); + R_SAFE(unrealdb_read_str(db, &mode_lock)); /* If we got this far, we can create/initialize the channel with the above */ channel = get_channel(&me, chname, CREATE); channel->creationtime = creationtime; @@ -456,10 +509,10 @@ int read_channeldb(void) channel->topic_time = topic_time; safe_strdup(channel->mode_lock, mode_lock); set_channel_mode(channel, modes1, modes2); - R_SAFE(read_listmode(fd, &channel->banlist)); - R_SAFE(read_listmode(fd, &channel->exlist)); - R_SAFE(read_listmode(fd, &channel->invexlist)); - R_SAFE(read_data(fd, &magic, sizeof(magic))); + R_SAFE(read_listmode(db, &channel->banlist)); + R_SAFE(read_listmode(db, &channel->exlist)); + R_SAFE(read_listmode(db, &channel->invexlist)); + R_SAFE(unrealdb_read_data(db, &magic, sizeof(magic))); FreeChannelEntry(); added++; if (magic != MAGIC_CHANNEL_END) @@ -469,7 +522,7 @@ int read_channeldb(void) } } - fclose(fd); + unrealdb_close(db); if (added) sendto_realops_and_log("[channeldb] Added %d persistent channels (+P)", added);