ircabot/applicationdata.cpp

344 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "applicationdata.h"
#include "global.h"
#include "version.h"
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QRegularExpression>
void ApplicationData::createConfigExample(const QString &pathToConfig)
{
QFile file(pathToConfig);
if (not file.open(QIODevice::WriteOnly)) {
throw std::runtime_error("ApplicationData::createConfigExample(): Output file openning is failed");
}
const QString configExample {
"[GLOBAL]\n"
"data_path = /srv/ircabot/data\n"
"# Uncomment \"AJAXIsDisabled\" for disable real time reading mode.\n"
"# This mode generates many requests from the client side\n"
"# and in exceptional cases may be undesirable. All JavaScript code will be removed.\n"
"#AJAXIsDisabled = true\n\n"
"# Global options for all servers.\n"
"nick = default_nickname\n"
"user = default_ident\n"
"real_name = default_real_name\n"
"# If password is empty logging in without password.\n"
"password = password_for_NickServ\n\n"
"# This triggers available in all chats.\n"
"# Request and answer must be splitted by \":::\". Triggers splitting by \"<!>\".\n"
"# You can use \"%CHANNEL_FOR_URL%\" to automatically substitute the address for the chat,\n"
"# from which the link to the web interface is requested.\n"
"triggers = version ::: " + IRCABOT_VERSION + " <!> webui ::: http://example.com/%CHANNEL_FOR_URL%\n\n"
"# Web interface options\n"
"# service_* parameters used for the appearance of the home button\n"
"service_name = IRCaBot\n"
"service_emoji = &#128193;\n"
"bind_to_address = 127.0.0.1\n"
"bind_to_port = 8080\n\n"
"[Displayed server name]\n"
"address = 127.0.0.1\n"
"port = 6667\n"
"# Channels splitting with comma.\n"
"channels = #general,#acetonevideo\n"
"# This triggers available in current server only.\n"
"triggers = hi ::: hello <!> developer ::: acetone\n\n"
"# Optional parameters if global is defined:\n"
"#nick = unique_nickname_for_this_server\n"
"#user = unique_ident_for_this_server\n"
"#real_name = unique_real_name_for_this_server\n"
"#password = password_for_this_user\n"
};
file.write(configExample.toUtf8());
file.close();
}
ApplicationData::ApplicationData(const QString& pathToConfig) :
m_webInterfacePort(0),
m_ajaxIsDisabled(false)
{
if (not QFile::exists(pathToConfig)) {
throw std::runtime_error("Configuration file not exist (" + pathToConfig.toStdString() + ")");
}
m_file = pathToConfig;
readConfig();
}
std::pair<QString, quint16> ApplicationData::getWebInterfaceAddress()
{
if (m_webInterfacePort == 0) {
throw std::runtime_error("Web interface port is undefined");
}
if (m_webInterfaceAddress.isEmpty()) {
throw std::runtime_error("Web interface address is undefined");
}
return {m_webInterfaceAddress, m_webInterfacePort};
}
QList<ConnectionData> ApplicationData::getConnections()
{
return m_connections;
}
QString ApplicationData::getServiceEmoji()
{
return m_serviceEmoji;
}
QString ApplicationData::getServiceName()
{
return m_serviceName;
}
QString ApplicationData::getDataFolder()
{
return m_dataPath;
}
bool ApplicationData::getAjaxIsDisabled()
{
return m_ajaxIsDisabled;
}
void ApplicationData::readConfig()
{
QFile file(m_file);
if (not file.open(QIODevice::ReadOnly)) {
throw std::runtime_error("ApplicationData::readConfig(): Can't open file for reading");
}
QString line {file.readLine()};
QString conffile;
while (not line.isEmpty()) {
line.remove('\n');
line.remove('\r');
if (line.startsWith('#') or line.isEmpty()) {
line = file.readLine();
continue;
}
conffile += line + '\n';
line = file.readLine();
}
file.close();
if (conffile.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): Empty data");
}
//// Парсинг GLOBAL
QString globalSection {conffile};
int globalBegin = conffile.indexOf("[GLOBAL]");
if (globalBegin == -1) {
throw std::runtime_error("ApplicationData::readConfig(): Wrong config. [GLOBAL] section not exist!");
}
int globalEnd = conffile.indexOf(QRegularExpression("\\[[^:]*\\]"), globalBegin+1); //IPv6 addresses safe
if (globalEnd != -1) {
// Удаление последующей [секции]
globalSection.remove(globalEnd, conffile.size()-globalEnd);
}
globalSection.remove(0, globalBegin);
//// Инициализация GLOBAL
// logPath
m_dataPath = global::getValue(globalSection, "data_path");
if (m_dataPath.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): 'data_path' in [GLOBAL] is undefined");
}
QDir logDir;
if (not QFile::exists(m_dataPath)) {
if (not logDir.mkpath(m_dataPath)) {
throw std::runtime_error("ApplicationData::readConfig(): Can't create " + m_dataPath.toStdString());
}
}
logDir.setPath(m_dataPath);
if (not logDir.mkdir("TeSt__FoLdEr")) {
throw std::runtime_error("ApplicationData::readConfig(): Log path '" + m_dataPath.toStdString() + "' is unwritable");
}
logDir.rmdir("TeSt__FoLdEr");
if (not m_dataPath.endsWith(global::slash)) {
m_dataPath += global::slash;
}
// web interface
m_webInterfaceAddress = global::getValue(globalSection, "bind_to_address");
if (m_webInterfaceAddress.isEmpty()) {
throw std::runtime_error("ApplicationData::readConfig(): 'bind_to_address' in [GLOBAL] is undefined");
}
bool success = false;
m_webInterfacePort = global::getValue(globalSection, "bind_to_port").toUShort(&success);
if (not success) {
throw std::runtime_error("ApplicationData::readConfig(): 'bind_to_port' in [GLOBAL] is incorrect");
}
m_serviceName = global::getValue(globalSection, "service_name");
if (m_serviceName.isEmpty()) m_serviceName = "IRCaBot";
m_serviceEmoji = global::getValue(globalSection, "service_emoji");
if (m_serviceEmoji.isEmpty()) m_serviceEmoji = "&#128193;";
QMap<QString, QString> globalTriggers;
QString triggersLine = global::getValue(globalSection, "triggers");
if (not triggersLine.isEmpty()) {
QStringList triggersPair = triggersLine.split("<!>");
for (auto &p: triggersPair) {
QStringList pair = p.split(":::");
if (pair.size() != 2) continue;
QString request = pair.first();
QString answer = pair.last();
if (request.startsWith(' ')) request.remove(QRegularExpression("^\\s*"));
if (request.endsWith(' ')) request.remove(QRegularExpression("\\s*$"));
if (answer.startsWith(' ')) answer.remove(QRegularExpression("^\\s*"));
if (answer.endsWith(' ')) answer.remove(QRegularExpression("\\s*$"));
if (request.isEmpty() or answer.isEmpty()) continue;
globalTriggers[request] = answer;
qInfo().noquote() << "[GLOBAL] Trigger (" << request << ":::" << answer << ")";
}
}
m_ajaxIsDisabled = global::getValue(globalSection, "AJAXIsDisabled") == "true";
// other
m_nick = global::getValue(globalSection, "nick");
m_nick.replace(' ', '_');
m_user = global::getValue(globalSection, "user");
m_realName = global::getValue(globalSection, "real_name");
m_password = global::getValue(globalSection, "password");
conffile.remove(globalSection);
//// Парсинг подключений
// Цикл до тех пор, пока остались заголовки секций
while (conffile.contains(QRegularExpression("\\[[^\\n]*\\]"))) {
QString currentSection {conffile};
int begin = conffile.indexOf(QRegularExpression("\\[[^\\n]*\\]"));
int end = conffile.indexOf(QRegularExpression("\\[[^:]*\\]"), begin+1);
if (end != -1) {
currentSection.remove(end, currentSection.size() - end);
}
currentSection.remove(0, begin);
conffile.remove(currentSection);
//// Инициализация информации о подключениях
ConnectionData newConnection;
newConnection.displayName = currentSection.mid(1, currentSection.indexOf(']')-1);
newConnection.address = global::getValue(currentSection, "address");
if (newConnection.address.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty 'address')";
continue;
}
success = false;
newConnection.port = global::getValue(currentSection, "port").toUShort(&success);
if (not success) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (wrong 'port')";
continue;
}
QString channelsString = global::getValue(currentSection, "channels");
if (channelsString.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty 'channels')";
continue;
}
newConnection.channels = channelsString.split(',');
newConnection.channels.removeAll("");
for (auto &ch: newConnection.channels) {
ch.remove(' ');
if (not ch.startsWith('#')) {
ch = '#' + ch;
}
}
newConnection.user = global::getValue(currentSection, "user");
if (newConnection.user.isEmpty()) {
if (m_user.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'user' and global too)";
continue;
}
else {
newConnection.user = m_user;
}
}
newConnection.nick = global::getValue(currentSection, "nick");
if (newConnection.nick.isEmpty()) {
if (m_nick.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'nick' and global too)";
continue;
}
else {
newConnection.nick = m_nick;
}
}
else {
newConnection.nick.replace(' ', '_');
}
newConnection.realName = global::getValue(currentSection, "real_name");
if (newConnection.realName.isEmpty()) {
if (m_realName.isEmpty()) {
qInfo().noquote() << "[" + newConnection.displayName + "]" << "ignored (empty local 'real_name' and global too)";
continue;
}
else {
newConnection.realName = m_realName;
}
}
newConnection.password = global::getValue(currentSection, "password");
if (newConnection.password.isEmpty() and not m_password.isEmpty()) {
newConnection.password = m_password;
}
QString path {global::toLowerAndNoSpaces(newConnection.displayName)};
newConnection.logFolderPath = m_dataPath + path;
QString triggersLine = global::getValue(currentSection, "triggers");
if (not triggersLine.isEmpty()) {
QStringList triggersPair = triggersLine.split("<!>");
for (auto &p: triggersPair) {
QStringList pair = p.split(":::");
if (pair.size() != 2) continue;
QString request = pair.first();
QString answer = pair.last();
if (request.startsWith(' ')) request.remove(QRegularExpression("^\\s*"));
if (request.endsWith(' ')) request.remove(QRegularExpression("\\s*$"));
if (answer.startsWith(' ')) answer.remove(QRegularExpression("^\\s*"));
if (answer.endsWith(' ')) answer.remove(QRegularExpression("\\s*$"));
if (request.isEmpty() or answer.isEmpty()) continue;
newConnection.triggers[request] = answer;
qInfo().noquote() << "["+newConnection.displayName+"] Trigger (" << request << ":::" << answer << ")";
}
}
for (auto &glob: globalTriggers) {
bool detected = false;
for (auto local: newConnection.triggers) {
if (newConnection.triggers.key(local) == globalTriggers.key(glob)) {
qInfo().noquote() << "[" + newConnection.displayName + "]" <<
"Trigger '" + globalTriggers.key(glob) + "' ignored (GLOBAL)";
detected = true;
}
}
if (not detected) {
newConnection.triggers[globalTriggers.key(glob)] = glob;
}
}
m_connections.push_back(newConnection);
}
}