123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- #include "applicationdata.h"
- #include "global.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 ::: " + global::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<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 = "📁";
- 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);
- }
- }
|