#include "applicationdata.h" #include "global.h" #include "version.h" #include #include #include #include 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 = 📁\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 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 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 = "📁"; QMap 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); } }