/* GNU General Public License version 3 notice Copyright (C) 2012 Mihawk . All rights reserved. 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/ >. */ #include "App.h" #include "Client.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "SshClient.h" #include "ActiveClient.h" #include "Settings.h" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /** Used just to expose the msleep function from QThread @author Mihawk */ class Sleeper: public QThread { public: static void msleep(unsigned long msecs) { QThread::msleep(msecs); } }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - App::App(int &argc, char **argv) : QCoreApplication(argc, argv), myServer(new QTcpServer()), mySocketConnectedFlag(false), mySshClient(new SshClient(this)) { if(!parseCommandLine()) { QTimer::singleShot(0, this, SLOT(quit())); return; } print("CMP Bot Service v0.13\n========================================================\n"); setApplicationName("CMP QWBOT"); setOrganizationDomain("https://gitlab.netdome.biz/community-messaging-project/qwbot"); setOrganizationName("CMP"); setApplicationVersion("0.13"); myClientsFrameTimerID = startTimer(0); loadServerList(); print("Connecting to central...\n"); connect(mySshClient, SIGNAL(connected()), SLOT(onCentralConnection())); mySshClient->connectToHost(Settings::globalInstance()->sshUserName(), Settings::globalInstance()->sshHostName(), Settings::globalInstance()->sshPort()); Settings::globalInstance()->save(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - App::~App() { Settings::globalInstance()->save(); cleanup(); delete myServer; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool App::parseCommandLine() { if(arguments().size() < 2) return true; QStringList args = App::arguments(); QString arg; QStringList::const_iterator itr; for(itr = args.constBegin()+1; itr != args.constEnd(); ++itr) { arg = *itr; if(arg == "--help" || arg == "-h") { printf("Usage: %s [--config/-c config_file] [-h]\n", args.at(0).section("/", -1).toLatin1().data()); return false; } else if(arg == "--config" || arg == "-c") { itr++; if(itr == args.constEnd()) { printf("--config: Expected config file path.\n"); return false; } arg = *itr; printf("Using config file [%s]...\n", arg.toLatin1().data()); Settings::globalInstance()->changeConfigFile(*itr); return true; } } return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::loadServerList() { Settings::ServerList list = Settings::globalInstance()->serverList(); Settings::Server sv; foreach(sv, list) { if(myClients.size() == Settings::globalInstance()->maxServers()) { print("Maximum number of servers allowed reached, some servers weren't added to the monitoring list.\n"); return; } addClient(sv.address, sv.port, sv.password); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::saveServerList() { Settings::ServerList list; ActiveClient* ac; foreach(ac, myClients) { Settings::Server sv; sv.address = ac->serverHostNameString().split(":").at(0); sv.port = ac->serverHostNameString().split(":").at(1).toUShort(); list.push_back(sv); } Settings::globalInstance()->setServerList(list); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const QStringList& App::lastMessages() const { return myLastMessages; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::print(const QString &msg) { printf("%s", msg.toLatin1().data()); if(mySocketConnectedFlag) { mySocket->write(msg.toLatin1()); mySocket->waitForBytesWritten(); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool App::addClient(const QString &hostName, quint16 port, const QString& password) { QHostInfo hi = QHostInfo::fromName(hostName); if (hi.error() != QHostInfo::NoError) { print("Couldn't add server " + hostName + ": " + hi.errorString()); return false; } QHostAddress hostAddress = hi.addresses().first(); QString fullAddress = hostAddress.toString() + ":" + QString::number(port); ActiveClient* ac; foreach(ac, myClients) { if(ac->serverAddressString() == fullAddress) { print("That client is already on the list [" + fullAddress + "]\n"); return false; } } ac = new ActiveClient(this, password, this); ac->setAddress(hostName, hostAddress, port); ac->client()->setQuakeFolder(Settings::globalInstance()->quakeFolder().toLatin1().data()); ac->client()->setName(Settings::globalInstance()->botName().toLatin1().data()); ac->client()->setSpectator(Settings::globalInstance()->botSpectator()); ac->client()->setPing(Settings::globalInstance()->botPing()); ac->client()->setColor(Settings::globalInstance()->botTopColor(), Settings::globalInstance()->botBottomColor()); myClients.push_back(ac); saveServerList(); print("Client added to watch list [" + fullAddress + "]\n"); return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool App::removeClient(const QString &hostName, quint16 port) { QHostInfo hi = QHostInfo::fromName(hostName); if (hi.error() != QHostInfo::NoError) { print("Couldn't add server " + hostName + ": " + hi.errorString()); return false; } QHostAddress hostAddress = hi.addresses().first(); QString fullAddress = hostAddress.toString() + ":" + QString::number(port); ActiveClient* ac; foreach(ac, myClients) { if(ac->serverAddressString() == fullAddress) { ac->client()->disconnect(); delete ac; myClients.removeAll(ac); saveServerList(); print("Client removed from watch list [" + fullAddress + "]\n"); return true; } } print("Client not found on the list [" + fullAddress + "]\n"); return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::activeClientsReplyCounters(int *serverCount, int *playerCount, ActiveClient *ignoreClient) { ActiveClient* ac; foreach(ac, myClients) { if(ac == ignoreClient) continue; if(ac->client()->state() == Client::ConnectedState) { int pc = ac->playerCount(); if(pc == 255 || pc == 0) pc = 0; else pc--; *playerCount += pc; (*serverCount)++; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::timerEvent(QTimerEvent *e) { if(e->timerId() == myClientsFrameTimerID) { ActiveClient *ac; foreach(ac, myClients) { ac->run(); } // HostNames scheduled daily refresh int currentHour = QTime::currentTime().hour(); int refreshHour = Settings::globalInstance()->refreshHostNamesHour(); if(currentHour == refreshHour && !myHostNamesRequested) { print("Refreshing hostname cache...\n"); requestCachedHostNames(); myHostNamesRequested = true; } else if(currentHour != refreshHour && myHostNamesRequested) { myHostNamesRequested = false; } } Sleeper::msleep(1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::requestBroadcast(const QString &type, const QString &user, const QString &server, const QString &message) { if(!Settings::globalInstance()->developerMode()) mySshClient->write("REQ_BC QWalt,-" + type + "-,qw://" + server + ",'" + user + "','" + message + "'\n"); else mySshClient->write("REQ_BC QDEV,-dev-,qw://" + server + ",'" + user + "','" + message + "'\n"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::addMessageToHistory(const QString &msg) { myLastMessages.push_back(msg); if(myLastMessages.size() > 5) myLastMessages.removeAt(0); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::broadcast(const QString &msg, ActiveClient* ignoredClient) { ActiveClient* ac; QString frequency = msg.section(' ', 0, 0); addMessageToHistory(msg); foreach(ac, myClients) { if(ac == ignoredClient) continue; if((frequency == "-qw-" && ac->client()->isQWMuted()) || (frequency == "-spam-" && ac->client()->isSpamMuted())) continue; if(ac->client()->state() == Client::ConnectedState) ac->client()->say(msg); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::broadcast(const QString &msg, int *serverCount, int *userCount) { ActiveClient* ac; *serverCount = 0; *userCount = 0; QString frequency = msg.section(' ', 0, 0); addMessageToHistory(msg); foreach(ac, myClients) { if((frequency == "-qw-" && ac->client()->isQWMuted()) || (frequency == "-spam-" && ac->client()->isSpamMuted())) continue; if(ac->client()->state() == Client::ConnectedState) { ac->client()->say(msg); *userCount += ac->playerCount() - 1; (*serverCount)++; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::cleanup() { ActiveClient* ac; foreach(ac, myClients) { ac->client()->disconnect(); delete ac; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::setReplyHashAndWaitForReply(const QString &serverAddress, const QString &hash) { ActiveClient* ac; foreach(ac, myClients) { if(serverAddress == ac->serverAddressString()) { ac->setReplyHashAndWaitForReply(hash); return; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const QList App::clients() const { return myClients; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::incrementReplyCounters(const QString &hash, int userCount, int channelCount, int playerCount, int serverCount) { ActiveClient* ac; foreach(ac, myClients) { if(ac->replyHash() == hash) { ac->incrementReplyCounters(userCount, channelCount, playerCount, serverCount); return; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::requestCachedHostNames() { ActiveClient* ac; foreach(ac, myClients) { if (!ac->hasHostName()) { print("Requested HostName for server " + ac->serverAddressString() + "\n"); mySshClient->write("REQ_DNS " + ac->serverAddressString() + "\n"); } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::setServerHostName(const QString &serverAddress, const QString &hostName) { ActiveClient* ac; foreach(ac, myClients) { if(ac->serverAddressString() == serverAddress) { if (!ac->hasHostName() && ac->hostName() != hostName) { print("HostName for " + serverAddress + " set to " + hostName + "\n"); ac->setHostName(hostName); } return; } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - QString App::serverHostName(const QString &serverAddress) const { ActiveClient* ac; foreach(ac, myClients) { if(ac->serverAddressString() == serverAddress) { return ac->hostName(); } } return QString(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void App::onCentralConnection() { print("Connected!\nRefreshing cached hostnames...\n"); requestCachedHostNames(); }