| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 | /*GNU General Public License version 3 noticeCopyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.This program is free software: you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program.  If not, see < http://www.gnu.org/licenses/ >.*/#include "SshClient.h"#include "App.h"#include "Settings.h"#include "ActiveClient.h"#include <QProcess>#include <QRegExp>#include <QDateTime>#include <QTimerEvent>#include <QStringList>#include <Pinger.h>#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.toLatin1());  myProcess->waitForBytesWritten();}// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static const char *umlauts[4][2] = {  {"ü", "u"},  {"ä", "a"},  {"ö", "o"},  {"ß", "ss"}};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;    // Parse umlauts    QString msg = a.cap(6);    for (int i = 0; i < 4; ++i) {      msg.replace(umlauts[i][0], umlauts[i][1]);    }    myApp->broadcast(a.cap(3) + " " + a.cap(5) + " - " + a.cap(4) + " : " + msg, &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;  }  /* REQ_ASSIGNMENTS - Central is requesting the server list I have */  if(command == "REQ_ASSIGNMENTS")  {    myApp->print("Server list requested... Sending it now!\n");    foreach (ActiveClient* ac, myApp->clients()) {      Pinger* pinger = new Pinger(ac->address(), ac->port(), this);      connect(pinger, SIGNAL(finished(QHostAddress,quint16,int)), SLOT(assignmentsReply(QHostAddress,quint16,int)));      pinger->ping();    }    return;  }  /* REQ_PING  - Requests an given server ping */  if(command == "REQ_PING")  {    QStringList splitCmd = commandData.split(":");    if(splitCmd.size() < 2)    {      write("Malformed REQ_PING command.\n");      return;    }    QString server = splitCmd.at(0);    quint16 port = splitCmd.at(1).toUShort();    Pinger* pinger = new Pinger(QHostAddress(server), port, this);    connect(pinger, SIGNAL(finished(QHostAddress,quint16,int)), SLOT(serverPong(QHostAddress,quint16,int)));    pinger->ping();    return;  }  /* REQ_ASSIGN */  if(command == "REQ_ASSIGN")  {    QRegExp rx("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})");    if(rx.indexIn(commandData) == -1)    {      write("Malformed REQ_ASSIGN request.\n");      return;    }    if(myApp->addClient(rx.cap(1), rx.cap(2).toUShort()))      write("ASSIGN_RE " + rx.cap(1) + ":" + rx.cap(2) + " OK\n");    else      write("ASSIGN_RE " + rx.cap(1) + ":" + rx.cap(2) + " FAILED Client already on my list.\n");    return;  }  /* REQ_UNASSIGN */  if(command == "REQ_UNASSIGN")  {    QRegExp rx("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})");    if(rx.indexIn(commandData) == -1)    {      write("Malformed REQ_UNASSIGN request.\n");      return;    }    if(myApp->removeClient(rx.cap(1), rx.cap(2).toUShort()))      write("UNASSIGN_RE " + rx.cap(1) + ":" + rx.cap(2) + " OK\n");    else      write("UNASSIGN_RE " + rx.cap(1) + ":" + rx.cap(2) + " FAILED Client not found on my list.\n");    return;  }  /* REQ_MAXSERVERS - Reply with the maximum number of servers we can handle */  if(command == "REQ_MAXSERVERS")  {    write("MAXSERVERS_RE " + QString::number(Settings::globalInstance()->maxServers()) + "\n");    return;  }  myApp->print("Unparsed cmd from central: " + time.toString() + " " + command + " " + commandData + "\n");}// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool SshClient::connectToHost(const QString &user, const QString &host, quint16 port){  myProcess->start("ssh", QStringList() << user + "@" + host << "-p " + QString::number(port));  myUsername = user;  myHostname = host;  myPort = port;  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, myPort);}// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -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;  }}// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void SshClient::serverPong(const QHostAddress &host, quint16 port, int ms){  write("PING_RE " + host.toString() + ":" + QString::number(port) + "," + QString::number(ms) + "\n");  sender()->deleteLater();}// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void SshClient::assignmentsReply(const QHostAddress &host, quint16 port, int ms){  QString fullAddress = host.toString() + ":" + QString::number(port);  write("ASSIGNMENTS_RE " + host.toString() + ":" + QString::number(port) + "," + QString::number(ms) + "\n");  sender()->deleteLater();}
 |