#include "App.h" #include "Client.h" #include #include #include #include #include #include #include #include #include #include #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); myQWNETSshClient->connectToHost("stomp", "b4r.org"); } 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: ?\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, int *serverCount, int *userCount) { ActiveClient* ac; *serverCount = 0; *userCount = 0; foreach(ac, myClients) { if(ac->client()->state() == Client::ConnectedState) { ac->client()->say(msg); *userCount += ac->queryThread()->playerCount() - 1; (*serverCount)++; } } print("Say command sent to all clients\n"); } void App::say(const QString &msg) { ActiveClient* ac; foreach(ac, myClients) { if(ac->client()->state() == Client::ConnectedState) { 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::activeClientsReplyCounters(int *serverCount, int *playerCount) { ActiveClient* ac; foreach(ac, myClients) { if(ac->client()->state() == Client::ConnectedState) { int pc = ac->queryThread()->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(); } return; } } void App::requestBroadcast(const QString &type, const QString &user, const QString &server, const QString &message) { //REQ_BC QDEV,-dev-,qw://123.123,'testuser','test, with '',``twists''$ hehe' qDebug() << "REQ_BC QDEV,-" + type + "-,qw://" + server + ",'" + user + "','" + message + "'"; myQWNETSshClient->write("REQ_BC QDEV,-" + type + "-,qw://" + server + ",'" + user + "','" + message + "'\n"); } void App::broadcast(const QString &msg) { say(msg); } void App::broadcast(const QString &msg, int *serverCount, int *userCount) { qDebug() << "Broadcasting" << msg; say(msg, serverCount, userCount); } void App::cleanup() { ActiveClient* ac; foreach(ac, myClients) { ac->client()->disconnect(); delete ac; } } App::~App() { cleanup(); delete myServer; } void App::setReplyHash(const QString &serverAddress, const QString &hash) { ActiveClient* ac; foreach(ac, myClients) { if(serverAddress == ac->serverAddressString()) { ac->setReplyHash(hash); return; } } } void App::incrementReplyCounters(const QString &hash, int userCount, int serverCount) { ActiveClient* ac; foreach(ac, myClients) { if(ac->replyHash() == hash) { ac->incrementReplyCounters(userCount, serverCount); return; } } } //======================================================================== App::ActiveClient::ActiveClient(App *app): myApp(app), myClient(new Client(app)), myQueryThread(new ServerQueryThread()), myBroadcastReplyTimer(new QTimer()), myUniqueUserCount(0), myUniqueServerCount(0) { } App::ActiveClient::~ActiveClient() { delete myClient; if(queryThread()->isRunning()) queryThread()->stopQueryThread(); while(queryThread()->isRunning()); delete myQueryThread; delete myBroadcastReplyTimer; } 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 the client is disconnect check if there is at least one player connected to the server */ 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 the client is connected and left alone on the server, disconnect yourself */ 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; } } /* Say the broadcast count */ if(!myBroadcastReplyTimer->isActive() && myReplyTimerWasActive) { myApp->activeClientsReplyCounters(&myUniqueServerCount, &myUniqueUserCount); //add my internal counter to the list client()->say("::cims:: Sent to " + QString::number(myUniqueServerCount) + " channels, " + QString::number(myUniqueUserCount) + " unique users."); myUniqueServerCount = myUniqueUserCount = 0; } myReplyTimerWasActive = myBroadcastReplyTimer->isActive(); client()->run(); } const QString App::ActiveClient::serverAddressString() { return QString(queryThread()->serverAddress().toString() + ':' + QString::number(queryThread()->serverPort())); } void App::ActiveClient::setReplyHash(const QString &hash) { myReplyHash = hash; /* Wait for 5 seconds */ myBroadcastReplyTimer->setSingleShot(true); myBroadcastReplyTimer->start(5000); } const QString& App::ActiveClient::replyHash() const { return myReplyHash; } bool App::ActiveClient::isWaitingReply() const { return myBroadcastReplyTimer->isActive(); } void App::ActiveClient::incrementReplyCounters(int userCount, int serverCount) { if(!myBroadcastReplyTimer->isActive()) return; myUniqueUserCount += userCount; myUniqueServerCount += serverCount; }