|
@@ -0,0 +1,486 @@
|
|
|
+#include "App.h"
|
|
|
+#include "Client.h"
|
|
|
+#include <QStringList>
|
|
|
+#include <QTcpSocket>
|
|
|
+#include <QTcpServer>
|
|
|
+#include <QRegExp>
|
|
|
+#include <QHostInfo>
|
|
|
+#include <QTimerEvent>
|
|
|
+#include <QDebug>
|
|
|
+#include <QtSql/QSqlQuery>
|
|
|
+#include <QCryptographicHash>
|
|
|
+#include <QHostAddress>
|
|
|
+#include "SshClient.h"
|
|
|
+#include "ServerQueryThread.h"
|
|
|
+
|
|
|
+App::App(int &argc, char **argv) :
|
|
|
+ QCoreApplication(argc, argv),
|
|
|
+ myServer(new QTcpServer()),
|
|
|
+ mySocketConnectedFlag(false),
|
|
|
+ myQWNETSshClient(new SshClient(this))
|
|
|
+{
|
|
|
+ print("CIMS Bot Service v0.101\n=========================================================\n");
|
|
|
+
|
|
|
+ setApplicationName("CIMSBOT");
|
|
|
+ setOrganizationDomain("qwbr.tk");
|
|
|
+ setOrganizationName("CIMS");
|
|
|
+ setApplicationVersion("0.101");
|
|
|
+
|
|
|
+ myServer->listen(QHostAddress::Any, 45000);
|
|
|
+ connect(myServer, SIGNAL(newConnection()), SLOT(onNewConnection()));
|
|
|
+
|
|
|
+ myClientsFrameTimerID = startTimer(0);
|
|
|
+}
|
|
|
+
|
|
|
+void App::onNewConnection()
|
|
|
+{
|
|
|
+ if(mySocketConnectedFlag)
|
|
|
+ {
|
|
|
+ print("Someone just connected to the bot.\nBye Bye.\n");
|
|
|
+ mySocket->disconnectFromHost();
|
|
|
+ if(mySocket->state() != QTcpSocket::UnconnectedState)
|
|
|
+ mySocket->waitForDisconnected();
|
|
|
+ mySocketConnectedFlag = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ mySocket = myServer->nextPendingConnection();
|
|
|
+ mySocketConnectedFlag = true;
|
|
|
+ connect(mySocket, SIGNAL(readyRead()), SLOT(onDataArrival()));
|
|
|
+ connect(mySocket, SIGNAL(disconnected()), SLOT(onDisconnection()));
|
|
|
+
|
|
|
+ print("Connected to CIMS's bot service.\n");
|
|
|
+
|
|
|
+ /* Set socket on clients for echoing */
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setSocket(mySocket);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::onDisconnection()
|
|
|
+{
|
|
|
+ /* set all sockets to null so it doenst echo anything on a dangling pointer */
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setSocket(NULL);
|
|
|
+ }
|
|
|
+ mySocketConnectedFlag = false;
|
|
|
+ mySocket->deleteLater();
|
|
|
+}
|
|
|
+
|
|
|
+void App::loadClients()
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void App::connectToServer(const QString &args)
|
|
|
+{
|
|
|
+ if(!args.size())
|
|
|
+ {
|
|
|
+ print("No server specified\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if(args.contains(" "))
|
|
|
+ {
|
|
|
+ print("Invalid server address\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QStringList clientData = args.split(":");
|
|
|
+ if(clientData.size() == 1)
|
|
|
+ {
|
|
|
+ addClient(clientData.at(0), 27500);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(clientData.size() == 2)
|
|
|
+ {
|
|
|
+ addClient(clientData.at(0), clientData.at(1).toUShort());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::disconnectFromServer(const QString &args)
|
|
|
+{
|
|
|
+ if(!args.size())
|
|
|
+ {
|
|
|
+ print("No server specified\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QStringList clientData = args.split(":");
|
|
|
+ if(clientData.size() == 1)
|
|
|
+ {
|
|
|
+ removeClient(clientData.at(0), 27500);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(clientData.size() == 2)
|
|
|
+ {
|
|
|
+ removeClient(clientData.at(0), clientData.at(1).toUShort());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::onDataArrival()
|
|
|
+{
|
|
|
+ while(mySocket->canReadLine())
|
|
|
+ {
|
|
|
+ QByteArray line;
|
|
|
+
|
|
|
+ line = mySocket->readLine();
|
|
|
+
|
|
|
+ QString data(line);
|
|
|
+ QRegExp regex("([0-9a-zA-Z]+)\\s+([a-z]+)\\s+(.*)");
|
|
|
+ if(regex.indexIn(data) == -1)
|
|
|
+ {
|
|
|
+ print("command format: <password> <command> ?<arguments>\nEg.: pss help\nDisconnected\n");
|
|
|
+ mySocket->disconnectFromHost();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QString pass = regex.capturedTexts().at(1);
|
|
|
+ if(!checkPassword(pass))
|
|
|
+ {
|
|
|
+ print("Wrong password\nDisconnected\n");
|
|
|
+ mySocket->disconnectFromHost();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QString cmd = regex.capturedTexts().at(2);
|
|
|
+ QString args = regex.capturedTexts().at(3).trimmed();
|
|
|
+
|
|
|
+ if(cmd == "connect")
|
|
|
+ {
|
|
|
+ connectToServer(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "disconnect")
|
|
|
+ {
|
|
|
+ disconnectFromServer(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "say")
|
|
|
+ {
|
|
|
+ say(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "say_team")
|
|
|
+ {
|
|
|
+ sayTeam(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "servers")
|
|
|
+ {
|
|
|
+ listClients();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "name")
|
|
|
+ {
|
|
|
+ setNick(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "color")
|
|
|
+ {
|
|
|
+ setColor(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "setping")
|
|
|
+ {
|
|
|
+ setPing(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "team")
|
|
|
+ {
|
|
|
+ setTeam(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "quit")
|
|
|
+ {
|
|
|
+ mySocket->disconnectFromHost();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(cmd == "help")
|
|
|
+ {
|
|
|
+ help();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::help()
|
|
|
+{
|
|
|
+ print("connect server:port -> connects the bot on a server\n");
|
|
|
+ print("disconnect server:port -> removes the bot from a server\n");
|
|
|
+ print("say message -> says the message on all servers where the bot is connected\n");
|
|
|
+ print("say_team message -> says the message on all servers where the bot is connected\n");
|
|
|
+ print("servers -> lists all servers the bot is connected\n");
|
|
|
+ print("name nick -> changes the bot name to nick\n");
|
|
|
+ print("color x x -> changes the player color\n");
|
|
|
+ print("setping x -> sets the bot ping to x. ofc you can't lower your actual ping with this.\n");
|
|
|
+ print("team teamname -> sets the bot team\n");
|
|
|
+ print("help -> displays this message\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::setTeam(const QString &args)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setTeam(args);
|
|
|
+ }
|
|
|
+ print("Team changed.\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::setColor(const QString &args)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ quint8 bottom, top;
|
|
|
+ QStringList colors = args.split(" ");
|
|
|
+
|
|
|
+ if(colors.size() < 2)
|
|
|
+ return;
|
|
|
+
|
|
|
+ bottom = colors.at(0).toUShort();
|
|
|
+ top = colors.at(1).toUShort();
|
|
|
+
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setColor(bottom, top);
|
|
|
+ }
|
|
|
+ print("All clients colors have changed.\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::setPing(const QString &args)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setPing(args.toInt());
|
|
|
+ }
|
|
|
+ print("All clients pings have changed.\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::setNick(const QString &args)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->setName(args.toAscii().data());
|
|
|
+ }
|
|
|
+ print("All clients nicks have changed.\n");
|
|
|
+}
|
|
|
+
|
|
|
+bool App::checkPassword(const QString &password)
|
|
|
+{
|
|
|
+ if(QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md4).toHex().toLower() == "bf4df9f74d05c50ea00492224fb02854")
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void App::print(const QString &msg)
|
|
|
+{
|
|
|
+ printf("%s", msg.toAscii().data());
|
|
|
+ if(mySocketConnectedFlag)
|
|
|
+ {
|
|
|
+ mySocket->write(msg.toAscii());
|
|
|
+ mySocket->waitForBytesWritten();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::addClient(const QString &host, quint16 port)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
|
|
|
+
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ if(QString(ac->client()->host()) == ha.toString() && ac->client()->port() == port)
|
|
|
+ {
|
|
|
+ print("That client is already on the list.\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ac = new ActiveClient(this);
|
|
|
+ if(mySocketConnectedFlag)
|
|
|
+ {
|
|
|
+ if(mySocket->state() == QTcpSocket::ConnectedState)
|
|
|
+ {
|
|
|
+ ac->client()->setSocket(mySocket);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ac->setAddress(ha, port);
|
|
|
+ ac->client()->setQuakeFolder(mySettings.value("quakeFolder", App::applicationDirPath()).toString().toAscii().data());
|
|
|
+ ac->client()->setName(mySettings.value("botName", "cimsbot").toString().toAscii().data());
|
|
|
+ ac->client()->setSpectator(true);
|
|
|
+ ac->client()->setPing(120);
|
|
|
+ ac->client()->setColor(11, 13);
|
|
|
+ myClients.push_back(ac);
|
|
|
+}
|
|
|
+
|
|
|
+void App::removeClient(const QString &host, quint16 port)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
|
|
|
+
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ if(QString(ac->client()->host()) == ha.toString() && ac->client()->port() == port)
|
|
|
+ {
|
|
|
+ ac->client()->disconnect();
|
|
|
+ delete ac;
|
|
|
+ myClients.removeAll(ac);
|
|
|
+
|
|
|
+
|
|
|
+ print("Client removed.\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ print("Client not found on the list.\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::say(const QString &msg)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->say(msg);
|
|
|
+ }
|
|
|
+ print("Say command sent to all clients\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::sayTeam(const QString &msg)
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->sayTeam(msg);
|
|
|
+ }
|
|
|
+ print("Say_team command sent to all clients\n");
|
|
|
+}
|
|
|
+
|
|
|
+void App::listClients()
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ QString reply(ac->client()->host());
|
|
|
+ reply.append(":" + QString::number(ac->client()->port()) + "\n");
|
|
|
+ print(reply.toAscii().data());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::timerEvent(QTimerEvent *e)
|
|
|
+{
|
|
|
+ if(e->timerId() == myClientsFrameTimerID)
|
|
|
+ {
|
|
|
+ ActiveClient *ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->run();
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void App::broadcast(const QString &msg)
|
|
|
+{
|
|
|
+ say(msg);
|
|
|
+}
|
|
|
+
|
|
|
+void App::cleanup()
|
|
|
+{
|
|
|
+ ActiveClient* ac;
|
|
|
+ foreach(ac, myClients)
|
|
|
+ {
|
|
|
+ ac->client()->disconnect();
|
|
|
+ delete ac;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+App::~App()
|
|
|
+{
|
|
|
+ cleanup();
|
|
|
+ delete myServer;
|
|
|
+}
|
|
|
+
|
|
|
+//========================================================================
|
|
|
+
|
|
|
+App::ActiveClient::ActiveClient(App *app):
|
|
|
+ myApp(app),
|
|
|
+ myClient(new Client(app)),
|
|
|
+ myQueryThread(new ServerQueryThread())
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+App::ActiveClient::~ActiveClient()
|
|
|
+{
|
|
|
+ delete myClient;
|
|
|
+
|
|
|
+ if(queryThread()->isRunning())
|
|
|
+ queryThread()->stopQueryThread();
|
|
|
+ while(queryThread()->isRunning());
|
|
|
+
|
|
|
+ delete myQueryThread;
|
|
|
+}
|
|
|
+
|
|
|
+Client* App::ActiveClient::client()
|
|
|
+{
|
|
|
+ return myClient;
|
|
|
+}
|
|
|
+
|
|
|
+ServerQueryThread* App::ActiveClient::queryThread()
|
|
|
+{
|
|
|
+ return myQueryThread;
|
|
|
+}
|
|
|
+
|
|
|
+void App::ActiveClient::setAddress(const QHostAddress &address, quint16 port)
|
|
|
+{
|
|
|
+ queryThread()->setAddress(address, port);
|
|
|
+}
|
|
|
+
|
|
|
+void App::ActiveClient::run()
|
|
|
+{
|
|
|
+ if(client()->state() == Client::DisconnectedState)
|
|
|
+ {
|
|
|
+ if(!queryThread()->isRunning())
|
|
|
+ {
|
|
|
+ queryThread()->startQueryThread();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int playerCount = queryThread()->playerCount();
|
|
|
+ if(playerCount != 255 && playerCount > 0 && myDisconnectTime.elapsed() > 10000)
|
|
|
+ {
|
|
|
+ myApp->print("Players online on server " + queryThread()->serverAddress().toString() + ":" + QString::number(queryThread()->serverPort()) + ". Joining...\n");
|
|
|
+ client()->connect(queryThread()->serverAddress().toString().toAscii(), queryThread()->serverPort());
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(client()->state() == Client::ConnectedState)
|
|
|
+ {
|
|
|
+ int playerCount = queryThread()->playerCount();
|
|
|
+ if(playerCount == 1 && client()->isOnServer())
|
|
|
+ {
|
|
|
+ myApp->print("I was left alone on " + queryThread()->serverAddress().toString() + ":" + QString::number(queryThread()->serverPort()) + ". Leaving...\n");
|
|
|
+ client()->disconnect();
|
|
|
+ myDisconnectTime.restart();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ client()->run();
|
|
|
+}
|