SshClient.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. GNU General Public License version 3 notice
  3. Copyright (C) 2012 Mihawk <luiz@netdome.biz>. All rights reserved.
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see < http://www.gnu.org/licenses/ >.
  14. */
  15. #include "SshClient.h"
  16. #include "App.h"
  17. #include "Settings.h"
  18. #include <QProcess>
  19. #include <QRegExp>
  20. #include <QDateTime>
  21. #include <QTimerEvent>
  22. #include <QStringList>
  23. #include <QDebug>
  24. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  25. SshClient::SshClient(App* app, QObject *parent) :
  26. QObject(parent),
  27. myApp(app),
  28. myProcess(new QProcess(this)),
  29. 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(.+)$")),
  30. myConnectedFlag(false),
  31. myConnectionTimerID(0),
  32. myPingTimerID(0),
  33. myPongTimerID(0),
  34. myClients(new QStringList())
  35. {
  36. connect(myProcess, SIGNAL(readyRead()), SLOT(read()));
  37. connect(myProcess, SIGNAL(finished(int)), SLOT(exited(int)));
  38. }
  39. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  40. SshClient::~SshClient()
  41. {
  42. delete myCommandRegex;
  43. delete myClients;
  44. }
  45. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  46. bool SshClient::isConnected() const
  47. {
  48. return myConnectedFlag;
  49. }
  50. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  51. void SshClient::read()
  52. {
  53. QString data(myProcess->readAll().trimmed());
  54. if(data.isEmpty())
  55. return;
  56. QStringList lines = data.split("\n", QString::SkipEmptyParts);
  57. QString line;
  58. foreach(line, lines)
  59. {
  60. line = line.trimmed();
  61. if(myCommandRegex->indexIn(line) != -1)
  62. {
  63. QDateTime time = QDateTime::fromString(myCommandRegex->cap(1), "yyyy-MM-dd HH:mm:ss");
  64. time = time.addSecs(-myCommandRegex->cap(2).toInt()/100*60*60);
  65. parse(time,
  66. myCommandRegex->cap(3).section(' ', 0, 0),
  67. myCommandRegex->cap(3).section(' ', 1)
  68. );
  69. }
  70. }
  71. }
  72. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  73. void SshClient::exited(int)
  74. {
  75. reconnect();
  76. }
  77. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  78. void SshClient::ping()
  79. {
  80. write("PING\n");
  81. myPingTimerID = startTimer(30000);
  82. }
  83. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  84. void SshClient::pong()
  85. {
  86. killTimer(myPingTimerID);
  87. myPongTimerID = startTimer(30000);
  88. }
  89. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  90. void SshClient::write(const QString &data)
  91. {
  92. myProcess->write(data.toAscii());
  93. myProcess->waitForBytesWritten();
  94. }
  95. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  96. void SshClient::parse(const QDateTime &time, const QString &command, const QString &commandData)
  97. {
  98. /* JOINED - Another user has just joined central */
  99. if(command == "JOINED")
  100. {
  101. QRegExp a("^User '([a-zA-Z]+)'.+$");
  102. if(a.indexIn(commandData) != -1)
  103. {
  104. if(!myClients->contains(a.capturedTexts().at(1)))
  105. myClients->push_back(a.capturedTexts().at(1));
  106. }
  107. return;
  108. }
  109. /* PARTED - Another user has left central */
  110. if(command == "PARTED")
  111. {
  112. QRegExp a("^User '([a-zA-Z]+)'.+$");
  113. if(a.indexIn(commandData) != -1)
  114. myClients->removeAll(a.capturedTexts().at(1));
  115. return;
  116. }
  117. /* HELLO - Server acknowledge */
  118. if(command == "HELLO")
  119. {
  120. ping();
  121. myConnectedFlag = true;
  122. killTimer(myConnectionTimerID);
  123. emit connected();
  124. return;
  125. }
  126. /* PING PONG - The connection is still up */
  127. if(command == "PONG")
  128. {
  129. pong();
  130. return;
  131. }
  132. /* SYS - Central generic message */
  133. if(command == "SYS")
  134. {
  135. myApp->print("Central Message: " + commandData + "\n");
  136. return;
  137. }
  138. /* BC - Broadcast order from central */
  139. if(command == "BC")
  140. {
  141. QRegExp a("^([A-Za-z0-9]+) (.+),(.+),(.+),'(.+)','(.+)'$");
  142. if(a.indexIn(commandData) == -1)
  143. return;
  144. if(a.cap(2) == "QDEV" && !Settings::globalInstance()->developerMode()) //developer mode not enabled ignore this message from developers
  145. return;
  146. int serverCount, userCount;
  147. myApp->broadcast(a.cap(3) + " " + a.cap(5) + " - " + a.cap(4) + " : " + a.cap(6), &serverCount, &userCount);
  148. if(userCount)
  149. write("BC_RE " + a.cap(1) + " Players=" + QString::number(userCount) + ",Servers=" + QString::number(serverCount) + "\n");
  150. return;
  151. }
  152. /* BC_ID - Broadcast reply with the ID of the broadcast you just generated */
  153. if(command == "BC_ID")
  154. {
  155. //BC_ID 4e5fca581569e168c7a86a0a9a91949f for: QDEV,-dev-,qw://123.123
  156. // qDebug() << commandData;
  157. QRegExp a("^([A-Za-z0-9]+) for: .+,-(.+)-,qw://(.+) [0-9]+/[0-9]+$");
  158. if(a.indexIn(commandData) == -1)
  159. return;
  160. QString hash = a.cap(1);
  161. //QString frequency = a.cap(2);
  162. QString server = a.cap(3);
  163. myApp->setReplyHashAndWaitForReply(server, hash);
  164. return;
  165. }
  166. /* BC_RE - Broadcast reply to increment the counters */
  167. if(command == "BC_RE")
  168. {
  169. QRegExp a("^([A-Za-z0-9]+) (Users|Players)=([0-9]+),(Channels|Servers)=([0-9]+)$");
  170. if(a.indexIn(commandData) == -1)
  171. return;
  172. QString hash = a.cap(1);
  173. QString userType = a.cap(2);
  174. QString channelType = a.cap(4);
  175. int userCount = 0;
  176. int channelCount = 0;
  177. int playerCount = 0;
  178. int serverCount = 0;
  179. if(userType == "Users")
  180. {
  181. userCount = a.cap(3).toInt();
  182. playerCount = 0;
  183. }
  184. else if(userType == "Players")
  185. {
  186. playerCount = a.cap(3).toInt();
  187. userCount = 0;
  188. }
  189. if(channelType == "Channels")
  190. {
  191. channelCount = a.cap(5).toInt();
  192. serverCount = 0;
  193. }
  194. else if(channelType == "Servers")
  195. {
  196. serverCount = a.cap(5).toInt();
  197. channelCount = 0;
  198. }
  199. // qDebug() << commandData;
  200. myApp->incrementReplyCounters(hash, userCount, channelCount, playerCount, serverCount);
  201. return;
  202. }
  203. myApp->print("Unparsed cmd from central: " + time.toString() + " " + command + " " + commandData + "\n");
  204. }
  205. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  206. bool SshClient::connectToHost(const QString &user, const QString &host)
  207. {
  208. myProcess->start("ssh", QStringList() << user + "@" + host);
  209. myUsername = user;
  210. myHostname = host;
  211. bool r = myProcess->waitForStarted();
  212. if(!r)
  213. return false;
  214. else
  215. {
  216. myConnectionTimerID = startTimer(30000);
  217. return true;
  218. }
  219. }
  220. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  221. void SshClient::reconnect()
  222. {
  223. if(!myUsername.isEmpty() && !myHostname.isEmpty())
  224. connectToHost(myUsername, myHostname);
  225. }
  226. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  227. void SshClient::disconnectFromHost()
  228. {
  229. killTimer(myConnectionTimerID);
  230. killTimer(myPingTimerID);
  231. killTimer(myPongTimerID);
  232. myProcess->terminate();
  233. myProcess->waitForFinished();
  234. myConnectedFlag = false;
  235. }
  236. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  237. void SshClient::timerEvent(QTimerEvent *e)
  238. {
  239. if(e->timerId() == myConnectionTimerID)
  240. {
  241. disconnectFromHost();
  242. emit error(ConnectionTimedOutError);
  243. return;
  244. }
  245. if(e->timerId() == myPingTimerID)
  246. {
  247. disconnectFromHost();
  248. emit error(ConnectionTimedOutError);
  249. return;
  250. }
  251. if(e->timerId() == myPongTimerID)
  252. {
  253. killTimer(myPongTimerID);
  254. ping();
  255. return;
  256. }
  257. }