App.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. #include "App.h"
  2. #include "Client.h"
  3. #include <QStringList>
  4. #include <QTcpSocket>
  5. #include <QTcpServer>
  6. #include <QRegExp>
  7. #include <QHostInfo>
  8. #include <QTimerEvent>
  9. #include <QDebug>
  10. #include <QtSql/QSqlQuery>
  11. #include <QCryptographicHash>
  12. #include <QHostAddress>
  13. #include <QThread>
  14. #include <QTimer>
  15. #include "SshClient.h"
  16. #include "ActiveClient.h"
  17. #include "Settings.h"
  18. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  19. /**
  20. Used just to expose the msleep function
  21. from QThread
  22. @author Mihawk <luiz@netdome.biz>
  23. */
  24. class Sleeper: public QThread
  25. {
  26. public:
  27. static void msleep(unsigned long msecs)
  28. {
  29. QThread::msleep(msecs);
  30. }
  31. };
  32. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  33. App::App(int &argc, char **argv) :
  34. QCoreApplication(argc, argv),
  35. myServer(new QTcpServer()),
  36. mySocketConnectedFlag(false),
  37. myQWNETSshClient(new SshClient(this))
  38. {
  39. if(!parseCommandLine())
  40. {
  41. QTimer::singleShot(0, this, SLOT(quit()));
  42. return;
  43. }
  44. print("CIMS Bot Service v0.101\n========================================================\n");
  45. setApplicationName("CIMSBOT");
  46. setOrganizationDomain("qwbr.tk");
  47. setOrganizationName("CIMS");
  48. setApplicationVersion("0.101");
  49. myClientsFrameTimerID = startTimer(0);
  50. myQWNETSshClient->connectToHost("stomp", "b4r.org");
  51. loadServerList();
  52. Settings::globalInstance()->save();
  53. }
  54. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  55. App::~App()
  56. {
  57. Settings::globalInstance()->save();
  58. cleanup();
  59. delete myServer;
  60. }
  61. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  62. bool App::parseCommandLine()
  63. {
  64. if(argc() < 2)
  65. return true;
  66. QStringList args = App::arguments();
  67. QString arg;
  68. QStringList::const_iterator itr;
  69. for(itr = args.constBegin()+1; itr != args.constEnd(); ++itr)
  70. {
  71. arg = *itr;
  72. if(arg == "--help" || arg == "-h")
  73. {
  74. printf("Usage: %s [--config/-c config_file] [-h]\n", args.at(0).section("/", -1).toAscii().data());
  75. return false;
  76. }
  77. else if(arg == "--config" || arg == "-c")
  78. {
  79. itr++;
  80. if(itr == args.constEnd())
  81. {
  82. printf("--config: Expected config file path.\n");
  83. return false;
  84. }
  85. arg = *itr;
  86. printf("Using config file [%s]...\n", arg.toAscii().data());
  87. Settings::globalInstance()->changeConfigFile(*itr);
  88. return true;
  89. }
  90. }
  91. return true;
  92. }
  93. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  94. void App::onNewConnection()
  95. {
  96. if(mySocketConnectedFlag)
  97. {
  98. print("Someone just connected to the bot.\nBye Bye.\n");
  99. mySocket->disconnectFromHost();
  100. if(mySocket->state() != QTcpSocket::UnconnectedState)
  101. mySocket->waitForDisconnected();
  102. mySocketConnectedFlag = false;
  103. }
  104. mySocket = myServer->nextPendingConnection();
  105. mySocketConnectedFlag = true;
  106. connect(mySocket, SIGNAL(readyRead()), SLOT(onDataArrival()));
  107. connect(mySocket, SIGNAL(disconnected()), SLOT(onDisconnection()));
  108. print("Connected to CIMS's bot service.\n");
  109. }
  110. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  111. void App::onDisconnection()
  112. {
  113. mySocketConnectedFlag = false;
  114. mySocket->deleteLater();
  115. }
  116. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  117. void App::loadServerList()
  118. {
  119. QStringList list = Settings::globalInstance()->serverList();
  120. QString sv;
  121. foreach(sv, list)
  122. {
  123. ActiveClient* ac = new ActiveClient(this);
  124. QStringList parms = sv.split(':');
  125. ac->setAddress(QHostAddress(parms.at(0)), parms.at(1).toUShort());
  126. ac->client()->setQuakeFolder(Settings::globalInstance()->quakeFolder().toAscii().data());
  127. ac->client()->setName(Settings::globalInstance()->botName().toAscii().data());
  128. ac->client()->setSpectator(Settings::globalInstance()->botSpectator());
  129. ac->client()->setPing(Settings::globalInstance()->botPing());
  130. ac->client()->setColor(Settings::globalInstance()->botTopColor(), Settings::globalInstance()->botBottomColor());
  131. myClients.push_back(ac);
  132. }
  133. }
  134. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  135. void App::saveServerList()
  136. {
  137. QStringList list;
  138. ActiveClient* ac;
  139. foreach(ac, myClients)
  140. list.push_back(ac->serverAddressString());
  141. Settings::globalInstance()->setServerList(list);
  142. }
  143. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  144. void App::parseAddClientCommand(const QString &args)
  145. {
  146. if(!args.size())
  147. {
  148. print("No server specified\n");
  149. return;
  150. }
  151. if(args.contains(" "))
  152. {
  153. print("Invalid server address\n");
  154. return;
  155. }
  156. QStringList clientData = args.split(":");
  157. if(clientData.size() == 1)
  158. {
  159. addClient(clientData.at(0), 27500);
  160. return;
  161. }
  162. if(clientData.size() == 2)
  163. {
  164. addClient(clientData.at(0), clientData.at(1).toUShort());
  165. return;
  166. }
  167. }
  168. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  169. void App::parseRemoveClientCommand(const QString &args)
  170. {
  171. if(!args.size())
  172. {
  173. print("No server specified\n");
  174. return;
  175. }
  176. QStringList clientData = args.split(":");
  177. if(clientData.size() == 1)
  178. {
  179. removeClient(clientData.at(0), 27500);
  180. return;
  181. }
  182. if(clientData.size() == 2)
  183. {
  184. removeClient(clientData.at(0), clientData.at(1).toUShort());
  185. return;
  186. }
  187. }
  188. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  189. void App::onDataArrival()
  190. {
  191. while(mySocket->canReadLine())
  192. {
  193. QByteArray line;
  194. line = mySocket->readLine();
  195. QString data(line);
  196. QRegExp regex("([0-9a-zA-Z]+)\\s+([a-z]+)\\s+(.*)");
  197. if(regex.indexIn(data) == -1)
  198. {
  199. print("command format: <password> <command> ?<arguments>\nEg.: pss help\nDisconnected\n");
  200. mySocket->disconnectFromHost();
  201. return;
  202. }
  203. QString pass = regex.capturedTexts().at(1);
  204. if(!checkPassword(pass))
  205. {
  206. print("Wrong password\nDisconnected\n");
  207. mySocket->disconnectFromHost();
  208. return;
  209. }
  210. QString cmd = regex.capturedTexts().at(2);
  211. QString args = regex.capturedTexts().at(3).trimmed();
  212. if(cmd == "add")
  213. {
  214. parseAddClientCommand(args);
  215. return;
  216. }
  217. if(cmd == "remove")
  218. {
  219. parseRemoveClientCommand(args);
  220. return;
  221. }
  222. if(cmd == "say")
  223. {
  224. broadcast(args);
  225. return;
  226. }
  227. if(cmd == "servers")
  228. {
  229. listClients();
  230. return;
  231. }
  232. if(cmd == "name")
  233. {
  234. setNick(args);
  235. return;
  236. }
  237. if(cmd == "color")
  238. {
  239. setColor(args);
  240. return;
  241. }
  242. if(cmd == "setping")
  243. {
  244. setPing(args);
  245. return;
  246. }
  247. if(cmd == "team")
  248. {
  249. setTeam(args);
  250. return;
  251. }
  252. if(cmd == "quit")
  253. {
  254. mySocket->disconnectFromHost();
  255. return;
  256. }
  257. if(cmd == "help")
  258. {
  259. help();
  260. return;
  261. }
  262. }
  263. }
  264. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  265. const QStringList& App::lastMessages() const
  266. {
  267. return myLastMessages;
  268. }
  269. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  270. void App::help()
  271. {
  272. print("connect server:port -> connects the bot on a server\n");
  273. print("disconnect server:port -> removes the bot from a server\n");
  274. print("say message -> says the message on all servers where the bot is connected\n");
  275. print("servers -> lists all servers the bot is connected\n");
  276. print("name nick -> changes the bot name to nick\n");
  277. print("color x x -> changes the player color\n");
  278. print("setping x -> sets the bot ping to x. ofc you can't lower your actual ping with this.\n");
  279. print("team teamname -> sets the bot team\n");
  280. print("help -> displays this message\n");
  281. }
  282. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  283. void App::setTeam(const QString &args)
  284. {
  285. ActiveClient* ac;
  286. foreach(ac, myClients)
  287. {
  288. ac->client()->setTeam(args);
  289. }
  290. print("Team changed.\n");
  291. }
  292. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  293. void App::setColor(const QString &args)
  294. {
  295. ActiveClient* ac;
  296. quint8 bottom, top;
  297. QStringList colors = args.split(" ");
  298. if(colors.size() < 2)
  299. return;
  300. bottom = colors.at(0).toUShort();
  301. top = colors.at(1).toUShort();
  302. foreach(ac, myClients)
  303. {
  304. ac->client()->setColor(bottom, top);
  305. }
  306. print("All clients colors have changed.\n");
  307. }
  308. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  309. void App::setPing(const QString &args)
  310. {
  311. ActiveClient* ac;
  312. foreach(ac, myClients)
  313. {
  314. ac->client()->setPing(args.toInt());
  315. }
  316. print("All clients pings have changed.\n");
  317. }
  318. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  319. void App::setNick(const QString &args)
  320. {
  321. ActiveClient* ac;
  322. foreach(ac, myClients)
  323. {
  324. ac->client()->setName(args.toAscii().data());
  325. }
  326. print("All clients nicks have changed.\n");
  327. }
  328. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  329. bool App::checkPassword(const QString &password)
  330. {
  331. if(QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md4).toHex().toLower() == "bf4df9f74d05c50ea00492224fb02854")
  332. return false; //telnet adm disabled!!
  333. return false;
  334. }
  335. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  336. void App::print(const QString &msg)
  337. {
  338. printf("%s", msg.toAscii().data());
  339. if(mySocketConnectedFlag)
  340. {
  341. mySocket->write(msg.toAscii());
  342. mySocket->waitForBytesWritten();
  343. }
  344. }
  345. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  346. void App::addClient(const QString &host, quint16 port)
  347. {
  348. ActiveClient* ac;
  349. QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
  350. foreach(ac, myClients)
  351. {
  352. if(QString(ac->client()->host()) == ha.toString() && ac->client()->port() == port)
  353. {
  354. print("That client is already on the list.\n");
  355. return;
  356. }
  357. }
  358. ac = new ActiveClient(this, this);
  359. ac->setAddress(ha, port);
  360. ac->client()->setQuakeFolder(Settings::globalInstance()->quakeFolder().toAscii().data());
  361. ac->client()->setName(Settings::globalInstance()->botName().toAscii().data());
  362. ac->client()->setSpectator(Settings::globalInstance()->botSpectator());
  363. ac->client()->setPing(Settings::globalInstance()->botPing());
  364. ac->client()->setColor(Settings::globalInstance()->botTopColor(), Settings::globalInstance()->botBottomColor());
  365. myClients.push_back(ac);
  366. saveServerList();
  367. print("Client added to watch list.\n");
  368. }
  369. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  370. void App::removeClient(const QString &host, quint16 port)
  371. {
  372. ActiveClient* ac;
  373. QHostAddress ha = QHostInfo::fromName(host).addresses().at(0);
  374. foreach(ac, myClients)
  375. {
  376. if(ac->serverAddressString() == QString(ha.toString() + ':' + QString::number(port)))
  377. {
  378. ac->client()->disconnect();
  379. delete ac;
  380. myClients.removeAll(ac);
  381. saveServerList();
  382. print("Client removed from watch list.\n");
  383. return;
  384. }
  385. }
  386. print("Client not found on the list.\n");
  387. }
  388. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  389. void App::listClients()
  390. {
  391. ActiveClient* ac;
  392. foreach(ac, myClients)
  393. {
  394. print(QString(ac->serverAddressString() + '\n'));
  395. }
  396. }
  397. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  398. void App::activeClientsReplyCounters(int *serverCount, int *playerCount, ActiveClient *ignoreClient)
  399. {
  400. ActiveClient* ac;
  401. foreach(ac, myClients)
  402. {
  403. if(ac == ignoreClient)
  404. continue;
  405. if(ac->client()->state() == Client::ConnectedState)
  406. {
  407. int pc = ac->playerCount();
  408. if(pc == 255 || pc == 0)
  409. pc = 0;
  410. else
  411. pc--;
  412. *playerCount += pc;
  413. (*serverCount)++;
  414. }
  415. }
  416. }
  417. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  418. void App::timerEvent(QTimerEvent *e)
  419. {
  420. if(e->timerId() == myClientsFrameTimerID)
  421. {
  422. ActiveClient *ac;
  423. foreach(ac, myClients)
  424. {
  425. ac->run();
  426. }
  427. }
  428. Sleeper::msleep(1);
  429. }
  430. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  431. void App::requestBroadcast(const QString &type, const QString &user, const QString &server, const QString &message)
  432. {
  433. if(!Settings::globalInstance()->developerMode())
  434. myQWNETSshClient->write("REQ_BC QWalt,-" + type + "-,qw://" + server + ",'" + user + "','" + message + "'\n");
  435. else
  436. myQWNETSshClient->write("REQ_BC QDEV,-dev-,qw://" + server + ",'" + user + "','" + message + "'\n");
  437. }
  438. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  439. void App::addMessageToHistory(const QString &msg)
  440. {
  441. myLastMessages.push_back(msg);
  442. if(myLastMessages.size() > 5)
  443. myLastMessages.removeAt(0);
  444. }
  445. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  446. void App::broadcast(const QString &msg, ActiveClient* ignoredClient)
  447. {
  448. ActiveClient* ac;
  449. QString frequency = msg.section(' ', 0, 0);
  450. addMessageToHistory(msg);
  451. foreach(ac, myClients)
  452. {
  453. if(ac == ignoredClient)
  454. continue;
  455. if((frequency == "-qw-" && ac->client()->isQWMuted()) || (frequency == "-spam-" && ac->client()->isSpamMuted()))
  456. continue;
  457. if(ac->client()->state() == Client::ConnectedState)
  458. ac->client()->say(msg);
  459. }
  460. }
  461. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  462. void App::broadcast(const QString &msg, int *serverCount, int *userCount)
  463. {
  464. ActiveClient* ac;
  465. *serverCount = 0;
  466. *userCount = 0;
  467. QString frequency = msg.section(' ', 0, 0);
  468. addMessageToHistory(msg);
  469. foreach(ac, myClients)
  470. {
  471. if((frequency == "-qw-" && ac->client()->isQWMuted()) || (frequency == "-spam-" && ac->client()->isSpamMuted()))
  472. continue;
  473. if(ac->client()->state() == Client::ConnectedState)
  474. {
  475. ac->client()->say(msg);
  476. *userCount += ac->playerCount() - 1;
  477. (*serverCount)++;
  478. }
  479. }
  480. }
  481. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  482. void App::cleanup()
  483. {
  484. ActiveClient* ac;
  485. foreach(ac, myClients)
  486. {
  487. ac->client()->disconnect();
  488. delete ac;
  489. }
  490. }
  491. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  492. void App::setReplyHashAndWaitForReply(const QString &serverAddress, const QString &hash)
  493. {
  494. ActiveClient* ac;
  495. foreach(ac, myClients)
  496. {
  497. if(serverAddress == ac->serverAddressString())
  498. {
  499. ac->setReplyHashAndWaitForReply(hash);
  500. return;
  501. }
  502. }
  503. }
  504. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  505. void App::incrementReplyCounters(const QString &hash, int userCount, int channelCount, int playerCount, int serverCount)
  506. {
  507. ActiveClient* ac;
  508. foreach(ac, myClients)
  509. {
  510. if(ac->replyHash() == hash)
  511. {
  512. ac->incrementReplyCounters(userCount, channelCount, playerCount, serverCount);
  513. return;
  514. }
  515. }
  516. }