/** * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * * Copyright (C) 2013-2015 Hercules Dev Team * * Hercules 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 3 of the License, 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, see . */ #define HERCULES_CORE #include "channel.h" #include "map/atcommand.h" #include "map/guild.h" #include "map/instance.h" #include "map/irc-bot.h" #include "map/map.h" #include "map/npc.h" #include "map/pc.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/db.h" #include "common/memmgr.h" #include "common/nullpo.h" #include "common/random.h" #include "common/showmsg.h" #include "common/socket.h" #include "common/strlib.h" #include "common/timer.h" #include "common/utils.h" #include #include #include static struct channel_interface channel_s; struct channel_interface *channel; static struct Channel_Config channel_config; /** * Returns the named channel. * * @param name The channel name * @param sd The issuer character, for character-specific channels (i.e. map, ally) * @return a pointer to the channel, or NULL. */ static struct channel_data *channel_search(const char *name, struct map_session_data *sd) { const char *realname = name; if (!realname || !*realname) return NULL; if (*realname == '#') { realname++; if (!*realname) return NULL; } if (channel->config->local && strcmpi(realname, channel->config->local_name) == 0) { if (!sd) return NULL; if (!map->list[sd->bl.m].channel) { channel->map_join(sd); } return map->list[sd->bl.m].channel; } if (channel->config->ally && strcmpi(realname, channel->config->ally_name) == 0) { if (!sd || !sd->status.guild_id || !sd->guild) return NULL; return sd->guild->channel; } return strdb_get(channel->db, realname); } /** * Creates a chat channel. * * If the channel type isn't HCS_TYPE_MAP or HCS_TYPE_ALLY, the channel is added to the channel->db. * * @param type The channel type. * @param name The channel name. * @param color The channel chat color. * @return A pointer to the created channel. */ static struct channel_data *channel_create(enum channel_types type, const char *name, unsigned char color) { struct channel_data *chan; if (!name) return NULL; CREATE(chan, struct channel_data, 1); chan->users = idb_alloc(DB_OPT_BASE); safestrncpy(chan->name, name, HCS_NAME_LENGTH); chan->color = color; chan->options = HCS_OPT_BASE; chan->banned = NULL; chan->msg_delay = 0; chan->type = type; if (chan->type != HCS_TYPE_MAP && chan->type != HCS_TYPE_ALLY) strdb_put(channel->db, chan->name, chan); return chan; } /** * Deletes a chat channel. * * @param chan The channel to delete */ static void channel_delete(struct channel_data *chan) { nullpo_retv(chan); if (db_size(chan->users) && !channel->config->closing) { struct map_session_data *sd; struct DBIterator *iter = db_iterator(chan->users); for (sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter)) { channel->leave_sub(chan, sd); } dbi_destroy(iter); } if (chan->banned) { db_destroy(chan->banned); chan->banned = NULL; } db_destroy(chan->users); if (chan->m) { map->list[chan->m].channel = NULL; aFree(chan); } else if (chan->type == HCS_TYPE_ALLY) { aFree(chan); } else if (!channel->config->closing) { strdb_remove(channel->db, chan->name); } } /** * Sets a chat channel password. * * @param chan The channel to edit. * @param pass The password to set. Pass NULL to remove existing passwords. */ static void channel_set_password(struct channel_data *chan, const char *password) { nullpo_retv(chan); if (password) safestrncpy(chan->password, password, HCS_NAME_LENGTH); else chan->password[0] = '\0'; } /** * Bans a character from the given channel. * * @param chan The channel. * @param ssd The source character, if any. * @param tsd The target character. * @retval HCS_STATUS_OK if the operation succeeded. * @retval HCS_STATUS_ALREADY if the target character is already banned. * @retval HCS_STATUS_NOPERM if the source character doesn't have enough permissions. * @retval HCS_STATUS_FAIL in case of generic failure. */ static enum channel_operation_status channel_ban(struct channel_data *chan, const struct map_session_data *ssd, struct map_session_data *tsd) { struct channel_ban_entry *entry = NULL; nullpo_retr(HCS_STATUS_FAIL, chan); nullpo_retr(HCS_STATUS_FAIL, tsd); if (ssd && chan->owner != ssd->status.char_id && !pc_has_permission(ssd, PC_PERM_HCHSYS_ADMIN)) return HCS_STATUS_NOPERM; if (pc_has_permission(tsd, PC_PERM_HCHSYS_ADMIN)) return HCS_STATUS_FAIL; if (chan->banned && idb_exists(chan->banned, tsd->status.account_id)) return HCS_STATUS_ALREADY; if (!chan->banned) chan->banned = idb_alloc(DB_OPT_BASE|DB_OPT_ALLOW_NULL_DATA|DB_OPT_RELEASE_DATA); CREATE(entry, struct channel_ban_entry, 1); safestrncpy(entry->name, tsd->status.name, NAME_LENGTH); idb_put(chan->banned, tsd->status.account_id, entry); channel->leave(chan, tsd); return HCS_STATUS_OK; } /** * Unbans a character from the given channel. * * @param chan The channel. * @param ssd The source character, if any. * @param tsd The target character. If no target character is specified, all characters are unbanned. * @retval HCS_STATUS_OK if the operation succeeded. * @retval HCS_STATUS_ALREADY if the target character is not banned. * @retval HCS_STATUS_NOPERM if the source character doesn't have enough permissions. * @retval HCS_STATUS_FAIL in case of generic failure. */ static enum channel_operation_status channel_unban(struct channel_data *chan, const struct map_session_data *ssd, struct map_session_data *tsd) { nullpo_retr(HCS_STATUS_FAIL, chan); if (ssd && chan->owner != ssd->status.char_id && !pc_has_permission(ssd, PC_PERM_HCHSYS_ADMIN)) return HCS_STATUS_NOPERM; if (!chan->banned) return HCS_STATUS_ALREADY; if (!tsd) { // Unban all db_destroy(chan->banned); chan->banned = NULL; return HCS_STATUS_OK; } // Unban one if (!idb_exists(chan->banned, tsd->status.account_id)) return HCS_STATUS_ALREADY; idb_remove(chan->banned, tsd->status.account_id); if (!db_size(chan->banned)) { db_destroy(chan->banned); chan->banned = NULL; } return HCS_STATUS_OK; } /** * Sets or edits a channel's options. * * @param chan The channel. * @param options The new options set to apply. */ static void channel_set_options(struct channel_data *chan, unsigned int options) { nullpo_retv(chan); chan->options = options; } /** * Sends a message to a channel. * * @param chan The destination channel. * @param sd The source character. * @param msg The message to send. * * If no source character is specified, it'll send an anonymous message. */ static void channel_send(struct channel_data *chan, struct map_session_data *sd, const char *msg) { char message[150]; nullpo_retv(chan); nullpo_retv(msg); if (sd && chan->msg_delay != 0 && DIFF_TICK(sd->hchsysch_tick + chan->msg_delay*1000, timer->gettick()) > 0 && !pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) { char output[CHAT_SIZE_MAX]; sprintf(output, msg_sd(sd, 1455), DIFF_TICK(sd->hchsysch_tick + chan->msg_delay * 1000, timer->gettick()) / 1000); // "You cannot send a message to this channel for another %d seconds." clif->messagecolor_self(sd->fd, COLOR_RED, output); return; } else if (sd) { int i; safesnprintf(message, 150, "[ #%s ] %s : %s", chan->name, sd->status.name, msg); clif->channel_msg(chan,sd,message); if (chan->type == HCS_TYPE_IRC) ircbot->relay(sd->status.name,msg); if (chan->msg_delay != 0) sd->hchsysch_tick = timer->gettick(); for (i = 0; i < MAX_EVENTQUEUE; i++) { if (chan->handlers[i][0] != '\0') { pc->setregstr(sd, script->add_variable("@channelmes$"), msg); npc->event(sd, chan->handlers[i], 0); } } } else { safesnprintf(message, 150, "[ #%s ] %s", chan->name, msg); clif->channel_msg2(chan, message); if (chan->type == HCS_TYPE_IRC) ircbot->relay(NULL, msg); } } /** * Joins a channel, without any permission checks. * * @param chan The channel to join * @param sd The character * @param stealth If true, hide join announcements. */ static void channel_join_sub(struct channel_data *chan, struct map_session_data *sd, bool stealth) { nullpo_retv(chan); nullpo_retv(sd); if (idb_put(chan->users, sd->status.char_id, sd)) return; VECTOR_ENSURE(sd->channels, 1, 1); VECTOR_PUSH(sd->channels, chan); if (!stealth && (chan->options&HCS_OPT_ANNOUNCE_JOIN)) { char message[60]; sprintf(message, msg_txt(897), chan->name, sd->status.name); // #%s '%s' joined clif->channel_msg(chan,sd,message); } /* someone is cheating, we kindly disconnect the bastard */ if (VECTOR_LENGTH(sd->channels) > 200) { sockt->eof(sd->fd); } } /** * Joins a channel. * * Ban and password checks are performed before joining the channel. * If the channel is an HCS_TYPE_ALLY channel, alliance channels are automatically joined. * * @param chan The channel to join * @param sd The character * @param password The specified join password, if any * @param silent If true, suppress the "You're now in the channel" greeting message * @retval HCS_STATUS_OK if the operation succeeded * @retval HCS_STATUS_ALREADY if the character is already in the channel * @retval HCS_STATUS_NOPERM if the specified password doesn't match * @retval HCS_STATUS_BANNED if the character is in the channel's ban list * @retval HCS_STATUS_FAIL in case of generic error */ static enum channel_operation_status channel_join(struct channel_data *chan, struct map_session_data *sd, const char *password, bool silent) { bool stealth = false; nullpo_retr(HCS_STATUS_FAIL, chan); nullpo_retr(HCS_STATUS_FAIL, sd); nullpo_retr(HCS_STATUS_FAIL, password); if (idb_exists(chan->users, sd->status.char_id)) { return HCS_STATUS_ALREADY; } if (chan->password[0] != '\0' && strcmp(chan->password, password) != 0) { if (pc_has_permission(sd, PC_PERM_HCHSYS_ADMIN)) { stealth = true; } else { return HCS_STATUS_NOPERM; } } if (chan->banned && idb_exists(chan->banned, sd->status.account_id)) { return HCS_STATUS_BANNED; } if (!silent && !(chan->options&HCS_OPT_ANNOUNCE_JOIN)) { char output[CHAT_SIZE_MAX]; if (chan->type == HCS_TYPE_MAP) { sprintf(output, msg_sd(sd,1435), chan->name, map->list[chan->m].name); // You're now in the '#%s' channel for '%s' } else { sprintf(output, msg_sd(sd,1403), chan->name); // You're now in the '%s' channel } clif->messagecolor_self(sd->fd, COLOR_DEFAULT, output); } if (chan->type == HCS_TYPE_ALLY) { struct guild *g = sd->guild; int i; for (i = 0; i < MAX_GUILDALLIANCE; i++) { struct guild *sg = NULL; if (g->alliance[i].opposition == 0 && g->alliance[i].guild_id && (sg = guild->search(g->alliance[i].guild_id)) != NULL) { if (!(sg->channel->banned && idb_exists(sg->channel->banned, sd->status.account_id))) { channel->join_sub(sg->channel, sd, stealth); } } } } channel->join_sub(chan, sd, stealth); return HCS_STATUS_OK; } /** * Removes a channel from a character's join lists. * * @param chan The channel to leave * @param sd The character */ static void channel_leave_sub(struct channel_data *chan, struct map_session_data *sd) { int i; nullpo_retv(chan); nullpo_retv(sd); ARR_FIND(0, VECTOR_LENGTH(sd->channels), i, VECTOR_INDEX(sd->channels, i) == chan); if (i < VECTOR_LENGTH(sd->channels)) { VECTOR_ERASE(sd->channels, i); } } /** * Leaves a channel. * * @param chan The channel to leave * @param sd The character */ static void channel_leave(struct channel_data *chan, struct map_session_data *sd) { nullpo_retv(chan); nullpo_retv(sd); if (!idb_remove(chan->users,sd->status.char_id)) return; if (chan == sd->gcbind) sd->gcbind = NULL; if (!db_size(chan->users) && chan->type == HCS_TYPE_PRIVATE) { channel->delete(chan); } else if (!channel->config->closing && (chan->options & HCS_OPT_ANNOUNCE_JOIN)) { char message[60]; sprintf(message, msg_txt(898), chan->name, sd->status.name); // #%s '%s' left clif->channel_msg(chan,sd,message); } channel->leave_sub(chan, sd); } /** * Quits the channel system. * * Leaves all joined channels. * * @param sd The target character */ static void channel_quit(struct map_session_data *sd) { nullpo_retv(sd); while (VECTOR_LENGTH(sd->channels) > 0) { // Loop downward to avoid unnecessary array compactions by channel_leave struct channel_data *chan = VECTOR_LAST(sd->channels); channel->leave(chan, sd); } } /** * Joins the local map channel. * * @param sd The target character (sd must be non null) */ static void channel_map_join(struct map_session_data *sd) { nullpo_retv(sd); if (sd->state.autotrade || sd->state.standalone) return; if (!map->list[sd->bl.m].channel) { if (map->list[sd->bl.m].flag.chsysnolocalaj || (map->list[sd->bl.m].instance_id >= 0 && instance->list[map->list[sd->bl.m].instance_id].owner_type != IOT_NONE)) return; map->list[sd->bl.m].channel = channel->create(HCS_TYPE_MAP, channel->config->local_name, channel->config->local_color); map->list[sd->bl.m].channel->m = sd->bl.m; } channel->join(map->list[sd->bl.m].channel, sd, "", false); } static void channel_irc_join(struct map_session_data *sd) { struct channel_data *chan = ircbot->channel; nullpo_retv(sd); if (sd->state.autotrade || sd->state.standalone) return; if (channel->config->irc_name[0] == '\0') return; if (chan) channel->join(chan, sd, "", false); } /** * Lets a guild's members join a newly allied guild's channel. * * Note: g_source members will join g_ally's channel. * To have g_ally members join g_source's channel, call this function twice, with inverted arguments. * * @param g_source Source guild * @param g_ally Allied guild */ static void channel_guild_join_alliance(const struct guild *g_source, const struct guild *g_ally) { struct channel_data *chan; nullpo_retv(g_source); nullpo_retv(g_ally); if ((chan = g_ally->channel)) { int i; for (i = 0; i < g_source->max_member; i++) { struct map_session_data *sd = g_source->member[i].sd; if (sd == NULL) continue; if (!(g_ally->channel->banned && idb_exists(g_ally->channel->banned, sd->status.account_id))) channel->join_sub(chan,sd, false); } } } /** * Makes a guild's members leave a no former allied guild's channel. * * Note: g_source members will leave g_ally's channel. * To have g_ally members leave g_source's channel, call this function twice, with inverted arguments. * * @param g_source Source guild * @param g_ally Former allied guild */ static void channel_guild_leave_alliance(const struct guild *g_source, const struct guild *g_ally) { struct channel_data *chan; nullpo_retv(g_source); nullpo_retv(g_ally); if ((chan = g_ally->channel)) { int i; for (i = 0; i < g_source->max_member; i++) { struct map_session_data *sd = g_source->member[i].sd; if (sd == NULL) continue; channel->leave(chan,sd); } } } /** * Makes a character quit all guild-related channels. * * @param sd The character (must be non null) */ static void channel_quit_guild(struct map_session_data *sd) { nullpo_retv(sd); for (int i = VECTOR_LENGTH(sd->channels) - 1; i >= 0; i--) { // Loop downward to avoid issues when channel->leave() compacts the array struct channel_data *chan = VECTOR_INDEX(sd->channels, i); if (chan->type != HCS_TYPE_ALLY) continue; channel->leave(chan, sd); } } static void read_channels_config(void) { struct config_t channels_conf; struct config_setting_t *chsys = NULL; const char *config_filename = "conf/channels.conf"; // FIXME hardcoded name if (!libconfig->load_file(&channels_conf, config_filename)) return; chsys = libconfig->lookup(&channels_conf, "chsys"); if (chsys != NULL) { struct config_setting_t *settings = libconfig->setting_get_elem(chsys, 0); struct config_setting_t *channels; struct config_setting_t *colors; int i,k; const char *local_name, *ally_name, *local_color, *ally_color, *irc_name, *irc_color; int ally_enabled = 0, local_enabled = 0, local_autojoin = 0, ally_autojoin = 0, allow_user_channel_creation = 0, irc_enabled = 0, irc_autojoin = 0, irc_flood_protection_rate = 0, irc_flood_protection_burst = 0, irc_flood_protection_enabled = 0, channel_opt_msg_delay = 10; if( !libconfig->setting_lookup_string(settings, "map_local_channel_name", &local_name) ) local_name = "map"; safestrncpy(channel->config->local_name, local_name, HCS_NAME_LENGTH); if( !libconfig->setting_lookup_string(settings, "ally_channel_name", &ally_name) ) ally_name = "ally"; safestrncpy(channel->config->ally_name, ally_name, HCS_NAME_LENGTH); if( !libconfig->setting_lookup_string(settings, "irc_channel_name", &irc_name) ) irc_name = "irc"; safestrncpy(channel->config->irc_name, irc_name, HCS_NAME_LENGTH); libconfig->setting_lookup_bool(settings, "map_local_channel", &local_enabled); libconfig->setting_lookup_bool(settings, "ally_channel_enabled", &ally_enabled); libconfig->setting_lookup_bool(settings, "irc_channel_enabled", &irc_enabled); if (local_enabled) channel->config->local = true; if (ally_enabled) channel->config->ally = true; if (irc_enabled) channel->config->irc = true; channel->config->irc_server[0] = channel->config->irc_channel[0] = channel->config->irc_nick[0] = channel->config->irc_nick_pw[0] = '\0'; if (channel->config->irc) { const char *irc_server, *irc_channel, *irc_nick, *irc_nick_pw; int irc_use_ghost = 0; if (!libconfig->setting_lookup_string(settings, "irc_channel_network", &irc_server)) { channel->config->irc = false; ShowWarning("channels.conf : irc channel enabled but irc_channel_network wasn't found, disabling irc channel...\n"); } else { char *server = aStrdup(irc_server); char *port = strchr(server, ':'); if (port == NULL) { channel->config->irc = false; ShowWarning("channels.conf: network port wasn't found in 'irc_channel_network', disabling irc channel...\n"); } else if ((size_t)(port-server) > sizeof channel->config->irc_server - 1) { channel->config->irc = false; ShowWarning("channels.conf: server name is too long in 'irc_channel_network', disabling irc channel...\n"); } else { *port = '\0'; port++; safestrncpy(channel->config->irc_server, server, sizeof channel->config->irc_server); channel->config->irc_server_port = atoi(port); } aFree(server); } if (libconfig->setting_lookup_string(settings, "irc_channel_channel", &irc_channel)) { safestrncpy(channel->config->irc_channel, irc_channel, 50); } else { channel->config->irc = false; ShowWarning("channels.conf : irc channel enabled but irc_channel_channel wasn't found, disabling irc channel...\n"); } if (libconfig->setting_lookup_string(settings, "irc_channel_nick", &irc_nick)) { if (strcmpi(irc_nick,"Hercules_chSysBot") == 0) { sprintf(channel->config->irc_nick, "Hercules_chSysBot%d",rnd()%777); } else { safestrncpy(channel->config->irc_nick, irc_nick, 40); } } else { channel->config->irc = false; ShowWarning("channels.conf : irc channel enabled but irc_channel_nick wasn't found, disabling irc channel...\n"); } if (libconfig->setting_lookup_string(settings, "irc_channel_nick_pw", &irc_nick_pw)) { safestrncpy(channel->config->irc_nick_pw, irc_nick_pw, 30); config_setting_lookup_bool(settings, "irc_channel_use_ghost", &irc_use_ghost); channel->config->irc_use_ghost = irc_use_ghost; } } libconfig->setting_lookup_bool(settings, "map_local_channel_autojoin", &local_autojoin); libconfig->setting_lookup_bool(settings, "ally_channel_autojoin", &ally_autojoin); libconfig->setting_lookup_bool(settings, "irc_channel_autojoin", &irc_autojoin); if (local_autojoin) channel->config->local_autojoin = true; if (ally_autojoin) channel->config->ally_autojoin = true; if (irc_autojoin) channel->config->irc_autojoin = true; libconfig->setting_lookup_bool(settings, "irc_flood_protection_enabled", &irc_flood_protection_enabled); if (irc_flood_protection_enabled) { ircbot->flood_protection_enabled = true; libconfig->setting_lookup_int(settings, "irc_flood_protection_rate", &irc_flood_protection_rate); libconfig->setting_lookup_int(settings, "irc_flood_protection_burst", &irc_flood_protection_burst); if (irc_flood_protection_rate > 0) ircbot->flood_protection_rate = irc_flood_protection_rate; else ircbot->flood_protection_rate = 1000; if (irc_flood_protection_burst > 0) ircbot->flood_protection_burst = irc_flood_protection_burst; else ircbot->flood_protection_burst = 3; } else { ircbot->flood_protection_enabled = false; } libconfig->setting_lookup_bool(settings, "allow_user_channel_creation", &allow_user_channel_creation); if( allow_user_channel_creation ) channel->config->allow_user_channel_creation = true; if( (colors = libconfig->setting_get_member(settings, "colors")) != NULL ) { int color_count = libconfig->setting_length(colors); CREATE(channel->config->colors, unsigned int, color_count); CREATE(channel->config->colors_name, char *, color_count); for(i = 0; i < color_count; i++) { struct config_setting_t *color = libconfig->setting_get_elem(colors, i); CREATE(channel->config->colors_name[i], char, HCS_NAME_LENGTH); safestrncpy(channel->config->colors_name[i], config_setting_name(color), HCS_NAME_LENGTH); channel->config->colors[i] = (unsigned int)strtoul(libconfig->setting_get_string_elem(colors,i),NULL,0); } channel->config->colors_count = color_count; } libconfig->setting_lookup_string(settings, "map_local_channel_color", &local_color); for (k = 0; k < channel->config->colors_count; k++) { if (strcmpi(channel->config->colors_name[k], local_color) == 0) break; } if (k < channel->config->colors_count) { channel->config->local_color = k; } else { ShowError("channels.conf: unknown color '%s' for 'map_local_channel_color', disabling '#%s'...\n",local_color,local_name); channel->config->local = false; } libconfig->setting_lookup_string(settings, "ally_channel_color", &ally_color); for (k = 0; k < channel->config->colors_count; k++) { if (strcmpi(channel->config->colors_name[k], ally_color) == 0) break; } if( k < channel->config->colors_count ) { channel->config->ally_color = k; } else { ShowError("channels.conf: unknown color '%s' for 'ally_channel_color', disabling '#%s'...\n",ally_color,ally_name); channel->config->ally = false; } libconfig->setting_lookup_string(settings, "irc_channel_color", &irc_color); for (k = 0; k < channel->config->colors_count; k++) { if (strcmpi(channel->config->colors_name[k], irc_color) == 0) break; } if (k < channel->config->colors_count) { channel->config->irc_color = k; } else { ShowError("channels.conf: unknown color '%s' for 'irc_channel_color', disabling '#%s'...\n",irc_color,irc_name); channel->config->irc = false; } if (channel->config->irc) { ircbot->channel = channel->create(HCS_TYPE_IRC, channel->config->irc_name, channel->config->irc_color); } if( (channels = libconfig->setting_get_member(settings, "default_channels")) != NULL ) { int channel_count = libconfig->setting_length(channels); for(i = 0; i < channel_count; i++) { struct config_setting_t *chan = libconfig->setting_get_elem(channels, i); const char *name = config_setting_name(chan); const char *color = libconfig->setting_get_string_elem(channels,i); ARR_FIND(0, channel->config->colors_count, k, strcmpi(channel->config->colors_name[k],color) == 0); if (k == channel->config->colors_count) { ShowError("channels.conf: unknown color '%s' for channel '%s', skipping channel...\n",color,name); continue; } if (strcmpi(name, channel->config->local_name) == 0 || strcmpi(name, channel->config->ally_name) == 0 || strcmpi(name, channel->config->irc_name) == 0 || strdb_exists(channel->db, name)) { ShowError("channels.conf: duplicate channel '%s', skipping channel...\n",name); continue; } channel->create(HCS_TYPE_PUBLIC, name, k); } } libconfig->setting_lookup_int(settings, "channel_opt_msg_delay", &channel_opt_msg_delay); if (channel_opt_msg_delay < 0) { ShowWarning("channels.conf: channel_opt_msg_delay value '%d' must be from 0-255. Defaulting to 0...\n", channel_opt_msg_delay); channel_opt_msg_delay = 0; } else if (channel_opt_msg_delay > 255) { ShowWarning("channels.conf: channel_opt_msg_delay value '%d' must be from 0-255. Defaulting to 255...\n", channel_opt_msg_delay); channel_opt_msg_delay = 255; } channel->config->channel_opt_msg_delay = channel_opt_msg_delay; ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' channels in '"CL_WHITE"%s"CL_RESET"'.\n", db_size(channel->db), config_filename); } libconfig->destroy(&channels_conf); } /*========================================== * *------------------------------------------*/ static int do_init_channel(bool minimal) { if (minimal) return 0; channel->db = stridb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, HCS_NAME_LENGTH); channel->config->ally = channel->config->local = channel->config->irc = channel->config->ally_autojoin = channel->config->local_autojoin = channel->config->irc_autojoin = false; channel->config_read(); return 0; } static void do_final_channel(void) { struct DBIterator *iter = db_iterator(channel->db); struct channel_data *chan; for( chan = dbi_first(iter); dbi_exists(iter); chan = dbi_next(iter) ) { channel->delete(chan); } dbi_destroy(iter); for (int i = 0; i < channel->config->colors_count; i++) { aFree(channel->config->colors_name[i]); } if (channel->config->colors_count) { aFree(channel->config->colors_name); aFree(channel->config->colors); } db_destroy(channel->db); } void channel_defaults(void) { channel = &channel_s; channel->db = NULL; channel->config = &channel_config; channel->init = do_init_channel; channel->final = do_final_channel; channel->search = channel_search; channel->create = channel_create; channel->delete = channel_delete; channel->set_password = channel_set_password; channel->ban = channel_ban; channel->unban = channel_unban; channel->set_options = channel_set_options; channel->send = channel_send; channel->join_sub = channel_join_sub; channel->join = channel_join; channel->leave = channel_leave; channel->leave_sub = channel_leave_sub; channel->quit = channel_quit; channel->map_join = channel_map_join; channel->guild_join_alliance = channel_guild_join_alliance; channel->guild_leave_alliance = channel_guild_leave_alliance; channel->quit_guild = channel_quit_guild; channel->irc_join = channel_irc_join; channel->config_read = read_channels_config; }