123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- /*
- GNU General Public License version 3 notice
- Copyright (C) 2012 Mihawk <luiz@netdome.biz>. 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 "SshClient.h"
- #include "App.h"
- #include "Settings.h"
- #include <QProcess>
- #include <QRegExp>
- #include <QDateTime>
- #include <QTimerEvent>
- #include <QStringList>
- #include <QDebug>
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SshClient::SshClient(App* app, QObject *parent) :
- QObject(parent),
- myApp(app),
- myProcess(new QProcess(this)),
- myCommandRegex(new QRegExp("^([0-9]{4}-[0-9]{2}-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2})\\s(\\+[0-9]{4}):\\s(.+)$")),
- myConnectedFlag(false),
- myConnectionTimerID(0),
- myPingTimerID(0),
- myPongTimerID(0)
- {
- connect(myProcess, SIGNAL(readyRead()), SLOT(read()));
- connect(myProcess, SIGNAL(finished(int)), SLOT(exited(int)));
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- SshClient::~SshClient()
- {
- delete myCommandRegex;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool SshClient::isConnected() const
- {
- return myConnectedFlag;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::read()
- {
- QString data(myProcess->readAll().trimmed());
- if(data.isEmpty())
- return;
- QStringList lines = data.split("\n", QString::SkipEmptyParts);
- QString line;
- foreach(line, lines)
- {
- line = line.trimmed();
- if(myCommandRegex->indexIn(line) != -1)
- {
- QDateTime time = QDateTime::fromString(myCommandRegex->cap(1), "yyyy-MM-dd HH:mm:ss");
- time = time.addSecs(-myCommandRegex->cap(2).toInt()/100*60*60);
- parse(time,
- myCommandRegex->cap(3).section(' ', 0, 0),
- myCommandRegex->cap(3).section(' ', 1)
- );
- }
- }
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::exited(int)
- {
- reconnect();
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::ping()
- {
- write("PING\n");
- myPingTimerID = startTimer(30000);
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::pong()
- {
- killTimer(myPingTimerID);
- myPingTimerID = -1;
- myPongTimerID = startTimer(30000);
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::write(const QString &data)
- {
- myProcess->write(data.toAscii());
- myProcess->waitForBytesWritten();
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::parse(const QDateTime &time, const QString &command, const QString &commandData)
- {
- /* JOINED - Another user has just joined central */
- if(command == "JOINED")
- {
- QRegExp a("^User '([a-zA-Z]+)'.+$");
- if(a.indexIn(commandData) != -1)
- {
- if(!myClients.contains(a.capturedTexts().at(1)))
- myClients.push_back(a.capturedTexts().at(1));
- }
- return;
- }
- /* PARTED - Another user has left central */
- if(command == "PARTED")
- {
- QRegExp a("^User '([a-zA-Z]+)'.+$");
- if(a.indexIn(commandData) != -1)
- myClients.removeAll(a.capturedTexts().at(1));
- return;
- }
- /* HELLO - Server acknowledge */
- if(command == "HELLO")
- {
- ping();
- myConnectedFlag = true;
- killTimer(myConnectionTimerID);
- myConnectionTimerID = -1;
- emit connected();
- return;
- }
- /* PING PONG - The connection is still up */
- if(command == "PONG")
- {
- pong();
- return;
- }
- /* SYS - Central generic message */
- if(command == "SYS")
- {
- myApp->print("Central Message: " + commandData + "\n");
- return;
- }
- /* BC - Broadcast order from central */
- if(command == "BC")
- {
- QRegExp a("^([A-Za-z0-9]+) (.+),(.+),(.+),'(.+)','(.+)'$");
- if(a.indexIn(commandData) == -1)
- return;
- if(a.cap(2) == "QDEV" && !Settings::globalInstance()->developerMode()) //developer mode not enabled ignore this message from developers
- return;
- int serverCount, userCount;
- myApp->broadcast(a.cap(3) + " " + a.cap(5) + " - " + a.cap(4) + " : " + a.cap(6), &serverCount, &userCount);
- if(userCount)
- write("BC_RE " + a.cap(1) + " Players=" + QString::number(userCount) + ",Servers=" + QString::number(serverCount) + "\n");
- return;
- }
- /* BC_ID - Broadcast reply with the ID of the broadcast you just generated */
- if(command == "BC_ID")
- {
- //BC_ID 4e5fca581569e168c7a86a0a9a91949f for: QDEV,-dev-,qw://123.123
- // qDebug() << commandData;
- QRegExp a("^([A-Za-z0-9]+) for: .+,-(.+)-,qw://(.+) [0-9]+/[0-9]+$");
- if(a.indexIn(commandData) == -1)
- return;
- QString hash = a.cap(1);
- //QString frequency = a.cap(2);
- QString server = a.cap(3);
- myApp->setReplyHashAndWaitForReply(server, hash);
- return;
- }
- /* BC_RE - Broadcast reply to increment the counters */
- if(command == "BC_RE")
- {
- QRegExp a("^([A-Za-z0-9]+) (Users|Players)=([0-9]+),(Channels|Servers)=([0-9]+)$");
- if(a.indexIn(commandData) == -1)
- return;
- QString hash = a.cap(1);
- QString userType = a.cap(2);
- QString channelType = a.cap(4);
- int userCount = 0;
- int channelCount = 0;
- int playerCount = 0;
- int serverCount = 0;
- if(userType == "Users")
- {
- userCount = a.cap(3).toInt();
- playerCount = 0;
- }
- else if(userType == "Players")
- {
- playerCount = a.cap(3).toInt();
- userCount = 0;
- }
- if(channelType == "Channels")
- {
- channelCount = a.cap(5).toInt();
- serverCount = 0;
- }
- else if(channelType == "Servers")
- {
- serverCount = a.cap(5).toInt();
- channelCount = 0;
- }
- // qDebug() << commandData;
- myApp->incrementReplyCounters(hash, userCount, channelCount, playerCount, serverCount);
- return;
- }
- /* DNS_RE - Reply from REQ_DNS, returns the HostName for the requested ip:port pair */
- if(command == "DNS_RE")
- {
- QRegExp a("^([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+:[0-9]+) ([0-9A-Za-z][A-Za-z0-9\\-\\.]+)$");
- if(a.indexIn(commandData) == -1)
- return;
- myApp->setServerHostName(a.cap(1), a.cap(2));
- return;
- }
- /* COMMANDS - List of commands allowed for me */
- if(command == "COMMANDS")
- {
- myCommands = commandData.split(", ", QString::SkipEmptyParts);
- return;
- }
- /* ROLES - List of roles I exert */
- if(command == "ROLES")
- {
- myRoles = commandData.split(", ", QString::SkipEmptyParts);
- return;
- }
- myApp->print("Unparsed cmd from central: " + time.toString() + " " + command + " " + commandData + "\n");
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- bool SshClient::connectToHost(const QString &user, const QString &host)
- {
- myProcess->start("ssh", QStringList() << user + "@" + host);
- myUsername = user;
- myHostname = host;
- bool r = myProcess->waitForStarted();
- if(!r)
- return false;
- else
- {
- myConnectionTimerID = startTimer(60000*3); // up to 3 minutes of waiting
- return true;
- }
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::reconnect()
- {
- if(!myUsername.isEmpty() && !myHostname.isEmpty())
- connectToHost(myUsername, myHostname);
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::disconnectFromHost()
- {
- killTimer(myConnectionTimerID);
- killTimer(myPingTimerID);
- killTimer(myPongTimerID);
- myConnectionTimerID = myPingTimerID = myPongTimerID = -1;
- myProcess->terminate();
- myProcess->waitForFinished();
- myConnectedFlag = false;
- }
- // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- void SshClient::timerEvent(QTimerEvent *e)
- {
- if(e->timerId() == myConnectionTimerID)
- {
- myApp->print("Timedout while trying to connect to central...\n");
- disconnectFromHost();
- emit error(ConnectionTimedOutError);
- return;
- }
- if(e->timerId() == myPingTimerID)
- {
- myApp->print("Ping with no pong! Disconnecting from central...\n");
- disconnectFromHost();
- emit error(ConnectionTimedOutError);
- return;
- }
- if(e->timerId() == myPongTimerID)
- {
- killTimer(myPongTimerID);
- myPongTimerID = -1;
- ping();
- return;
- }
- }
|