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 17KB

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