Rewrite channel topics code to support JSON format

This commit is contained in:
relesgoe 2020-10-06 01:27:54 -07:00
parent ca61400e5d
commit 7901f666a3
15 changed files with 322 additions and 317 deletions

View file

@ -3,7 +3,7 @@ set(OUTPUT_CONFS ad.json anongame_infos.conf address_translation.conf
autoupdate.conf bnalias.conf bnban.conf bnetd_default_user.plain
bnissue.txt bnmaps.conf bnxpcalc.conf
bnxplevel.conf channel.conf command_groups.conf realm.conf
sql_DB_layout.conf supportfile.conf topics.conf
sql_DB_layout.conf supportfile.conf topics.json
tournament.conf versioncheck.json icons.conf
email_verification.conf cacert.pem)
foreach(CONF ${OUTPUT_CONFS})
@ -20,7 +20,7 @@ if(WITH_BNETD)
autoupdate.conf bnalias.conf bnban.conf
bnetd_default_user.plain bnissue.txt bnmaps.conf
bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf
realm.conf sql_DB_layout.conf supportfile.conf topics.conf
realm.conf sql_DB_layout.conf supportfile.conf topics.json
tournament.conf versioncheck.json icons.conf
email_verification.conf cacert.pem)
endif(WITH_BNETD)

View file

@ -76,7 +76,7 @@ i18ndir = "${SYSCONFDIR}/i18n"
issuefile = "${SYSCONFDIR}/bnissue.txt"
channelfile = "${SYSCONFDIR}/channel.conf"
adfile = "${SYSCONFDIR}/ad.json"
topicfile = "${SYSCONFDIR}/topics.conf"
topicfile = "${SYSCONFDIR}/topics.json"
ipbanfile = "${SYSCONFDIR}/bnban.conf"
mpqfile = "${SYSCONFDIR}/autoupdate.conf"
logfile = "${LOCALSTATEDIR}/bnetd.log"

View file

@ -62,7 +62,7 @@ i18ndir = conf\i18n
issuefile = conf\bnissue.txt
channelfile = conf\channel.conf
adfile = conf\ad.json
topicfile = conf\topics.conf
topicfile = conf\topics.json
ipbanfile = conf\bnban.conf
transfile = conf\address_translation.conf
mpqfile = conf\autoupdate.conf

View file

@ -1,2 +0,0 @@
Chat This is the public chat channel. Feel free to chat...
Moderated Support Support is provided in this channel...

6
conf/topics.json.in Normal file
View file

@ -0,0 +1,6 @@
{
"topics": {
"Chat": "This is the public chat channel. Feel free to chat...",
"Moderated Support": "Support is provided in this channel..."
}
}

View file

@ -30,6 +30,8 @@
# include "account.h"
#include <optional.hpp>
#ifdef CHANNEL_INTERNAL_ACCESS
#include <cstdio>
@ -163,6 +165,9 @@ namespace pvpgn
extern int channel_conn_has_tmpVOICE(t_channel const * channel, t_connection * c);
extern t_connection * channel_get_first(t_channel const * channel);
extern t_connection * channel_get_next(void);
extern nonstd::optional<std::string> channel_get_topic(const t_channel* channel); // in topic.cpp
extern bool channel_set_topic(t_channel* channel, const std::string& topic); // in topic.cpp
extern bool channel_display_topic(t_channel* channel, t_connection* conn); // in topic.cpp
extern int channellist_create(void);
extern int channellist_destroy(void);

View file

@ -4918,51 +4918,48 @@ namespace pvpgn
static int _handle_topic_command(t_connection * c, char const * text)
{
std::vector<std::string> args = split_command(text, 1);
std::string topicstr = args[1];
t_channel * channel = conn_get_channel(c);
t_channel* channel = conn_get_channel(c);
if (channel == nullptr)
{
message_send_text(c, message_type_error, c, localize(c, "This command can only be used inside a channel."));
return -1;
}
class_topic Topic;
char const * channel_name = channel_get_name(channel);
// set channel topic
if (!topicstr.empty())
const char* channel_name = channel_get_name(channel);
if (channel_name == nullptr)
{
if ((topicstr.size() + 1) > MAX_TOPIC_LEN)
{
msgtemp = localize(c, "Max topic length exceeded (max {} symbols)", MAX_TOPIC_LEN);
message_send_text(c, message_type_error, c, msgtemp);
return -1;
}
message_send_text(c, message_type_error, c, localize(c, "An error has occurred."));
return -1;
}
if (std::strlen(text) > std::strlen("/topic "))
{
// set channel topic
if (!(account_is_operator_or_admin(conn_get_account(c), channel_name)))
{
msgtemp = localize(c, "You must be at least a Channel Operator of {} to set the topic", channel_name);
message_send_text(c, message_type_error, c, msgtemp);
return -1;
}
bool do_save;
if (channel_get_permanent(channel))
do_save = true;
else
do_save = false;
const char* topic = text + std::strlen("/topic ");
if ((std::strlen(topic) + 1) > MAX_TOPIC_LEN)
{
msgtemp = localize(c, "Max topic length exceeded (max {} symbols)", MAX_TOPIC_LEN);
message_send_text(c, message_type_error, c, msgtemp);
Topic.set(std::string(channel_name), std::string(topicstr), do_save);
return -1;
}
channel_set_topic(channel, topic);
}
// display channel topic
if (Topic.display(c, std::string(channel_name)) == false)
else
{
msgtemp = localize(c, "{} topic: no topic", channel_name);
message_send_text(c, message_type_info, c, msgtemp);
// get channel topic
channel_display_topic(channel, c);
}
return 0;

View file

@ -2038,8 +2038,7 @@ namespace pvpgn
if (conn_is_irc_variant(c) == 0)
{
class_topic Topic;
Topic.display(c, std::string(channel_get_name(c->protocol.chat.channel)));
channel_display_topic(c->protocol.chat.channel, c);
}
if (c->protocol.chat.channel && (channel_get_flags(c->protocol.chat.channel) & channel_flags_moderated))

View file

@ -24,6 +24,9 @@
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <string>
#include <optional.hpp>
#include "compat/strcasecmp.h"
#include "common/irc_protocol.h"
@ -433,21 +436,19 @@ namespace pvpgn
static int _handle_list_command(t_connection * conn, int numparams, char ** params, char * text)
{
std::string tmp;
irc_send(conn, RPL_LISTSTART, "Channel :Users Names"); /* backward compatibility */
if (numparams == 0)
{
t_elem const * curr;
class_topic Topic;
LIST_TRAVERSE_CONST(channellist(), curr)
{
t_channel const * channel = (const t_channel*)elem_get_data(curr);
char const * tempname = irc_convert_channel(channel, conn);
std::string topicstr = Topic.get(channel_get_name(channel));
nonstd::optional<std::string> topic = channel_get_topic(channel);
/* FIXME: AARON: only list channels like in /channels command */
tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr;
std::string tmp = fmt::format("{} {} :{}", tempname, channel_get_length(channel), topic.value_or(""));
if (tmp.length() > MAX_IRC_MESSAGE_LEN)
eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded");
@ -459,7 +460,6 @@ namespace pvpgn
{
int i;
char ** e;
class_topic Topic;
e = irc_get_listelems(params[0]);
/* FIXME: support wildcards! */
@ -474,10 +474,10 @@ namespace pvpgn
if (!channel)
continue; /* channel doesn't exist */
std::string topicstr = Topic.get(channel_get_name(channel));
nonstd::optional<std::string> topic = channel_get_topic(channel);
char const * tempname = irc_convert_channel(channel, conn);
tmp = std::string(tempname) + " " + std::to_string(channel_get_length(channel)) + " :" + topicstr;
std::string tmp = fmt::format("{} {} :{}", tempname, channel_get_length(channel), topic.value_or(""));
if (tmp.length() > MAX_IRC_MESSAGE_LEN)
eventlog(eventlog_level_warn, __FUNCTION__, "LISTREPLY length exceeded");

View file

@ -26,6 +26,9 @@
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <string>
#include <optional.hpp>
#include "compat/strcasecmp.h"
#include "common/irc_protocol.h"
@ -482,20 +485,12 @@ namespace pvpgn
return 0;
}
class_topic Topic;
topicstr = Topic.get(channel_get_name(gamechannel));
nonstd::optional<std::string> topic = channel_get_topic(gamechannel);
if (topicstr.empty() == false) {
if (std::strlen(gamename) + 1 + 20 + 1 + 1 + std::strlen(topicstr.c_str()) > MAX_IRC_MESSAGE_LEN) {
WARN0("LISTREPLY length exceeded");
return 0;
}
}
else {
if (std::strlen(gamename) + 1 + 20 + 1 + 1 > MAX_IRC_MESSAGE_LEN) {
WARN0("LISTREPLY length exceeded");
return 0;
}
if (std::strlen(gamename) + 1 + 20 + 1 + 1 + topic.value_or("").length() > MAX_IRC_MESSAGE_LEN)
{
WARN0("LISTREPLY length exceeded");
return 0;
}
/***
@ -538,8 +533,9 @@ namespace pvpgn
std::strcat(temp, "::");
if (topicstr.c_str()) {
std::snprintf(temp_a, sizeof(temp_a), "%s", topicstr.c_str()); /* topic */
if (topic.has_value())
{
std::snprintf(temp_a, sizeof(temp_a), "%s", topic.value().c_str()); /* topic */
std::strcat(temp, temp_a);
}

View file

@ -27,8 +27,10 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <string>
#include <fmt/format.h>
#include <optional.hpp>
#include "compat/strcasecmp.h"
@ -1740,13 +1742,12 @@ namespace pvpgn
extern int irc_send_topic(t_connection* c, t_channel const* channel)
{
class_topic Topic;
std::string topicstr = Topic.get(channel_get_name(channel));
nonstd::optional<std::string> topic = channel_get_topic(channel);
char temp[MAX_IRC_MESSAGE_LEN];
if (topicstr.empty() == false)
if (topic.has_value())
{
std::snprintf(temp, sizeof(temp), "%s :%s", irc_convert_channel(channel, c), topicstr.c_str());
std::snprintf(temp, sizeof(temp), "%s :%s", irc_convert_channel(channel, c), topic.value().c_str());
irc_send(c, RPL_TOPIC, temp);
}
else
@ -1765,15 +1766,17 @@ namespace pvpgn
{
char** e = NULL;
char temp[MAX_IRC_MESSAGE_LEN];
class_topic Topic;
if (params && params[0])
{
if (conn_get_wol(conn) == 1) {
t_channel* channel = conn_get_channel(conn);
if (channel)
Topic.set(std::string(channel_get_name(channel)), std::string(text), false);
else {
{
channel_set_topic(channel, text);
}
else
{
std::snprintf(temp, sizeof(temp), "%s :You're not on that channel", params[0]);
irc_send(conn, ERR_NOTONCHANNEL, temp);
}

View file

@ -420,7 +420,7 @@ int pre_server_startup(void)
teamlist_load();
if (realmlist_create(prefs_get_realmfile()) < 0)
eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list");
//topiclist_load(std::string(prefs_get_topicfile()));
load_topic_conf(prefs_get_topicfile());
userlog_init();
if (prefs_get_verify_account_email() == 1)
{
@ -460,7 +460,7 @@ void post_server_shutdown(int status)
switch (status)
{
case 0:
//topiclist_unload();
unload_topic_conf();
account_email_verification_unload();
smtp_cleanup();
account_email_verification_unload();

View file

@ -1329,6 +1329,7 @@ namespace pvpgn
clanlist_save();
gamelist_check_voidgame();
ladders.save();
topiclist_save();
next_savetime += prefs_get_user_sync_timer();
}
accountlist_save(FS_NONE);

View file

@ -1,248 +1,283 @@
/*
* Copyright (C) 2015 xboi209
*
* 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 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 <http://www.gnu.org/licenses/>.
*/
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "common/setup_before.h"
#include "topic.h"
#include <chrono>
#include <fstream>
#include <memory>
#include <new>
#include <regex>
#include <string>
#include <unordered_map>
#include <utility>
#include <optional.hpp>
#include "json/json.hpp"
#include "compat/strcasecmp.h"
#include "common/eventlog.h"
#include "common/field_sizes.h"
#include "channel.h"
#include "i18n.h"
#include "message.h"
#include "prefs.h"
#include "common/setup_after.h"
using nlohmann::json;
using nonstd::optional;
using nonstd::nullopt;
namespace pvpgn
{
namespace bnetd
{
std::vector<std::shared_ptr<class_topic::t_topic>> class_topic::class_topiclist::Head;
static std::unordered_map<std::string, std::string> topic_head;
bool class_topic::class_topiclist::IsHeadLoaded = false;
static bool is_topic_conf_loaded = false;
static std::string topic_conf_filename;
class_topic::class_topic()
bool load_topic_conf(const std::string& filename)
{
//Already loaded, do not load again
if (this->topiclist.IsHeadLoaded == true)
return;
std::ifstream topicfile_stream(prefs_get_topicfile());
if (!topicfile_stream)
if (is_topic_conf_loaded)
{
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
return;
eventlog(eventlog_level_warn, __FUNCTION__, "topic conf already loaded");
return true;
}
std::string strLine;
std::smatch match;
const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic
auto t0 = std::chrono::steady_clock::now();
//loop through each line in topic file
while (std::getline(topicfile_stream, strLine))
try
{
//skip empty lines
if (strLine.empty() == true)
continue;
if (!std::regex_search(strLine, match, rgx))
std::ifstream topicfile_stream(filename);
if (!topicfile_stream.is_open())
{
eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str());
continue;
}
//check if current line in file already exists in Head
if (this->topiclist.get(match[1].str()) != nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "Duplicate line for channel {} in topic file", match[1].str().c_str());
continue;
}
//save current line to Head
this->topiclist.add(match[1].str(), match[2].str(), true);
}
this->topiclist.IsHeadLoaded = true;
}
//Get channel_name's topic string
std::string class_topic::get(const std::string channel_name)
{
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
{
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
return std::string();
}
auto topic = this->topiclist.get(channel_name);
if (topic == nullptr)
return std::string();
return topic->topicstr;
}
//Sets channel_name's topic
bool class_topic::set(const std::string channel_name, const std::string topic_text, bool do_save)
{
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
{
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
return false;
}
if ((topic_text.size() + 1) > MAX_TOPIC_LEN || topic_text.empty() == true)
{
eventlog(eventlog_level_error, __FUNCTION__, "got invalid topic length");
return false;
}
auto topic = this->topiclist.get(channel_name);
if (topic != nullptr)
{
eventlog(eventlog_level_trace, __FUNCTION__, "Setting <{}>'s topic to <{}>", channel_name.c_str(), topic_text.c_str());
topic->topicstr = topic_text;
}
else
{
eventlog(eventlog_level_trace, __FUNCTION__, "Adding <{}:{}> to topiclist", channel_name.c_str(), topic_text.c_str());
this->topiclist.add(channel_name, topic_text, do_save);
}
if (do_save == true)
{
if (this->topiclist.save() == false)
{
eventlog(eventlog_level_error, __FUNCTION__, "error saving topic list");
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file \"{}\"", filename);
return false;
}
}
return true;
}
json jconf;
topicfile_stream >> jconf;
// Displays channel_name's topic to connection c
bool class_topic::display(t_connection * c, const std::string channel_name)
{
if ((channel_name.size() + 1) > MAX_CHANNELNAME_LEN || channel_name.empty() == true)
{
eventlog(eventlog_level_error, __FUNCTION__, "got invalid channel name length");
return false;
}
auto topic = this->topiclist.get(channel_name);
if (topic == nullptr)
return false;
auto topicstr = topic->topicstr;
if (topicstr.empty())
{
eventlog(eventlog_level_error, __FUNCTION__, "topic is empty");
return false;
}
//send parts of topic string as separate message if there's a newline character
std::regex rgx("\\\\n+");
std::sregex_token_iterator iter(topicstr.begin(), topicstr.end(), rgx, -1), end;
for (bool first = true; iter != end; ++iter)
{
std::string msg(iter->str());
if (first == true)
for (auto& entry : jconf.at("topics").items())
{
msg.insert(0, topic->channel_name + " topic: ");
first = false;
}
message_send_text(c, message_type_info, c, msg);
}
return true;
}
//Get t_topic pointer of channel_name
std::shared_ptr<class_topic::t_topic> class_topic::class_topiclist::get(const std::string channel_name)
{
for (auto topic : this->Head)
{
if (strcasecmp(channel_name.c_str(), topic->channel_name.c_str()) == 0)
return topic;
}
eventlog(eventlog_level_debug, __FUNCTION__, "returning nullptr");
return nullptr;
}
//Saves data from Head vector to topic file
bool class_topic::class_topiclist::save()
{
std::fstream topicfile_stream(prefs_get_topicfile(), std::ofstream::app);
if (!topicfile_stream)
{
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
return false;
}
std::string strLine;
std::smatch match;
const std::regex rgx("(.*)\t(.*)"); // tab character separates the channel name and topic
//Check if data in Head vector already exists in topic file
while (std::getline(topicfile_stream, strLine))
{
if (!std::regex_search(strLine, match, rgx))
{
eventlog(eventlog_level_error, __FUNCTION__, "Invalid line in topic file ({})", strLine.c_str());
continue;
}
for (auto topic : this->Head)
{
if (topic->save == true)
try
{
if (match[1].str() == topic->channel_name)
auto success = topic_head.emplace(entry.key(), entry.value());
if (!success.second)
{
break;
}
else
{
topicfile_stream << topic->channel_name << "\t" << topic->topicstr << std::endl;
break;
eventlog(eventlog_level_warn, __FUNCTION__, "failed to load topic for channel \"{}\" (possible duplicate?)", entry.key());
}
}
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "could not load topic for channel \"{}\"", entry.key());
continue;
}
}
}
return true;
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "failed to load {} ({})", filename, e.what());
return false;
}
auto t1 = std::chrono::steady_clock::now();
eventlog(eventlog_level_info, __FUNCTION__, "Successfully loaded {} channel topics in {} milliseconds", topic_head.size(), std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count());
topic_conf_filename = filename;
is_topic_conf_loaded = true;
return is_topic_conf_loaded;
}
//Adds a new pointer to the Head vector
void class_topic::class_topiclist::add(std::string channel_name, std::string topic_text, bool do_save)
void unload_topic_conf()
{
auto topic = std::make_shared<class_topic::t_topic>(class_topic::t_topic{ channel_name, topic_text, do_save });
this->Head.push_back(std::move(topic));
topiclist_save();
topic_head.clear();
topic_conf_filename.clear();
is_topic_conf_loaded = false;
eventlog(eventlog_level_info, __FUNCTION__, "Successfully unloaded all channel topics");
}
void topiclist_save()
{
if (!is_topic_conf_loaded)
{
eventlog(eventlog_level_error, __FUNCTION__, "topic conf not loaded");
return;
}
try
{
std::ofstream topicfile_stream(topic_conf_filename);
if (!topicfile_stream.is_open())
{
eventlog(eventlog_level_error, __FUNCTION__, "couldn't open topic file");
return;
}
json jconf;
jconf["topics"] = topic_head;
topicfile_stream << jconf.dump(1, '\t');
}
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "failed to save topic_head to {} ({})", topic_conf_filename, e.what());
return;
}
}
nonstd::optional<std::string> channel_get_topic(const t_channel* channel)
{
if (channel == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
return nonstd::nullopt;
}
const char* channelname = channel_get_name(channel);
if (channelname == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel name");
return nonstd::nullopt;
}
try
{
auto search = topic_head.find(channelname);
if (search == topic_head.end())
{
return nonstd::nullopt;
}
return nonstd::optional<std::string>{search->second};
}
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "failed to get channel topic for channel \"{}\"", channelname);
return nonstd::nullopt;
}
}
bool channel_set_topic(t_channel* channel, const std::string& topic)
{
if (channel == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
return false;
}
const char* channelname = channel_get_name(channel);
if (channelname == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel name");
return false;
}
try
{
auto success = topic_head.emplace(channelname, topic);
if (success.second == false)
{
// topic was not inserted because a topic for the channel already exists in topic_head
success.first->second = topic;
}
return true;
}
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "failed to set channel topic for channel \"{}\"", channelname);
return false;
}
}
bool channel_display_topic(t_channel* channel, t_connection* conn)
{
if (channel == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL channel");
return false;
}
if (conn == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL conn");
return false;
}
try
{
nonstd::optional<std::string> topic = channel_get_topic(channel);
if (topic.has_value())
{
const std::string delimiter = "\\n";
const std::size_t delimiter_len = delimiter.length();
const std::string topicstr = topic.value();
// support "\n" (not '\n') by individually sending substrings
std::size_t old_newline_pos = 0;
std::size_t curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
while (curr_newline_pos != std::string::npos)
{
if (old_newline_pos == curr_newline_pos)
{
old_newline_pos += delimiter_len;
curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
continue;
}
std::string substr = topicstr.substr(old_newline_pos, curr_newline_pos - old_newline_pos);
message_send_text(conn, message_type_info, conn, substr);
old_newline_pos = curr_newline_pos + delimiter_len;
curr_newline_pos = topicstr.find(delimiter, old_newline_pos);
}
message_send_text(conn, message_type_info, conn, topicstr.substr(old_newline_pos, std::string::npos));
}
return true;
}
catch (const std::exception& e)
{
message_send_text(conn, message_type_error, conn, localize(conn, "An error has occurred."));
const char* channelname = channel_get_name(channel);
eventlog(eventlog_level_error, __FUNCTION__, "failed to display channel topic for channel \"{}\"", channelname ? channelname : "?");
return false;
}
}
}

View file

@ -1,72 +1,37 @@
/*
* Copyright (C) 2015 xboi209
*
* 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 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 <http://www.gnu.org/licenses/>.
*/
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDED_TOPIC_TYPES
#define INCLUDED_TOPIC_TYPES
#ifndef INCLUDED_TOPIC_H
#define INCLUDED_TOPIC_H
#include <memory>
#include <string>
#include <vector>
#include "connection.h"
#include "common/list.h"
#endif
#ifndef JUST_NEED_TYPES
#ifndef INCLUDED_TOPIC_PROTOS
#define INCLUDED_TOPIC_PROTOS
namespace pvpgn
{
namespace bnetd
{
class class_topic
{
//all public member functions of class_topic verify channel name and topic length
public:
class_topic();
bool load_topic_conf(const std::string& filename);
void unload_topic_conf();
void topiclist_save();
std::string get(const std::string channel_name);
bool set(const std::string channel_name, const std::string topic_text, bool do_save);
bool display(t_connection * c, const std::string channel_name);
private:
struct t_topic
{
std::string channel_name;
std::string topicstr;
bool save;
};
}
class class_topiclist
{
public:
static bool IsHeadLoaded;
std::shared_ptr<class_topic::t_topic> get(const std::string channel_name);
bool save();
void add(std::string channel_name, std::string topic_text, bool do_save);
private:
static std::vector<std::shared_ptr<class_topic::t_topic>> Head;
} topiclist;
};
}
} //namespace bnetd
} //namespace pvpgn
#endif
#endif