You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

netsoul.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. #include "netsoul.h"
  2. int NetSoul::m_defaultTimeout = 3000;
  3. NetSoul::NetSoul(QObject *parent) : QObject(parent)
  4. {
  5. m_sock = new QTcpSocket(this);
  6. m_sock->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
  7. connect(m_sock, SIGNAL(hostFound()), this, SLOT(socketHostFound()));
  8. connect(m_sock, SIGNAL(connected()), this, SLOT(socketConnected()));
  9. connect(m_sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
  10. connect(m_sock, SIGNAL(readyRead()), this, SLOT(socketReadyRead()));
  11. connect(m_sock, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
  12. m_timeout = new QTimer(this);
  13. m_timeout->setSingleShot(true);
  14. connect(m_timeout, SIGNAL(timeout()), this, SLOT(timedout()));
  15. m_state = Disconnected;
  16. m_userState = Actif;
  17. setLocation(QString());
  18. m_data = "Epimafia NetSoul";
  19. }
  20. QAbstractSocket::SocketError NetSoul::getLastNetworkError()
  21. {
  22. return m_lastNetworkError;
  23. }
  24. QString NetSoul::getId() const
  25. {
  26. return m_id;
  27. }
  28. EpiUser NetSoul::getEpiUser() const
  29. {
  30. return m_epiUser;
  31. }
  32. NetSoul::User NetSoul::getUser() const
  33. {
  34. return m_user;
  35. }
  36. NetSoul::State NetSoul::getState() const
  37. {
  38. return m_state;
  39. }
  40. QString NetSoul::getLocation() const
  41. {
  42. return m_location;
  43. }
  44. NetSoul::UserState NetSoul::getUserStateFromString(QString state)
  45. {
  46. state = state.toLower().trimmed();
  47. if(state == "away")
  48. return Away;
  49. else if(state == "lock" || state == "locked")
  50. return Lock;
  51. else if(state == "actif")
  52. return Actif;
  53. else if(state == "connection" || state =="login")
  54. return Login;
  55. else if(state == "logout")
  56. return Logout;
  57. return UnknownState;
  58. }
  59. QString NetSoul::getStringFromUserState(NetSoul::UserState state)
  60. {
  61. const QMetaObject & mo = NetSoul::staticMetaObject;
  62. QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("UserState"));
  63. return me.valueToKey(state);
  64. }
  65. QString NetSoul::getDecodedString(QString enc)
  66. {
  67. enc.replace("\\n", "\n");
  68. int min = 0;
  69. int i = -1;
  70. while((i = enc.indexOf("%", min)) != -1)
  71. {
  72. bool ok = false;
  73. int ch = enc.mid(i + 1, 2).toInt(&ok, 16);
  74. if(ok)
  75. enc.replace(i, 3, QChar::fromLatin1((char)ch));
  76. else
  77. min = i + 1;
  78. }
  79. return enc;
  80. }
  81. QString NetSoul::getEncodedString(QString dec)
  82. {
  83. QString out;
  84. foreach(QChar c, dec)
  85. {
  86. if((c.toAscii() >= 'a' && c.toAscii() <= 'z') || (c.toAscii() >= 'A' && c.toAscii() <= 'Z') || (c.toAscii() >= '0' && c.toAscii() <= '9'))
  87. out += c;
  88. else
  89. out += "%" + QString::number((unsigned char)c.toLatin1(), 16);
  90. }
  91. return out;
  92. }
  93. QString NetSoul::getStringFromState(NetSoul::State state)
  94. {
  95. const QMetaObject & mo = NetSoul::staticMetaObject;
  96. QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("State"));
  97. return me.valueToKey(state);
  98. }
  99. void NetSoul::setLocation(QString l)
  100. {
  101. if(l.isEmpty())
  102. l = "Anywhere...";
  103. m_location = l;
  104. }
  105. void NetSoul::setLogin(EpiUser user)
  106. {
  107. m_epiUser = user;
  108. }
  109. void NetSoul::login(EpiUser user)
  110. {
  111. setLogin(user);
  112. login();
  113. }
  114. void NetSoul::login()
  115. {
  116. if(m_state != Disconnected && m_state != ProtocolError && m_state != NetworkError && m_state != NetSoul::BadLogin)
  117. return;
  118. m_id.clear();
  119. m_buf.clear();
  120. m_nextCommands.clear();
  121. m_currentCmd = NoCommand;
  122. m_lastNetworkError = QAbstractSocket::UnknownSocketError;
  123. setState(HostLookUp);
  124. startTimeout();
  125. m_sock->connectToHost("ns-server.epita.fr", 4242);
  126. }
  127. void NetSoul::logout()
  128. {
  129. if(m_state == Disconnecting || m_state == Disconnected || m_state == ProtocolError || m_state == NetworkError || m_state == NetSoul::BadLogin)
  130. return;
  131. setState(Disconnecting);
  132. if(m_state == Connecting)
  133. {
  134. m_sock->abort();
  135. setState(Disconnected);
  136. }
  137. else
  138. write("exit\n", 100);
  139. }
  140. void NetSoul::setUserState(NetSoul::UserState state)
  141. {
  142. addCommand("state " + NetSoul::getStringFromUserState(state).toLower() + ":" + QString::number(QDateTime::currentDateTime().toTime_t()) + "\n", ChangeStatus, -1);
  143. }
  144. void NetSoul::whois(QString user)
  145. {
  146. whois(QStringList() << user);
  147. }
  148. void NetSoul::whois(QStringList users)
  149. {
  150. addCommand("user_cmd who {" + users.join(",") + "}\n", Whois);
  151. }
  152. void NetSoul::addToWatchList(QString user)
  153. {
  154. addToWatchList(QStringList() << user);
  155. }
  156. void NetSoul::addToWatchList(QStringList users)
  157. {
  158. addCommand("user_cmd watch_log_user {" + users.join(",") + "}\n", AddToWatchList, -1);
  159. }
  160. void NetSoul::listUsers(QObject* receiver, const char* member, QString user)
  161. {
  162. NSListUsersEmitter* emitter = new NSListUsersEmitter(this);
  163. connect(emitter, SIGNAL(listed(NetSoul::Users)), receiver, member);
  164. m_listEmitters.append(emitter);
  165. if(!user.isEmpty())
  166. user.prepend(" ");
  167. addCommand("list_users" + user + "\n", ListUsers);
  168. }
  169. void NetSoul::sendMessage(QString user, QString location, QString message)
  170. {
  171. addCommand("user_cmd msg *:" + user + "@*" + getEncodedString(location) + "* msg " + getEncodedString(message) + "\n", SendMessage, -1);
  172. }
  173. void NetSoul::sendMessage(NetSoul::User user, QString message)
  174. {
  175. sendMessage(user.login, user.location, message);
  176. }
  177. void NetSoul::socketConnected()
  178. {
  179. setState(Connected);
  180. startTimeout();
  181. }
  182. void NetSoul::socketDisconnected()
  183. {
  184. if(m_state == Disconnected || m_state == ProtocolError || m_state == NetworkError || m_state == NetSoul::BadLogin)
  185. return;
  186. if(m_state == Disconnecting)
  187. setState(Disconnected);
  188. else
  189. setState(ProtocolError);
  190. }
  191. void NetSoul::socketError(QAbstractSocket::SocketError e)
  192. {
  193. if(m_state == Disconnected || m_state == Disconnecting)
  194. return;
  195. m_lastNetworkError = e;
  196. setState(NetworkError);
  197. }
  198. void NetSoul::socketReadyRead()
  199. {
  200. if(m_currentCmd != NoCommand)
  201. startTimeout();
  202. if(!m_sock->canReadLine() && !m_buf.contains('\n'))
  203. return;
  204. if(m_state == Connected)
  205. {
  206. QString salut = m_sock->readLine();
  207. QRegExp reg("^salut ([0-9]+) ([0-9a-f]{32}) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]{1,5}) ([0-9]+)\n$");
  208. if(salut.indexOf(reg) != -1)
  209. {
  210. if(!m_epiUser.hasLogin())
  211. {
  212. setState(LoggedIn);
  213. nextCommand();
  214. }
  215. else
  216. {
  217. m_user.data = m_data;
  218. m_user.id = reg.cap(1);
  219. m_user.ip = reg.cap(3);
  220. m_user.location = m_location;
  221. m_user.login = m_epiUser.getLogin();
  222. setState(LoggingIn);
  223. write("auth_ag ext_user none none\next_user_log " + m_epiUser.getLogin() + " " +
  224. QString(QCryptographicHash::hash(QString(reg.cap(2) + "-" + reg.cap(3) + "/" + reg.cap(4) + m_epiUser.getPwd_socks()).toStdString().c_str(), QCryptographicHash::Md5).toHex())
  225. + " " + getEncodedString(m_location) + " " + getEncodedString(m_data) + "\n");
  226. }
  227. }
  228. else
  229. protError();
  230. return;
  231. }
  232. else if(m_state == LoggingIn)
  233. {
  234. m_buf += m_sock->readAll();
  235. if(m_buf.count("\n") == 2)
  236. {
  237. if(m_buf == "rep 002 -- cmd end\nrep 002 -- cmd end\n")
  238. {
  239. setState(LoggedIn);
  240. nextCommand();
  241. }
  242. else if(m_buf == "rep 002 -- cmd end\nrep 033 -- ext user identification fail\n")
  243. {
  244. setState(BadLogin);
  245. m_sock->abort();
  246. }
  247. else
  248. protError();
  249. m_buf.clear();
  250. }
  251. return;
  252. }
  253. else if(m_state == LoggedIn)
  254. {
  255. QString d = m_sock->readAll();
  256. m_buf += d;
  257. if(m_currentCmd == Whois)
  258. {
  259. QRegExp user_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| .* -- cmd end\n");
  260. if(m_buf.contains(user_cmd))
  261. {
  262. m_buf.remove(0, user_cmd.cap().size());
  263. foreach(QString cmd, user_cmd.cap().split("\n", QString::SkipEmptyParts))
  264. {
  265. QRegExp get_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| ");
  266. if(cmd.contains(get_cmd))
  267. {
  268. cmd.remove(0, get_cmd.cap().size());
  269. QRegExp reg("^who ([0-9]+) ([^ ]+) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$");
  270. if(cmd.contains(reg))
  271. {
  272. QStringList args = reg.capturedTexts();
  273. args.removeAt(0);
  274. for(int i = 0; i <= 4; ++i)
  275. args.removeAt(3);
  276. User usr;
  277. int i = 0;
  278. usr.id = args.at(i++);
  279. usr.login = args.at(i++);
  280. usr.ip = args.at(i++);
  281. usr.location = getDecodedString(args.at(i++));
  282. usr.promo = getDecodedString(args.at(i++));
  283. usr.stateString = args.at(i++).split(":").at(0);
  284. usr.state = getUserStateFromString(usr.stateString);
  285. usr.data = getDecodedString(args.at(i++));
  286. emit userDataChanged(usr);
  287. }
  288. else if(cmd != "who rep 002 -- cmd end")
  289. {
  290. protError();
  291. return;
  292. }
  293. }
  294. else
  295. {
  296. protError();
  297. return;
  298. }
  299. }
  300. commandFinished();
  301. }
  302. else
  303. return;
  304. }
  305. else if(m_currentCmd == ListUsers)
  306. {
  307. QString cmdEnd = "rep 002 -- cmd end\n";
  308. int index = m_buf.indexOf(cmdEnd);
  309. if(index == -1)
  310. return;
  311. QString users = m_buf.left(index + cmdEnd.size());
  312. m_buf.remove(0, users.size());
  313. Users usrs;
  314. foreach(QString user, users.split('\n', QString::SkipEmptyParts))
  315. {
  316. QRegExp reg("^([0-9]+) ([^ ]+) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$");
  317. if(user.contains(reg))
  318. {
  319. QStringList args = reg.capturedTexts();
  320. args.removeAt(0);
  321. for(int i = 0; i <= 4; ++i)
  322. args.removeAt(3);
  323. User usr;
  324. int i = 0;
  325. usr.id = args.at(i++);
  326. usr.login = args.at(i++);
  327. usr.ip = args.at(i++);
  328. usr.location = getDecodedString(args.at(i++));
  329. usr.promo = getDecodedString(args.at(i++));
  330. usr.stateString = args.at(i++).split(":").at(0);
  331. usr.state = getUserStateFromString(usr.stateString);
  332. usr.data = getDecodedString(args.at(i++));
  333. usrs.append(usr);
  334. }
  335. else if(user != "rep 002 -- cmd end")
  336. {
  337. protError();
  338. return;
  339. }
  340. }
  341. if(!m_listEmitters.isEmpty())
  342. {
  343. NSListUsersEmitter* emitter = m_listEmitters.at(0);
  344. emitter->emitList(usrs);
  345. emitter->deleteLater();
  346. m_listEmitters.removeAt(0);
  347. }
  348. commandFinished();
  349. }
  350. else
  351. {
  352. QRegExp user_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| ([^\n]+)\n");
  353. QRegExp ping("^ping ([0-9]+) \n");
  354. if(m_buf.contains(user_cmd))
  355. {
  356. m_buf.remove(0, user_cmd.cap().size());
  357. User usr;
  358. usr.id = user_cmd.cap(1);
  359. usr.login = user_cmd.cap(4).split("@").at(0);
  360. usr.ip = user_cmd.cap(4).split("@").at(1);
  361. usr.location = getDecodedString(user_cmd.cap(6));
  362. usr.promo = user_cmd.cap(7);
  363. QString cmd = user_cmd.cap(8).trimmed();
  364. QRegExp state("^state ([^ ]+):([0-9]+)$");
  365. QRegExp msg("^msg ([^ ]*) dst=(.+)$");
  366. if(cmd == "logout")
  367. {
  368. usr.state = Logout;
  369. usr.stateString = getStringFromUserState(usr.state);
  370. emit userDataChanged(usr);
  371. commandFinished();
  372. }
  373. else if(cmd == "login")
  374. {
  375. usr.state = Login;
  376. usr.stateString = getStringFromUserState(usr.state);
  377. emit userDataChanged(usr);
  378. commandFinished();
  379. }
  380. else if(cmd.contains(msg))
  381. {
  382. Message message;
  383. message.from = usr;
  384. message.message = getDecodedString(msg.cap(1));
  385. emit newMessage(message);
  386. commandFinished();
  387. }
  388. else if(cmd.contains(state))
  389. {
  390. usr.stateString = state.cap(1);
  391. usr.state = getUserStateFromString(usr.stateString);
  392. emit userDataChanged(usr);
  393. commandFinished();
  394. }
  395. else
  396. emit unknownCommand("user_cmd: " + cmd);
  397. }
  398. else if(m_buf.contains(ping))
  399. {
  400. m_buf.remove(0, ping.cap().size());
  401. addCommand("ping\n", Ping, -1);
  402. }
  403. else
  404. {
  405. int i = m_buf.indexOf("\n");
  406. emit unknownCommand(m_buf.left(i + 1));
  407. m_buf.remove(0, i + 1);
  408. }
  409. }
  410. }
  411. else
  412. {
  413. m_buf.clear();
  414. return;
  415. }
  416. socketReadyRead();
  417. }
  418. void NetSoul::socketHostFound()
  419. {
  420. setState(Connecting);
  421. }
  422. void NetSoul::setState(NetSoul::State s)
  423. {
  424. m_state = s;
  425. emit stateChanged(m_state);
  426. }
  427. void NetSoul::protError()
  428. {
  429. if(m_state == Disconnected || m_state == NetworkError || m_state == ProtocolError || m_state == NetSoul::BadLogin)
  430. return;
  431. setState(ProtocolError);
  432. m_sock->abort();
  433. }
  434. void NetSoul::startTimeout()
  435. {
  436. startTimeout(m_defaultTimeout);
  437. }
  438. void NetSoul::startTimeout(int t)
  439. {
  440. m_timeout->start(t);
  441. }
  442. void NetSoul::stopTimeout()
  443. {
  444. m_timeout->stop();
  445. }
  446. void NetSoul::write(QString c)
  447. {
  448. write(c, m_defaultTimeout);
  449. }
  450. void NetSoul::write(QString c, int t)
  451. {
  452. if(t != -1)
  453. startTimeout(t);
  454. m_sock->write(c.toAscii().constData());
  455. }
  456. void NetSoul::timedout()
  457. {
  458. if(m_state == Disconnected || m_state == NetworkError || m_state == ProtocolError || m_state == NetSoul::BadLogin)
  459. return;
  460. if(m_state == Disconnecting)
  461. {
  462. setState(Disconnected);
  463. m_sock->abort();
  464. return;
  465. }
  466. m_lastNetworkError = QAbstractSocket::SocketTimeoutError;
  467. setState(NetworkError);
  468. m_sock->abort();
  469. }
  470. void NetSoul::nextCommand()
  471. {
  472. stopTimeout();
  473. if(m_currentCmd == NoCommand && !m_nextCommands.isEmpty() && (m_state == LoggedIn || (m_state == Connected && !m_epiUser.hasLogin())))
  474. {
  475. NextCommand cmd = m_nextCommands.at(0);
  476. m_nextCommands.removeAt(0);
  477. m_currentCmd = cmd.commandName;
  478. write(cmd.commandString, cmd.timeout);
  479. if(cmd.timeout == -1)
  480. {
  481. m_currentCmd = NoCommand;
  482. nextCommand();
  483. }
  484. }
  485. }
  486. void NetSoul::addCommand(QString cmd, NetSoul::Command command)
  487. {
  488. addCommand(cmd, command, m_defaultTimeout);
  489. }
  490. void NetSoul::addCommand(QString cmd, NetSoul::Command command, int t)
  491. {
  492. NextCommand c;
  493. c.commandName = command;
  494. c.commandString = cmd;
  495. c.timeout = t;
  496. m_nextCommands.append(c);
  497. nextCommand();
  498. }
  499. void NetSoul::commandFinished()
  500. {
  501. m_currentCmd = NoCommand;
  502. nextCommand();
  503. }
  504. QDebug &operator<< (QDebug d, NetSoul::Command& cmd)
  505. {
  506. const QMetaObject & mo = NetSoul::staticMetaObject;
  507. QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("Command"));
  508. return d<<me.valueToKey(cmd);
  509. }
  510. QDebug &operator<< (QDebug d, NetSoul::State& state)
  511. {
  512. return d << NetSoul::getStringFromState(state);
  513. }
  514. QDebug &operator<< (QDebug d, NetSoul::UserState& state)
  515. {
  516. return d << NetSoul::getStringFromUserState(state);
  517. }
  518. QDebug &operator<< (QDebug d, NetSoul::User& user)
  519. {
  520. d.nospace()<<"("<<user.id<<" "<<user.login.toStdString().c_str()<<"@"<<user.location.toStdString().c_str()
  521. <<" "<<user.ip.toStdString().c_str()<<" "<<user.state<< (user.state == NetSoul::UnknownState ? " " + user.stateString : "")<<" "<<user.promo<<" "<<user.data<<")";
  522. return d.maybeSpace();
  523. }
  524. QDebug &operator<< (QDebug d, NetSoul::Message& msg)
  525. {
  526. return d<<"("<<msg.from<<msg.message<<")";
  527. }
  528. void NSListUsersEmitter::emitList(NetSoul::Users users)
  529. {
  530. emit listed(users);
  531. }
  532. QDataStream &operator<<(QDataStream& stream, NetSoul::Message& msg)
  533. {
  534. return stream << msg.from << msg.message;
  535. }
  536. QDataStream &operator>>(QDataStream& stream, NetSoul::Message& msg)
  537. {
  538. QString m;
  539. NetSoul::User u;
  540. stream >> u;
  541. stream >> m;
  542. if(stream.status() == QDataStream::Ok)
  543. {
  544. msg.from = u;
  545. msg.message = m;
  546. }
  547. return stream;
  548. }
  549. QDataStream &operator<<(QDataStream& stream, NetSoul::UserState& state)
  550. {
  551. return stream << (quint8)state;
  552. }
  553. QDataStream &operator>>(QDataStream& stream, NetSoul::UserState& state)
  554. {
  555. quint8 s;
  556. stream >> s;
  557. if(stream.status() == QDataStream::Ok)
  558. state = (NetSoul::UserState)s;
  559. return stream;
  560. }
  561. QDataStream &operator<<(QDataStream& stream, NetSoul::State& state)
  562. {
  563. return stream << (quint8)state;
  564. }
  565. QDataStream &operator>>(QDataStream& stream, NetSoul::State& state)
  566. {
  567. quint8 s;
  568. stream >> s;
  569. if(stream.status() == QDataStream::Ok)
  570. state = (NetSoul::State)s;
  571. return stream;
  572. }
  573. QDataStream &operator<<(QDataStream& stream, NetSoul::User& user)
  574. {
  575. return stream << user.data
  576. << user.id
  577. << user.ip
  578. << user.location
  579. << user.login
  580. << user.promo
  581. << user.state
  582. << user.stateString;
  583. }
  584. QDataStream &operator>>(QDataStream& stream, NetSoul::User& user)
  585. {
  586. QString data, id, ip, location, login, promo, stateString;
  587. NetSoul::UserState state;
  588. stream >> data;
  589. stream >> id;
  590. stream >> ip;
  591. stream >> location;
  592. stream >> login;
  593. stream >> promo;
  594. stream >> state;
  595. stream >> stateString;
  596. if(stream.status() == QDataStream::Ok)
  597. {
  598. user.data = data;
  599. user.id = id;
  600. user.ip = ip;
  601. user.location = location;
  602. user.login = login;
  603. user.promo = promo;
  604. user.state = state;
  605. user.stateString = stateString;
  606. }
  607. return stream;
  608. }