PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/spring_89.0/rts/lib/lobby/Connection.cpp

#
C++ | 571 lines | 524 code | 44 blank | 3 comment | 130 complexity | a7c1d7aa0a71bb9bae3cf7f3f49793dd MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, Unlicense, AGPL-3.0, LGPL-2.1
  1. /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
  2. #ifdef _MSC_VER
  3. #include "lib/streflop/streflop_cond.h"
  4. #endif
  5. #include "Connection.h"
  6. #include "RawTextMessage.h"
  7. #include <bitset>
  8. #include <boost/asio.hpp>
  9. #include <boost/bind.hpp>
  10. #include "System/Net/Socket.h"
  11. #include "lib/md5/md5.h"
  12. #include "lib/md5/base64.h"
  13. #include "lib/streflop/streflop_cond.h"
  14. #if BOOST_VERSION < 103600
  15. using namespace boost::system::posix_error;
  16. #else
  17. using namespace boost::system::errc;
  18. #endif
  19. using namespace boost::asio;
  20. Connection::Connection() : sock(netservice), timer(netservice)
  21. {
  22. }
  23. Connection::~Connection()
  24. {
  25. if (sock.is_open()) {
  26. SendData("EXIT\n");
  27. }
  28. sock.shutdown(boost::asio::ip::udp::socket::shutdown_both);
  29. sock.close();
  30. netservice.poll();
  31. netservice.reset();
  32. }
  33. void Connection::Connect(const std::string& server, int port)
  34. {
  35. using namespace boost::asio;
  36. boost::system::error_code err;
  37. ip::address tempAddr = netcode::WrapIP(server, &err);
  38. if (err)
  39. {
  40. // error, maybe a hostname?
  41. ip::tcp::resolver resolver(netservice);
  42. std::ostringstream portbuf;
  43. portbuf << port;
  44. ip::tcp::resolver::query query(server, portbuf.str());
  45. ip::tcp::resolver::iterator iter = netcode::WrapResolve(resolver, query, &err);
  46. if (err)
  47. {
  48. DoneConnecting(false, err.message());
  49. return;
  50. }
  51. tempAddr = iter->endpoint().address();
  52. }
  53. ip::tcp::endpoint serverep(tempAddr, port);
  54. sock.async_connect(serverep, boost::bind(&Connection::ConnectCallback, this, placeholders::error));
  55. }
  56. void Connection::Disconnect()
  57. {
  58. if (sock.is_open()) {
  59. SendData("EXIT\n");
  60. }
  61. sock.close();
  62. Disconnected();
  63. }
  64. std::string MD5Base64(const std::string& plain)
  65. {
  66. md5_state_t state;
  67. md5_init(&state);
  68. md5_append(&state, (md5_byte_t*)plain.c_str(), plain.size());
  69. unsigned char digest[16];
  70. md5_finish(&state, digest);
  71. return base64_encode(digest, 16);
  72. }
  73. void Connection::Register(const std::string& name, const std::string& password)
  74. {
  75. std::ostringstream out;
  76. out << "REGISTER " << name << " " << MD5Base64(password) << "\n";
  77. SendData(out.str());
  78. }
  79. void Connection::Login(const std::string& name, const std::string& password)
  80. {
  81. std::ostringstream out;
  82. out << "LOGIN " << name << " " << MD5Base64(password) << " 0 localhost libLobby v0.1\n";
  83. SendData(out.str());
  84. myUserName = name;
  85. }
  86. void Connection::ConfirmAggreement()
  87. {
  88. SendData("CONFIRMAGREEMENT\n");
  89. }
  90. void Connection::Rename(const std::string& newName)
  91. {
  92. std::ostringstream out;
  93. out << "RENAMEACCOUNT " << newName << "\n";
  94. SendData(out.str());
  95. }
  96. void Connection::ChangePass(const std::string& oldpass, const std::string& newpass)
  97. {
  98. std::ostringstream out;
  99. out << "CHANGEPASSWORD " << MD5Base64(oldpass) << " " << MD5Base64(newpass) << "\n";
  100. SendData(out.str());
  101. }
  102. void Connection::StatusUpdate(bool ingame, bool away)
  103. {
  104. std::bitset<8> statusbf;
  105. statusbf.set(0, ingame);
  106. statusbf.set(1, away);
  107. int trank = myStatus.rank;
  108. if (trank >= 4)
  109. {
  110. statusbf.set(4);
  111. trank -= 4;
  112. }
  113. if (trank >= 2)
  114. {
  115. statusbf.set(3);
  116. trank -= 2;
  117. }
  118. if (trank == 1)
  119. {
  120. statusbf.set(2);
  121. }
  122. statusbf.set(5, myStatus.moderator);
  123. statusbf.set(6, myStatus.bot);
  124. std::ostringstream out;
  125. out << "MYSTATUS " << static_cast<unsigned>(statusbf.to_ulong()) << "\n";
  126. SendData(out.str());
  127. }
  128. void Connection::Channels()
  129. {
  130. SendData("CHANNELS\n");
  131. }
  132. void Connection::RequestMutelist(const std::string& channel)
  133. {
  134. std::ostringstream out;
  135. out << "MUTELIST " << channel << std::endl;
  136. SendData(out.str());
  137. }
  138. void Connection::JoinChannel(const std::string& channame, const std::string& password)
  139. {
  140. std::ostringstream out;
  141. out << "JOIN " << channame;
  142. if (!password.empty())
  143. out << " " << password;
  144. out << std::endl;
  145. SendData(out.str());
  146. }
  147. void Connection::LeaveChannel(const std::string& channame)
  148. {
  149. std::ostringstream out;
  150. out << "LEAVE " << channame << std::endl;
  151. SendData(out.str());
  152. }
  153. void Connection::KickChannelMember(const std::string& channame, const std::string& user, const std::string& reason)
  154. {
  155. std::ostringstream out;
  156. out << "FORCELEAVECHANNEL " << channame << " " << user;
  157. if (!reason.empty())
  158. out << " " << reason;
  159. out << std::endl;
  160. SendData(out.str());
  161. }
  162. void Connection::ChangeTopic(const std::string& channame, const std::string& topic)
  163. {
  164. std::ostringstream out;
  165. out << "CHANNELTOPIC " << channame << " " << topic << std::endl;
  166. SendData(out.str());
  167. }
  168. void Connection::Say(const std::string& channel, const std::string& text)
  169. {
  170. std::ostringstream out;
  171. out << "SAY " << channel << " " << text << std::endl;
  172. SendData(out.str());
  173. }
  174. void Connection::SayEx(const std::string& channel, const std::string& text)
  175. {
  176. std::ostringstream out;
  177. out << "SAYEX " << channel << " " << text << std::endl;
  178. SendData(out.str());
  179. }
  180. void Connection::SayPrivate(const std::string& user, const std::string& text)
  181. {
  182. std::ostringstream out;
  183. out << "SAYPRIVATE " << user << " " << text << std::endl;
  184. SendData(out.str());
  185. }
  186. void Connection::Ping()
  187. {
  188. if (sock.is_open())
  189. {
  190. SendData("PING\n");
  191. timer.expires_from_now(boost::posix_time::seconds(10));
  192. timer.async_wait(boost::bind(&Connection::Ping, this));
  193. }
  194. }
  195. void Connection::SendData(const std::string& msg)
  196. {
  197. if (sock.is_open())
  198. {
  199. sock.send(boost::asio::buffer(msg));
  200. }
  201. else
  202. {
  203. NetworkError("Already disconnected");
  204. }
  205. }
  206. void Connection::Poll()
  207. {
  208. netservice.poll();
  209. }
  210. void Connection::Run()
  211. {
  212. netservice.run();
  213. netservice.reset();
  214. }
  215. void Connection::DataReceived(const std::string& command, const std::string& msg)
  216. {
  217. if (command == "TASServer")
  218. {
  219. if (!msg.empty())
  220. {
  221. RawTextMessage buf(msg);
  222. std::string serverVer = buf.GetWord();
  223. std::string clientVer = buf.GetWord();
  224. int udpport = buf.GetInt();
  225. int mode = buf.GetInt();
  226. ServerGreeting(serverVer, clientVer, udpport, mode);
  227. }
  228. Ping();
  229. }
  230. else if (command == "DENIED")
  231. {
  232. LoginDenied(msg);
  233. }
  234. else if (command == "LOGININFOEND")
  235. {
  236. LoginEnd();
  237. }
  238. else if (command == "REGISTRATIONDENIED")
  239. {
  240. RegisterDenied(msg);
  241. }
  242. else if (command == "REGISTRATIONACCEPTED")
  243. {
  244. RegisterAccepted();
  245. }
  246. else if (command == "ADDUSER")
  247. {
  248. RawTextMessage buf(msg);
  249. std::string name = buf.GetWord();
  250. std::string country = buf.GetWord();
  251. int cpu = buf.GetInt();
  252. AddUser(name, country, cpu);
  253. }
  254. else if (command == "REMOVEUSER")
  255. {
  256. RawTextMessage buf(msg);
  257. std::string name = buf.GetWord();
  258. RemoveUser(name);
  259. }
  260. else if (command == "CLIENTSTATUS")
  261. {
  262. RawTextMessage buf(msg);
  263. std::string name = buf.GetWord();
  264. ClientStatus status;
  265. std::bitset<8> statusbf(buf.GetInt());
  266. status.ingame = statusbf[0];
  267. status.away = statusbf[1];
  268. status.rank = (statusbf[2]? 1 : 0) + (statusbf[3]? 2 : 0) + (statusbf[4]? 4 : 0);
  269. status.moderator = statusbf[5];
  270. status.bot = statusbf[6];
  271. if (name == myUserName)
  272. myStatus = status;
  273. UserStatusUpdate(name, status);
  274. }
  275. else if (command == "JOIN")
  276. {
  277. RawTextMessage buf(msg);
  278. std::string channame = buf.GetWord();
  279. Joined(channame);
  280. }
  281. else if (command == "JOINFAILED")
  282. {
  283. RawTextMessage buf(msg);
  284. std::string channame = buf.GetWord();
  285. std::string reason = buf.GetWord();
  286. JoinFailed(channame, reason);
  287. }
  288. else if (command == "CLIENTS")
  289. {
  290. RawTextMessage buf(msg);
  291. std::string channame = buf.GetWord();
  292. std::string client;
  293. while (!(client = buf.GetWord()).empty())
  294. {
  295. ChannelMember(channame, client, false);
  296. }
  297. }
  298. else if (command == "JOINED")
  299. {
  300. RawTextMessage buf(msg);
  301. std::string channame = buf.GetWord();
  302. std::string client = buf.GetWord();
  303. ChannelMember(channame, client, true);
  304. }
  305. else if (command == "LEFT")
  306. {
  307. RawTextMessage buf(msg);
  308. std::string channame = buf.GetWord();
  309. std::string client = buf.GetWord();
  310. std::string reason = buf.GetSentence();
  311. ChannelMemberLeft(channame, client, reason);
  312. }
  313. else if (command == "FORCELEAVECHANNEL")
  314. {
  315. RawTextMessage buf(msg);
  316. std::string channame = buf.GetWord();
  317. std::string client = buf.GetWord();
  318. std::string reason = buf.GetSentence();
  319. ChannelMemberKicked(channame, client, reason);
  320. }
  321. else if (command == "CHANNELTOPIC")
  322. {
  323. RawTextMessage buf(msg);
  324. std::string channame = buf.GetWord();
  325. std::string author = buf.GetWord();
  326. long unsigned time = buf.GetTime();
  327. std::string topic = buf.GetSentence();
  328. ChannelTopic(channame, author, time, topic);
  329. }
  330. else if (command == "CHANNELMESSAGE")
  331. {
  332. RawTextMessage buf(msg);
  333. std::string channame = buf.GetWord();
  334. std::string message = buf.GetSentence();
  335. ChannelMessage(channame, message);
  336. }
  337. else if (command == "AGREEMENT")
  338. {
  339. aggreementbuf += msg + "\n";
  340. }
  341. else if (command == "AGREEMENTEND")
  342. {
  343. Aggreement(Connection::RTFToPlain(aggreementbuf));
  344. }
  345. else if (command == "MOTD")
  346. {
  347. Motd(msg);
  348. }
  349. else if (command == "SERVERMSG")
  350. {
  351. ServerMessage(msg);
  352. }
  353. else if (command == "CHANNEL")
  354. {
  355. RawTextMessage buf(msg);
  356. std::string channame = buf.GetWord();
  357. unsigned users = buf.GetInt();
  358. ChannelInfo(channame, users);
  359. }
  360. else if (command == "ENDOFCHANNELS")
  361. {
  362. ChannelInfoEnd();
  363. }
  364. else if (command == "MUTELISTBEGIN")
  365. {
  366. inMutelistChannel = msg;
  367. mutelistBuf.clear();
  368. }
  369. else if (command == "MUTELIST")
  370. {
  371. mutelistBuf.push_back(msg);
  372. }
  373. else if (command == "MUTELISTEND")
  374. {
  375. Mutelist(inMutelistChannel, mutelistBuf);
  376. inMutelistChannel.clear();
  377. mutelistBuf.clear();
  378. }
  379. else if (command == "SERVERMSGBOX")
  380. {
  381. RawTextMessage buf(msg);
  382. std::string message = buf.GetSentence();
  383. std::string url = buf.GetSentence();
  384. ServerMessageBox(message, url);
  385. }
  386. else if (command == "SAID")
  387. {
  388. RawTextMessage buf(msg);
  389. std::string channame = buf.GetWord();
  390. std::string user = buf.GetWord();
  391. std::string text = buf.GetSentence();
  392. Said(channame, user, text);
  393. }
  394. else if (command == "SAIDEX")
  395. {
  396. RawTextMessage buf(msg);
  397. std::string channame = buf.GetWord();
  398. std::string user = buf.GetWord();
  399. std::string text = buf.GetSentence();
  400. SaidEx(channame, user, text);
  401. }
  402. else if (command == "SAIDPRIVATE")
  403. {
  404. RawTextMessage buf(msg);
  405. std::string user = buf.GetWord();
  406. std::string text = buf.GetSentence();
  407. SaidPrivate(user, text);
  408. }
  409. else if (command == "BATTLEOPENED")
  410. {
  411. RawTextMessage buf(msg);
  412. int id = buf.GetInt();
  413. bool replay = !buf.GetInt();
  414. bool nat = buf.GetInt();
  415. std::string founder = buf.GetWord();
  416. std::string ip = buf.GetWord();
  417. int port = buf.GetInt();
  418. int maxplayers = buf.GetInt();
  419. bool password = buf.GetInt();
  420. int rank = buf.GetInt();
  421. int maphash = buf.GetInt();
  422. std::string map = buf.GetSentence();
  423. std::string title = buf.GetSentence();
  424. std::string mod = buf.GetSentence();
  425. BattleOpened(id, replay, nat, founder, ip, port, maxplayers, password, rank, maphash, title, map, mod);
  426. }
  427. else if (command == "UPDATEBATTLEINFO")
  428. {
  429. RawTextMessage buf(msg);
  430. int id = buf.GetInt();
  431. int spectators = buf.GetInt();
  432. bool locked = buf.GetInt();
  433. int maphash = buf.GetInt();
  434. std::string map = buf.GetSentence();
  435. BattleUpdated(id, spectators, locked, maphash, map);
  436. }
  437. else if (command == "BATTLECLOSED")
  438. {
  439. RawTextMessage buf(msg);
  440. int id = buf.GetInt();
  441. BattleClosed(id);
  442. }
  443. else
  444. {
  445. // std::cout << "Unhandled command: " << command << " " << msg << std::endl;
  446. }
  447. }
  448. void Connection::ConnectCallback(const boost::system::error_code& error)
  449. {
  450. if (!error)
  451. {
  452. DoneConnecting(true, "");
  453. boost::asio::async_read_until(sock, incomeBuffer, "\n", boost::bind(&Connection::ReceiveCallback, this, placeholders::error, placeholders::bytes_transferred));
  454. }
  455. else
  456. {
  457. DoneConnecting(false, error.message());
  458. }
  459. }
  460. void Connection::ReceiveCallback(const boost::system::error_code& error, size_t bytes)
  461. {
  462. if (!error)
  463. {
  464. std::string msg;
  465. std::string command;
  466. std::istream buf(&incomeBuffer);
  467. buf >> command;
  468. std::getline(buf, msg);
  469. if (!msg.empty())
  470. msg = msg.substr(1);
  471. DataReceived(command, msg);
  472. }
  473. else
  474. {
  475. if (error.value() == connection_reset || error.value() == boost::asio::error::eof)
  476. {
  477. sock.close();
  478. Disconnected();
  479. }
  480. else if (sock.is_open()) //! ignore error messages after connect was closed
  481. {
  482. NetworkError(error.message());
  483. }
  484. }
  485. if (sock.is_open())
  486. {
  487. boost::asio::async_read_until(sock, incomeBuffer, "\n", boost::bind(&Connection::ReceiveCallback, this, placeholders::error, placeholders::bytes_transferred));
  488. }
  489. }
  490. void Connection::SendCallback(const boost::system::error_code& error)
  491. {
  492. if (!error)
  493. {
  494. }
  495. else
  496. {
  497. }
  498. }
  499. std::string Connection::RTFToPlain(const std::string& rich)
  500. {
  501. static const std::string screwTag = "\\pard";
  502. static const std::string lineend = "\\par";
  503. std::string out = rich.substr(rich.find_first_of('{')+1, (rich.find_last_of('}') - rich.find_first_of('{')) -2);
  504. out = out.substr(out.find_last_of('}')+1);
  505. size_t pos = 0;
  506. while ((pos = out.find(screwTag)) != std::string::npos)
  507. {
  508. out.erase(pos, screwTag.size());
  509. }
  510. while ((pos = out.find(lineend)) != std::string::npos)
  511. {
  512. out.replace(pos, lineend.size(), "\n");
  513. }
  514. while ((pos = out.find('\\')) != std::string::npos)
  515. {
  516. size_t pos2 = out.find_first_of("\\ \t\n}", pos+1);
  517. if (pos2 != std::string::npos)
  518. {
  519. out.erase(pos, pos2-pos);
  520. }
  521. else
  522. {
  523. break;
  524. }
  525. }
  526. return out;
  527. }