Browse Source

Added new commands:
-REQ_ASSINGMENTS
-REQ_PING
-REQ_ASSIGN
-REQ_UNASSIGN

Mihawk 12 years ago
parent
commit
e4dcb883da
13 changed files with 351 additions and 388 deletions
  1. 6 0
      ActiveClient.cpp
  2. 7 0
      ActiveClient.h
  3. 23 257
      App.cpp
  4. 23 78
      App.h
  5. 46 0
      Pinger.cpp
  6. 71 0
      Pinger.h
  7. 23 33
      ServerQuery.cpp
  8. 12 13
      ServerQuery.h
  9. 7 0
      Settings.cpp
  10. 2 1
      Settings.h
  11. 103 3
      SshClient.cpp
  12. 24 1
      SshClient.h
  13. 4 2
      cimsqwbot.pro

+ 6 - 0
ActiveClient.cpp

@@ -187,3 +187,9 @@ const QString& ActiveClient::hostName() const
 {
   return myHostName;
 }
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int ActiveClient::ping() const
+{
+  return myQuery->ping();
+}

+ 7 - 0
ActiveClient.h

@@ -138,6 +138,13 @@ public:
   */
   const QString&    hostName() const;
 
+  /**
+    Returns the server ping
+
+    @return The ping
+  */
+  int               ping() const;
+
 private slots:
   /**
     Called when a query gathering server information has just finished successfully.

+ 23 - 257
App.cpp

@@ -77,7 +77,7 @@ App::App(int &argc, char **argv) :
 
   print("Connecting to central...\n");
   connect(mySshClient, SIGNAL(connected()), SLOT(onCentralConnection()));
-  mySshClient->connectToHost(Settings::globalInstance()->sshUserName(), Settings::globalInstance()->sshHostName());
+  mySshClient->connectToHost(Settings::globalInstance()->sshUserName(), Settings::globalInstance()->sshHostName(), Settings::globalInstance()->sshPort());
 
   Settings::globalInstance()->save();
 }
@@ -124,33 +124,6 @@ bool App::parseCommandLine()
   return true;
 }
 
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-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");
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void App::onDisconnection()
-{
-  mySocketConnectedFlag = false;
-  mySocket->deleteLater();
-}
-
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 void App::loadServerList()
 {
@@ -159,6 +132,11 @@ void App::loadServerList()
 
   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;
+    }
     ActiveClient* ac = new ActiveClient(this, sv.supportsSendPrivate);
     ac->setAddress(QHostAddress(sv.address), sv.port);
     ac->client()->setQuakeFolder(Settings::globalInstance()->quakeFolder().toAscii().data());
@@ -166,7 +144,6 @@ void App::loadServerList()
     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);
   }
 }
@@ -188,148 +165,6 @@ void App::saveServerList()
   Settings::globalInstance()->setServerList(list);
 }
 
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void App::parseAddClientCommand(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::parseRemoveClientCommand(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 == "add")
-    {
-      parseAddClientCommand(args);
-      return;
-    }
-
-    if(cmd == "remove")
-    {
-      parseRemoveClientCommand(args);
-      return;
-    }
-
-    if(cmd == "say")
-    {
-      broadcast(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;
-    }
-  }
-}
-
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 const QStringList& App::lastMessages() const
 {
@@ -337,107 +172,45 @@ const QStringList& App::lastMessages() const
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-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("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)
+void App::print(const QString &msg)
 {
-  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)
+  printf("%s", msg.toAscii().data());
+  if(mySocketConnectedFlag)
   {
-    ac->client()->setColor(bottom, top);
+    mySocket->write(msg.toAscii());
+    mySocket->waitForBytesWritten();
   }
-  print("All clients colors have changed.\n");
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void App::setPing(const QString &args)
+bool App::hasSendPrivateSupport(const QString &serverAddress) const
 {
   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());
+    if(ac->serverAddressString() == serverAddress)
+      return ac->client()->supportsSendPrivate();
   }
-  print("All clients nicks have changed.\n");
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-bool App::checkPassword(const QString &password)
-{
-  if(QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md4).toHex().toLower() == "bf4df9f74d05c50ea00492224fb02854")
-    return false; //telnet adm disabled!!
   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)
+bool App::addClient(const QString &host, quint16 port, bool sendPrivateSupport)
 {
   ActiveClient* ac;
-  QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
+  QHostAddress ha(host);
 
   foreach(ac, myClients)
   {
     if(QString(ac->client()->host()) == ha.toString() && ac->client()->port() == port)
     {
       print("That client is already on the list.\n");
-      return;
+      return false;
     }
   }
 
-  ac = new ActiveClient(this, this);
+  ac = new ActiveClient(this, sendPrivateSupport, this);
   ac->setAddress(ha, port);
   ac->client()->setQuakeFolder(Settings::globalInstance()->quakeFolder().toAscii().data());
   ac->client()->setName(Settings::globalInstance()->botName().toAscii().data());
@@ -449,10 +222,12 @@ void App::addClient(const QString &host, quint16 port)
   saveServerList();
 
   print("Client added to watch list.\n");
+
+  return true;
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void App::removeClient(const QString &host, quint16 port)
+bool App::removeClient(const QString &host, quint16 port)
 {
   ActiveClient* ac;
   QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
@@ -468,20 +243,11 @@ void App::removeClient(const QString &host, quint16 port)
       saveServerList();
 
       print("Client removed from watch list.\n");
-      return;
+      return true;
     }
   }
   print("Client not found on the list.\n");
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-void App::listClients()
-{
-  ActiveClient* ac;
-  foreach(ac, myClients)
-  {
-    print(QString(ac->serverAddressString() + '\n'));
-  }
+  return false;
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

+ 23 - 78
App.h

@@ -138,6 +138,29 @@ public:
   */
   QString               serverHostName(const QString& serverAddress) const;
 
+  /**
+    Returns whether a given server supports the s-p command.
+
+    @param serverAddress The server we need this info
+    @return True if it supports, false otherwise
+  */
+  bool                  hasSendPrivateSupport(const QString &serverAddress) const;
+
+  /**
+    Checks if the password specified by the user is correct.
+
+    @param  password  The password
+    @return  True if its correct, false otherwise
+  */
+
+  bool                  addClient(const QString& host, quint16 port, bool sendPrivateSupport = false);
+  /**
+    Checks if the password specified by the user is correct.
+
+    @param  password  The password
+    @return  True if its correct, false otherwise
+  */
+  bool                  removeClient(const QString& host, quint16 port);
 protected:
   /**
     App's MainLoop is here.
@@ -195,22 +218,6 @@ private:
   */
   bool                  parseCommandLine();
 
-  /**
-    Checks if the password specified by the user is correct.
-
-    @param  password  The password
-    @return  True if its correct, false otherwise
-  */
-
-  void                  addClient(const QString& host, quint16 port);
-  /**
-    Checks if the password specified by the user is correct.
-
-    @param  password  The password
-    @return  True if its correct, false otherwise
-  */
-  void                  removeClient(const QString& host, quint16 port);
-
   /**
     Requests the hostnames of all servers being monitored.
     Those hostnames are obtained from central, that has a smart
@@ -218,69 +225,7 @@ private:
   */
   void                  requestCachedHostNames();
 
-  /**
-    The following functions sets the Team, Color, Nick and Ping
-    for all the bots on all servers that we are monitoring.
-  */
-  void                  setTeam(const QString& team);
-  void                  setColor(const QString& args);
-  void                  setNick(const QString& args);
-  void                  setPing(const QString& args);
-
-  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-  // The following functions are used to create the TelNet administration
-  // interface
-
-  /**
-    Checks if the password specified by the user is correct.
-
-    @param  password  The password
-    @return  True if its correct, false otherwise
-  */
-  bool                  checkPassword(const QString& password);
-
-  /**
-    Parses add client command coming from the user.
-
-    @param  args  The command arguments unparsed
-  */
-  void                  parseAddClientCommand(const QString& args);
-
-  /**
-    Parses remove client command coming from the user.
-
-    @param  args  The command arguments unparsed
-  */
-  void                  parseRemoveClientCommand(const QString& args);
-
-  /**
-    Lists all the servers we are monitoring.
-  */
-  void                  listClients();
-
-  /**
-    Displays a list of commands available to the user.
-  */
-  void                  help();
-
-
 private slots:
-  /**
-    Called everytime data arrives coming from the TelNet user.
-  */
-  void                  onDataArrival();
-
-  /**
-    Called everytime an user is trying to connect to the TelNet interface.
-  */
-  void                  onNewConnection();
-
-  /**
-    Called everytime an user is disconnected from the TelNet interface.
-  */
-  void                  onDisconnection();
-
-  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   /**
     Called everytime we are fully connected to central.
   */

+ 46 - 0
Pinger.cpp

@@ -0,0 +1,46 @@
+#include "Pinger.h"
+#include <QUdpSocket>
+#include <QTime>
+#include <QTimerEvent>
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Pinger::Pinger(const QHostAddress &host, quint16 port, QObject *parent) :
+  QObject(parent),
+  mySocket(new QUdpSocket(this)),
+  myHost(host),
+  myPort(port),
+  myTimerID(-1)
+{
+  mySocket->connectToHost(host, port);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Pinger::ping()
+{
+  mySocket->write("\xff\xff\xff\xffk\n");
+  mySocket->waitForBytesWritten(500);
+  myTime.start();
+  myTimerID = startTimer(1000);
+  connect(mySocket, SIGNAL(readyRead()), SLOT(pong()));
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Pinger::pong()
+{
+  emit finished(myHost, myPort, myTime.elapsed());
+  disconnect(mySocket, SIGNAL(readyRead()), this, SLOT(pong()));
+  killTimer(myTimerID);
+  myTimerID = -1;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void Pinger::timerEvent(QTimerEvent *e)
+{
+  if(e->timerId() != myTimerID)
+    return;
+
+  emit finished(myHost, myPort, myTime.elapsed());
+  disconnect(mySocket, SIGNAL(readyRead()), this, SLOT(pong()));
+  killTimer(myTimerID);
+  myTimerID = -1;
+}

+ 71 - 0
Pinger.h

@@ -0,0 +1,71 @@
+#ifndef PINGER_H
+#define PINGER_H
+
+#include <QObject>
+#include <QTime>
+#include <QHostAddress>
+
+class QUdpSocket;
+
+/**
+  This class is used to ping any QuakeWorld server
+
+  @author   Mihawk <luiz@netdome.biz>
+  @file     Pinger.h
+  @date     27/10/2012
+*/
+class Pinger : public QObject
+{
+  Q_OBJECT
+public:
+  /**
+    Constructor.
+
+    @param host The host we are going to ping
+    @param port The host's port
+  */
+  Pinger(const QHostAddress& host, quint16 port, QObject *parent = 0);
+
+  /**
+    Starts the pinging process
+  */
+  void ping();
+
+signals:
+  /**
+    Called when we received an answer from the server
+    sending the time diff between the call to ping() and now
+    as the ping.
+
+    @param host The host we are pinging
+    @param port The host's port
+    @param ping The ping we got from this server
+  */
+  void finished(const QHostAddress& host, quint16 port, int ping);
+
+protected:
+  /**
+    Called when the pinging process times out
+    this timeout is set to 1000ms.
+
+    @param e The timer event
+  */
+  void timerEvent(QTimerEvent *e);
+
+private slots:
+  /**
+    Called when we received an answer from the server
+    sending the time diff between the call to ping() and now
+    as the ping. This function will emit the finished signal.
+  */
+  void pong();
+
+private:
+  QUdpSocket*  mySocket; // Our socket
+  QTime        myTime;   // Time used for calculating the ping
+  QHostAddress myHost;   // Host to be pinged
+  quint16      myPort;   // Host's port
+  int          myTimerID;// Timer used for timing out an ping request
+};
+
+#endif // PINGER_H

+ 23 - 33
ServerQuery.cpp

@@ -23,13 +23,15 @@ along with this program.  If not, see < http://www.gnu.org/licenses/ >.
 #include <QStringList>
 #include <QRegExp>
 #include <QTime>
+#include <QTimer>
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 ServerQuery::ServerQuery(QObject *parent) :
   QObject(parent),
   mySocket(new QUdpSocket(this)),
   myPort(27500),
-  myActiveFlag(false)
+  myActiveFlag(false),
+  myCurrentPingIndex(0)
 {
   connect(mySocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
 }
@@ -48,7 +50,7 @@ void ServerQuery::run()
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-bool ServerQuery::query(bool pingServer)
+bool ServerQuery::query()
 {
   myActiveFlag = true;
 
@@ -76,18 +78,7 @@ bool ServerQuery::query(bool pingServer)
     myActiveFlag = false;
     return false;
   }
-  if(!mySocket->waitForBytesWritten(300))
-  {
-    emit error(SendError);
-    myActiveFlag = false;
-    return false;
-  }
-
-  if(pingServer)
-    myPing = ping(10); //avg ping
-  else
-    myPing = 0;
-
+  myPingTime.restart();
   return true;
 }
 
@@ -138,6 +129,8 @@ void ServerQuery::parseServerInfo()
     return;
   }
 
+  myPings[myCurrentPingIndex++ & 7] = myPingTime.elapsed();
+
   if(!serverData.startsWith("\xff\xff\xff\xffn"))
   {
     emit error(InvalidResponseError);
@@ -242,30 +235,27 @@ void ServerQuery::socketError(QAbstractSocket::SocketError)
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-int ServerQuery::ping(int count, int timeout)
+int ServerQuery::ping() const
 {
-  int pong = 0;
-  QTime timer;
+  quint16 avgPing = 0;
+  quint8  divisor;
+  if(!myCurrentPingIndex)
+    return 0xffff;
 
-  timer.start();
-  for(int i = 0; i < count; i++)
+  if(myCurrentPingIndex < 8)
   {
-    mySocket->write("\xff\xff\xff\xffk\n");
-    timer.restart();
-    if(!mySocket->waitForReadyRead(timeout))
-    {
-      pong += 999;
-    }
-    else
-    {
-      if(mySocket->readAll() == "l")
-        pong += timer.elapsed();
-      else
-        pong += 999;
-    }
+    for(int i = 0; i < myCurrentPingIndex; ++i)
+      avgPing += myPings[i];
+    divisor = myCurrentPingIndex;
+  }
+  else
+  {
+    for(int i = 0; i < 8; ++i)
+      avgPing += myPings[i];
+    divisor = 8;
   }
 
-  return pong/count;
+  return avgPing / divisor;
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

+ 12 - 13
ServerQuery.h

@@ -22,8 +22,9 @@ along with this program.  If not, see < http://www.gnu.org/licenses/ >.
 
 #include <QObject>
 #include <QHostAddress>
-
+#include <QTime>
 class QUdpSocket;
+class QTimer;
 
 // Player info struct
 struct Player
@@ -98,13 +99,19 @@ public:
     @param pingServer Whether we should gather ping information too (this blocks the query)
     @return True on success, false otherwise
   */
-  bool          query(bool pingServer = false);
+  bool          query();
 
   /**
     Must be called when a query is active.
   */
   void          run();
 
+
+  /**
+    Returns the server ping based on an 8 values average
+  */
+  int           ping() const;
+
   /**
     Returns whether a query is active or not.
 
@@ -173,17 +180,9 @@ private:
   quint16       myPing;        // The ping from us to the server
   bool          myIsProxyFlag; // Indicates whether this server is a proxy
   bool          myActiveFlag;  // Indicates whether we are in the middle of a query process
-
-  /**
-    Pings the server we are gathering information from.
-    Note: This function blocks code until its finished.
-    FIXME: Make this function non-blocking too.
-
-    @param  count   Number of times we should ping the server
-    @param  timeout How long to wait for a server reply
-    @return The average ping
-  */
-  int           ping(int count = 3, int timeout = 1000);
+  quint16       myPings[8];    // For average ping calculation
+  quint8        myCurrentPingIndex; // For filling the average ping list
+  QTime         myPingTime;    // Does ping calculation for the current request
 
   // Tables used for namefun conversion
   static char   ourReadableCharsTable[256];

+ 7 - 0
Settings.cpp

@@ -198,6 +198,12 @@ QString Settings::sshUserName() const
   return ourSettings->value("sshUserName", "stomp").toString();
 }
 
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+quint16 Settings::sshPort() const
+{
+  return ourSettings->value("sshPort", 22).toUInt();
+}
+
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 int Settings::refreshHostNamesHour() const
 {
@@ -228,6 +234,7 @@ void Settings::save()
   ourSettings->setValue("developerMode", developerMode());
   ourSettings->setValue("sshHostName", sshHostName());
   ourSettings->setValue("sshUserName", sshUserName());
+  ourSettings->setValue("sshPort", sshPort());
   ourSettings->setValue("refreshHostNamesHour", refreshHostNamesHour());
   ourSettings->setValue("maxServers", maxServers());
 

+ 2 - 1
Settings.h

@@ -82,7 +82,8 @@ public:
   int               timeToWaitForCountReply() const; // Time to wait from a reply from central about the amount of people that the message we broadcasted has reached
   bool              developerMode() const; // Developer mode
   QString           sshUserName() const; // Username on central
-  QString           sshHostName() const; // HostName of central
+  QString           sshHostName() const; // Central's HostName
+  quint16           sshPort() const; // Central's port
   int               refreshHostNamesHour() const; // An specific hour that the bot must refresh his hostname list
   int               maxServers() const; // Maximum servers that we can monitor
 

+ 103 - 3
SshClient.cpp

@@ -25,6 +25,7 @@ along with this program.  If not, see < http://www.gnu.org/licenses/ >.
 #include <QDateTime>
 #include <QTimerEvent>
 #include <QStringList>
+#include <Pinger.h>
 #include <QDebug>
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -261,15 +262,97 @@ void SshClient::parse(const QDateTime &time, const QString &command, const QStri
     return;
   }
 
+  /* REQ_ASSIGNMENTS - Central is requesting the server list I have */
+  if(command == "REQ_ASSIGNMENTS")
+  {
+    myApp->print("Server list requested... Sending it now!\n");
+    // TODO: Maybe fetching server data directly from ActiveClients list is a better idea
+    Settings::ServerList serverList = Settings::globalInstance()->serverList();
+    Settings::Server server;
+    foreach(server, serverList)
+    {
+      // Let the reply be send after the pinging is done
+      Pinger* pinger = new Pinger(QHostAddress(server.address), server.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}),?(true|false)?");
+    if(rx.indexIn(commandData) == -1)
+    {
+      write("Malformed REQ_ASSIGN request.\n");
+      return;
+    }
+
+    bool spSupport = false;
+    if(rx.cap(3).size())
+    {
+      if(rx.cap(3) == "true")
+        spSupport = true;
+    }
+    if(myApp->addClient(rx.cap(1), rx.cap(2).toUShort(), spSupport))
+      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)
+bool SshClient::connectToHost(const QString &user, const QString &host, quint16 port)
 {
-  myProcess->start("ssh", QStringList() << user + "@" + host);
+  myProcess->start("ssh", QStringList() << user + "@" + host << "-p " + QString::number(port));
   myUsername = user;
   myHostname = host;
+  myPort = port;
 
   bool r = myProcess->waitForStarted();
   if(!r)
@@ -285,7 +368,7 @@ bool SshClient::connectToHost(const QString &user, const QString &host)
 void SshClient::reconnect()
 {
   if(!myUsername.isEmpty() && !myHostname.isEmpty())
-    connectToHost(myUsername, myHostname);
+    connectToHost(myUsername, myHostname, myPort);
 }
 
 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -329,3 +412,20 @@ void SshClient::timerEvent(QTimerEvent *e)
     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) + "," + QVariant(myApp->hasSendPrivateSupport(fullAddress)).toString() + "," + QString::number(ms) + "\n");
+
+  sender()->deleteLater();
+}

+ 24 - 1
SshClient.h

@@ -26,6 +26,7 @@ along with this program.  If not, see < http://www.gnu.org/licenses/ >.
 class QProcess;
 class QDateTime;
 class App;
+class QHostAddress;
 
 /**
   SSH Client exclusively for connecting to QWNET's central.
@@ -62,7 +63,7 @@ public:
     @param user Username
     @param host Hostname
   */
-  bool connectToHost(const QString& user, const QString &host);
+  bool connectToHost(const QString& user, const QString &host, quint16 port);
 
   /**
     Returns whether we are connected right now.
@@ -117,6 +118,27 @@ private slots:
   */
   void exited(int exitCode);
 
+  /**
+    Called when a simple ping request to an QW server has finished
+
+    @param host The server's address
+    @param port The server's port
+    @param ms   The server's ping
+  */
+  void serverPong(const QHostAddress& host, quint16 port, int ms);
+
+  /**
+    Called when a simple ping request to an QW server has finished.
+    This function reply to the REQ_ASSIGNMENTS request from central.
+    The reason this is being done here is because central requests the ping
+    in the answer.
+
+    @param host The server's address
+    @param port The server's port
+    @param ms   The server's ping
+  */
+  void assignmentsReply(const QHostAddress& host, quint16 port, int ms);
+
 private:
   App*        myApp;               // The app
   QProcess*   myProcess;           // The ssh process
@@ -128,6 +150,7 @@ private:
   QStringList myClients;           // List of other clients connected to central
   QString     myUsername;          // My username on central
   QString     myHostname;          // Central's hostname
+  quint16     myPort;              // Central's port number
   QStringList myRoles;             // My roles on central
   QStringList myCommands;          // The list of commands that I can issue
 

+ 4 - 2
cimsqwbot.pro

@@ -9,7 +9,8 @@ SOURCES += main.cpp \
     ServerQuery.cpp \
     SshClient.cpp \
     ActiveClient.cpp \
-    Settings.cpp
+    Settings.cpp \
+    Pinger.cpp
 
 HEADERS += \
     Client.h \
@@ -17,7 +18,8 @@ HEADERS += \
     ServerQuery.h \
     SshClient.h \
     ActiveClient.h \
-    Settings.h
+    Settings.h \
+    Pinger.h