udpsession.cpp
上传用户:zslianheng
上传日期:2013-04-03
资源大小:946k
文件大小:28k
源码类别:

Linux/Unix编程

开发平台:

Visual C++

  1. /***************************************************************************
  2.  *                                                                         *
  3.  *   This program is free software; you can redistribute it and/or modify  *
  4.  *   it under the terms of the GNU General Public License as published by  *
  5.  *   the Free Software Foundation; either version 2 of the License, or     *
  6.  *   (at your option) any later version.                                   *
  7.  *                                                                         *
  8.  *   copyright            : (C) 2002 by Zhang Yong                         *
  9.  *   email                : z-yong163@163.com                              *
  10.  ***************************************************************************/
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <iostream.h>
  15. #include "udppacket.h"
  16. #include "sessionhash.h"
  17. #include "ndes.h"
  18. enum {
  19. UDP_ACK = 1,
  20. UDP_NEW_UIN,
  21. UDP_GET_CONTACTLIST,
  22. UDP_LOGIN,
  23. UDP_LOGOUT,
  24. UDP_KEEPALIVE,
  25. UDP_CHANGE_STATUS,
  26. UDP_UPDATE_CONTACT,
  27. UDP_MODIFY_USER,
  28. UDP_UPDATE_USER,
  29. UDP_SEND_MSG,
  30. UDP_GROUP_SEND_MSG,
  31. UDP_SEARCH_RANDOM,
  32. UDP_SEARCH_CUSTOM,
  33. UDP_ADD_FRIEND,
  34. UDP_DEL_FRIEND,
  35. UDP_BROADCAST_MSG,
  36. UDP_SRV_USER_ONLINE = 0x0100,
  37. UDP_SRV_USER_OFFLINE,
  38. UDP_SRV_MULTI_ONLINE,
  39. UDP_SRV_STATUS_CHANGED,
  40. UDP_SRV_MESSAGE,
  41. UDP_SRV_SEARCH,
  42. };
  43. enum {
  44. LOGIN_SUCCESS,
  45. LOGIN_INVALID_UIN,
  46. LOGIN_WRONG_PASSWD,
  47. };
  48. enum {
  49. STATUS_ONLINE = 0,
  50. STATUS_OFFLINE,
  51. STATUS_AWAY,
  52. STATUS_INVIS
  53. };
  54. enum {
  55. MSG_TEXT,
  56. MSG_AUTO_REPLY,
  57. MSG_AUTH_ACCEPTED,
  58. MSG_AUTH_REQ,
  59. MSG_AUTH_REJECTED,
  60. MSG_ADDED,
  61. MSG_BROADCAST,
  62. };
  63. enum {
  64. ADD_FRIEND_ACCEPTED,
  65. ADD_FRIEND_AUTH_REQ,
  66. ADD_FRIEND_REJECTED,
  67. };
  68. #define MYICQ_UDP_VER 1
  69. #define MYICQ_PORT_DEFAULT 8000
  70. #define MAX_SEARCH_PER_PAGE 25
  71. #define MAX_SEND_ATTEMPTS 2
  72. #define MAX_SQL_STATEMENT 4096
  73. #define MAX_MSG_LEN 512
  74. #define START_UIN 1000
  75. struct BROADCAST_MSG {
  76. IcqListItem listItem;
  77. uint32 id;
  78. uint8 type;
  79. uint32 when;
  80. uint32 src;
  81. char text[MAX_MSG_LEN];
  82. uint32 to;
  83. uint32 maxUIN;
  84. time_t expire;
  85. };
  86. int UdpSession::sock = -1;
  87. IcqList UdpSession::globalSendQueue;
  88. IcqList UdpSession::keepAliveList;
  89. IcqList UdpSession::broadMsgList;
  90. MYSQL UdpSession::mysql;
  91. char UdpSession::sqlStmt[MAX_SQL_STATEMENT];
  92. uint32 UdpSession::sessionCount = 0;
  93. bool UdpSession::initialize()
  94. {
  95. srand(time(NULL));
  96. desinit(0);
  97. sock = socket(AF_INET, SOCK_DGRAM, 0);
  98. if (sock < 0) {
  99. cerr << "create socket failed." << endl;
  100. return false;
  101. }
  102. sockaddr_in addr;
  103. memset(&addr, 0, sizeof(addr));
  104. addr.sin_family = AF_INET;
  105. addr.sin_port = htons(MYICQ_PORT_DEFAULT);
  106. addr.sin_addr.s_addr = INADDR_ANY;
  107. if (bind(sock, (sockaddr *) &addr, sizeof(addr)) < 0) {
  108. cerr << "socket bind failed." << endl;
  109. close(sock);
  110. return false;
  111. }
  112. if (!mysql_init(&mysql)) {
  113. cerr << "mysql initialization failed." << endl;
  114. return false;
  115. }
  116. const char *host = LOCAL_HOST;
  117. const char *user = "myicq";
  118. const char *pass = "myicq";
  119. const char *db = "myicq";
  120. const char *unix_socket = NULL;
  121. if (!mysql_real_connect(&mysql, host, user, pass, db, 0, unix_socket, 0)) {
  122. cerr << "can not connect to mysql server." << endl;
  123. mysql_close(&mysql);
  124. return false;
  125. }
  126. return true;
  127. }
  128. void UdpSession::cleanUp()
  129. {
  130. IcqListItem *head = &keepAliveList.head;
  131. IcqListItem *pos;
  132. UdpSession *p;
  133. while ((pos = head->next) != head) {
  134. p = LIST_ENTRY(pos, UdpSession, keepAliveItem);
  135. delete p;
  136. }
  137. mysql_close(&mysql);
  138. close(sock);
  139. }
  140. bool UdpSession::onReceive()
  141. {
  142. UdpInPacket in;
  143. int n = in.recv(sock);
  144. if (n < 0)
  145. return false;
  146. if (n < (int) sizeof(UDP_HEADER)) {
  147. cout << "packet size is less than " << sizeof(UDP_HEADER) << endl;
  148. return false;
  149. }
  150. uint16 ver = in.getVersion();
  151. if (ver > MYICQ_UDP_VER) {
  152. cout << "packet v" << ver << " not supported." << endl;
  153. return false;
  154. }
  155. uint16 cmd = in.getCmd();
  156. uint32 uin = in.getUIN();
  157. uint32 ip = in.getIP();
  158. uint16 port = in.getPort();
  159. UdpSession *session = NULL;
  160. if (cmd == UDP_NEW_UIN)
  161. session = SessionHash::getDead(ip, port);
  162. else if (cmd != UDP_LOGIN) {
  163. session = SessionHash::getAlive(uin);
  164. if (!session && cmd == UDP_ACK)
  165. session = SessionHash::getDead(ip, port);
  166. if (!session)
  167. return false;
  168. }
  169. if (!session) {
  170. session = new UdpSession;
  171. if (!session) {
  172. cout << "create session failed." << endl;
  173. return false;
  174. }
  175. }
  176. session->onReceive(in);
  177. return true;
  178. }
  179. time_t UdpSession::checkSendQueue()
  180. {
  181. IcqListItem *pos;
  182. UdpOutPacket *p;
  183. UdpSession *session;
  184. IcqListItem *head = &globalSendQueue.head;
  185. time_t now = time(NULL);
  186. while ((pos = head->next) != head) {
  187. p = LIST_ENTRY(head->next, UdpOutPacket, globalSendItem);
  188. if (p->expire > now)
  189. return (p->expire - now);
  190. session = p->session;
  191. cout << "packet " << p->getSeq() << " time out." << endl;
  192. p->attempts++;
  193. if (p->attempts <= MAX_SEND_ATTEMPTS) {
  194. cout << "retrasmit packet." << endl;
  195. pos->remove();
  196. p->expire = now + SEND_TIMEOUT;
  197. session->sendDirect(p);
  198. globalSendQueue.add(pos);
  199. }
  200. else {
  201. cout << "maximum attempts reached. delete it!" << endl;
  202. p->sendItem.remove();
  203. p->globalSendItem.remove();
  204. delete p;
  205. }
  206. }
  207. return SEND_TIMEOUT;
  208. }
  209. time_t UdpSession::checkKeepAlive()
  210. {
  211. IcqListItem *pos;
  212. UdpSession *p;
  213. IcqListItem *head = &keepAliveList.head;
  214. time_t now = time(NULL);
  215. while ((pos = head->next) != head) {
  216. p = LIST_ENTRY(pos, UdpSession, keepAliveItem);
  217. if (p->expire > now)
  218. return (p->expire - now);
  219. cout << p->uin << " expires." << endl;
  220. if (p->status != STATUS_OFFLINE)
  221. p->dead();
  222. delete p;
  223. }
  224. return KEEPALIVE_TIMEOUT;
  225. }
  226. void UdpSession::broadcastMessages()
  227. {
  228. if (broadMsgList.isEmpty())
  229. return;
  230. BROADCAST_MSG *msg = LIST_ENTRY(broadMsgList.getHead(), BROADCAST_MSG, listItem);
  231. for (int i = 0; i < 10; ++i) {
  232. UdpSession *s = SessionHash::getAlive(msg->to);
  233. if (s)
  234. sendMessage(msg->type, msg->to, msg->src, s, msg->when, msg->text);
  235. else {
  236. int n = sprintf(sqlStmt, "INSERT INTO broadmsg_tbl VALUES(%lu, %lu)", msg->to, msg->id);
  237. mysql_real_query(&mysql, sqlStmt, n);
  238. }
  239. msg->to++;
  240. if (msg->to > msg->maxUIN) {
  241. msg->listItem.remove();
  242. delete msg;
  243. break;
  244. }
  245. }
  246. }
  247. void UdpSession::addFriend(uint32 dst, uint32 src, UdpSession *dstSession, UdpSession *srcSession)
  248. {
  249. int n = sprintf(sqlStmt, "INSERT INTO friend_tbl values(%lu, %lu)", src, dst);
  250. mysql_real_query(&mysql, sqlStmt, n);
  251. if (!srcSession)
  252. return;
  253. if (dstSession && dstSession->status != STATUS_INVIS) {
  254. UdpOutPacket *out = srcSession->createPacket(UDP_SRV_USER_ONLINE);
  255. out->write32(dst);
  256. out->write32(dstSession->status);
  257. out->write32(dstSession->ip);
  258. out->write16(dstSession->port);
  259. out->write32(dstSession->realIP);
  260. srcSession->sendPacket(out);
  261. }
  262. }
  263. void UdpSession::onlineNotify()
  264. {
  265. MYSQL_RES *res;
  266. MYSQL_ROW row;
  267. int n = sprintf(sqlStmt, "SELECT uin1 FROM friend_tbl WHERE uin2=%lu", uin);
  268. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  269. while ((row = mysql_fetch_row(res))) {
  270. uint32 friendUIN = atol(row[0]);
  271. UdpSession *session = SessionHash::getAlive(friendUIN);
  272. if (session) {
  273. UdpOutPacket *out = session->createPacket(UDP_SRV_USER_ONLINE);
  274. out->write32(uin);
  275. out->write32(status);
  276. out->write32(ip);
  277. out->write16(port);
  278. out->write32(realIP);
  279. session->sendPacket(out);
  280. }
  281. }
  282. mysql_free_result(res);
  283. }
  284. }
  285. void UdpSession::offlineNotify()
  286. {
  287. MYSQL_RES *res;
  288. MYSQL_ROW row;
  289. int n = sprintf(sqlStmt, "SELECT uin1 FROM friend_tbl WHERE uin2=%lu", uin);
  290. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  291. while ((row = mysql_fetch_row(res))) {
  292. uint32 friendUIN = atol(row[0]);
  293. UdpSession *session = SessionHash::getAlive(friendUIN);
  294. if (session) {
  295. UdpOutPacket *out = session->createPacket(UDP_SRV_USER_OFFLINE);
  296. out->write32(uin);
  297. session->sendPacket(out);
  298. }
  299. }
  300. mysql_free_result(res);
  301. }
  302. }
  303. UdpSession::UdpSession()
  304. {
  305. udpVer = 0;
  306. tcpVer = 0;
  307. sid = 0;
  308. uin = 0;
  309. auth = 0;
  310. ip = realIP = 0;
  311. port = 0;
  312. status = STATUS_OFFLINE;
  313. sendSeq = rand() & 0x7fff;
  314. recvSeq = 0;
  315. window = 0;
  316. expire = time(NULL) + KEEPALIVE_TIMEOUT;
  317. sessionCount++;
  318. }
  319. UdpSession::~UdpSession()
  320. {
  321. IcqListItem *head = &sendQueue.head;
  322. IcqListItem *pos;
  323. while ((pos = head->next) != head) {
  324. UdpOutPacket *p = LIST_ENTRY(pos, UdpOutPacket, sendItem);
  325. pos->remove();
  326. p->globalSendItem.remove();
  327. delete p;
  328. }
  329. listItem.remove();
  330. keepAliveItem.remove();
  331. sessionCount--;
  332. }
  333. UdpOutPacket *UdpSession::createPacket(uint16 cmd, uint16 ackSeq)
  334. {
  335. UdpOutPacket *out = new UdpOutPacket(this);
  336. createPacket(*out, cmd, ackSeq);
  337. return out;
  338. }
  339. void UdpSession::createPacket(UdpOutPacket &out, uint16 cmd, uint16 ackSeq)
  340. {
  341. out.write16(udpVer);
  342. out.write32(0);
  343. out.write32(sid);
  344. out.write16(++sendSeq);
  345. out.write16(ackSeq);
  346. out.write16(cmd);
  347. out.write32(uin);
  348. }
  349. void UdpSession::sendAckPacket(uint16 cmd, uint16 seq)
  350. {
  351. UdpOutPacket out(this);
  352. out.write16(udpVer);
  353. out.write32(0);
  354. out.write32(sid);
  355. out.write16(0);
  356. out.write16(seq);
  357. out.write16(cmd);
  358. out.write32(uin);
  359. sendDirect(&out);
  360. }
  361. void UdpSession::sendPacket(UdpOutPacket *p)
  362. {
  363. p->attempts = 0;
  364. p->expire = time(NULL) + SEND_TIMEOUT;
  365. sendDirect(p);
  366. sendQueue.add(&p->sendItem);
  367. globalSendQueue.add(&p->globalSendItem);
  368. }
  369. bool UdpSession::setWindow(uint16 seq)
  370. {
  371. if (seq >= recvSeq + 32 || seq < recvSeq)
  372. return false;
  373. if (seq == recvSeq) {
  374. do {
  375. recvSeq++;
  376. window >>= 1;
  377. } while (window & 0x1);
  378. } else {
  379. uint32 mask = (1 << (seq - recvSeq));
  380. if (window & mask)
  381. return false;
  382. else
  383. window |= mask;
  384. }
  385. return true;
  386. }
  387. bool UdpSession::onReceive(UdpInPacket &in)
  388. {
  389. uint16 cmd = in.getCmd();
  390. uint16 seq = in.getSeq();
  391. if (cmd != UDP_LOGIN && cmd != UDP_NEW_UIN) {
  392. if (sid != in.getSID() || uin != in.getUIN()) {
  393. cout << "packet does not belong to this session." << endl;
  394. return false;
  395. }
  396. in.decrypt(passwd);
  397. } else if (sid == 0) {
  398. udpVer = in.getVersion();
  399. ip = in.getIP();
  400. port = in.getPort();
  401. recvSeq = seq;
  402. sid = in.getSID();
  403. }
  404. if (cmd != UDP_ACK && !setWindow(seq)) {
  405. cout << "packet " << seq << " is duplicated" << endl;
  406. sendAckPacket(cmd, seq);
  407. return false;
  408. }
  409. switch (cmd) {
  410. case UDP_ACK:
  411. onAck(seq);
  412. return true;
  413. case UDP_KEEPALIVE:
  414. onKeepAlive(in);
  415. break;
  416. case UDP_NEW_UIN:
  417. onNewUIN(in);
  418. break;
  419. case UDP_GET_CONTACTLIST:
  420. onGetContactList(in);
  421. break;
  422. case UDP_LOGIN:
  423. onLogin(in);
  424. break;
  425. case UDP_LOGOUT:
  426. onLogout(in);
  427. return true;
  428. case UDP_CHANGE_STATUS:
  429. sendAckPacket(cmd, seq);
  430. onChangeStatus(in);
  431. break;
  432. case UDP_UPDATE_CONTACT:
  433. onUpdateContact(in);
  434. break;
  435. case UDP_UPDATE_USER:
  436. onUpdateUser(in);
  437. break;
  438. case UDP_MODIFY_USER:
  439. sendAckPacket(cmd, seq);
  440. onModifyUser(in);
  441. break;
  442. case UDP_SEND_MSG:
  443. sendAckPacket(cmd, seq);
  444. onSendMessage(in);
  445. break;
  446. case UDP_GROUP_SEND_MSG:
  447. sendAckPacket(cmd, seq);
  448. //onGroupSendMessage(in);
  449. break;
  450. case UDP_SEARCH_RANDOM:
  451. onSearchRandom(in);
  452. break;
  453. case UDP_SEARCH_CUSTOM:
  454. onSearchCustom(in);
  455. break;
  456. case UDP_ADD_FRIEND:
  457. onAddFriend(in);
  458. break;
  459. case UDP_DEL_FRIEND:
  460. sendAckPacket(cmd, seq);
  461. onDelFriend(in);
  462. break;
  463. case UDP_BROADCAST_MSG:
  464. sendAckPacket(cmd, seq);
  465. onBroadcastMsg(in);
  466. break;
  467. default:
  468. cerr << "unknown cmd " << cmd << endl;
  469. return false;
  470. }
  471. return true;
  472. }
  473. void UdpSession::onAck(uint16 seq)
  474. {
  475. IcqListItem *head = &sendQueue.head;
  476. IcqListItem *pos;
  477. UdpOutPacket *p;
  478. LIST_FOR_EACH(pos, head) {
  479. p = LIST_ENTRY(pos, UdpOutPacket, sendItem);
  480. if (p->getSeq() == seq) {
  481. cout << "packet " << seq << " is ACKed" << endl;
  482. pos->remove();
  483. p->globalSendItem.remove();
  484. delete p;
  485. if (status == STATUS_OFFLINE && sendQueue.isEmpty()) {
  486. cout << "session is dead!" << endl;
  487. delete this;
  488. }
  489. return;
  490. }
  491. }
  492. cout << "ACK packet(seq = " << seq << ") is ignored" << endl;
  493. }
  494. void UdpSession::onNewUIN(UdpInPacket &in)
  495. {
  496. const char *pass = in.readString();
  497. int n = sprintf(sqlStmt, "INSERT INTO basic_tbl (passwd) VALUES(password('%s'))", pass);
  498. if (mysql_real_query(&mysql, sqlStmt, n) == 0) {
  499. uin = (uint32) mysql_insert_id(&mysql);
  500. n = sprintf(sqlStmt, "INSERT INTO ext_tbl (uin) VALUES(%lu)", uin);
  501. mysql_real_query(&mysql, sqlStmt, n);
  502. }
  503. UdpOutPacket *out = createPacket(UDP_NEW_UIN, in.getSeq());
  504. out->write32(uin);
  505. sendPacket(out);
  506. keepAliveList.add(&keepAliveItem);
  507. SessionHash::addDead(this);
  508. cout << uin << " has registered." << endl;
  509. }
  510. void UdpSession::onGetContactList(UdpInPacket &in)
  511. {
  512. MYSQL_RES *res;
  513. MYSQL_ROW row;
  514. int n = sprintf(sqlStmt, "SELECT uin2 FROM friend_tbl WHERE uin1=%lu", uin);
  515. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  516. uint16 num = 0;
  517. UdpOutPacket *out = createPacket(UDP_GET_CONTACTLIST, in.getSeq());
  518. char *old = out->skip(sizeof(num));
  519. while ((row = mysql_fetch_row(res))) {
  520. out->write32(atol(row[0]));
  521. num++;
  522. }
  523. mysql_free_result(res);
  524. old = out->setCursor(old);
  525. out->write16(num);
  526. out->setCursor(old);
  527. sendPacket(out);
  528. }
  529. }
  530. void UdpSession::onLogin(UdpInPacket &in)
  531. {
  532. uint8 error = LOGIN_INVALID_UIN;
  533. UdpOutPacket *out;
  534. uint32 friendUIN;
  535. MYSQL_RES *res;
  536. MYSQL_ROW row;
  537. uin = in.getUIN();
  538. const char *pass = in.readString();
  539. status = in.read32();
  540. tcpVer = in.read16();
  541. realIP = in.read32();
  542. int n = sprintf(sqlStmt, "SELECT auth FROM basic_tbl WHERE uin=%lu AND passwd=password('%s')", uin, pass);
  543. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  544. row = mysql_fetch_row(res);
  545. if (row) {
  546. auth = atoi(row[0]);
  547. error = LOGIN_SUCCESS;
  548. }
  549. mysql_free_result(res);
  550. }
  551. out = createPacket(UDP_LOGIN, in.getSeq());
  552. out->write8(error);
  553. sendPacket(out);
  554. keepAliveList.add(&keepAliveItem);
  555. if (error != LOGIN_SUCCESS) {
  556. cout << uin << " login failed." << endl;
  557. SessionHash::addDead(this);
  558. return;
  559. }
  560. strncpy(passwd, pass, 8);
  561. cout << uin << " successfully logged in." << endl;
  562. UdpSession *session = SessionHash::getAlive(uin);
  563. if (session) {
  564. cout << "previous session exists, delete it." << endl;
  565. delete session;
  566. session = NULL;
  567. }
  568. SessionHash::addAlive(this);
  569. // inform me of my friends' status.
  570. n = sprintf(sqlStmt, "SELECT uin2 FROM friend_tbl WHERE uin1=%lu", uin);
  571. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  572. uint16 num = 0;
  573. out = createPacket(UDP_SRV_MULTI_ONLINE);
  574. char *old = out->skip(sizeof(num));
  575. while ((row = mysql_fetch_row(res))) {
  576. friendUIN = atol(row[0]);
  577. UdpSession *session = SessionHash::getAlive(friendUIN);
  578. if (session && session->status != STATUS_INVIS) {
  579. num++;
  580. out->write32(friendUIN);
  581. out->write32(session->status);
  582. out->write32(session->ip);
  583. out->write16(session->port);
  584. out->write32(session->realIP);
  585. cout << "friend " << friendUIN << " is online" << endl;
  586. }
  587. }
  588. if (num) {
  589. old = out->setCursor(old);
  590. out->write16(num);
  591. out->setCursor(old);
  592. sendPacket(out);
  593. } else {
  594. sendSeq--;
  595. delete out;
  596. }
  597. mysql_free_result(res);
  598. }
  599. // I am now online!
  600. if (status == STATUS_OFFLINE)
  601. status = STATUS_ONLINE;
  602. if (status != STATUS_INVIS)
  603. onlineNotify();
  604. // Broadcast messages
  605. n = sprintf(sqlStmt, "SELECT src, type, time, msg FROM broadmsg_tbl a, broadmsg_content_tbl b "
  606. "WHERE dst=%lu AND a.id=b.id", uin);
  607. sendMessages(sqlStmt, n);
  608. // Has someone sent me messages during offline?
  609. n = sprintf(sqlStmt, "SELECT src, type, time, msg FROM message_tbl WHERE dst=%lu", uin);
  610. sendMessages(sqlStmt, n);
  611. }
  612. void UdpSession::sendMessages(const char *sql, int n)
  613. {
  614. MYSQL_RES *res;
  615. MYSQL_ROW row;
  616. if (mysql_real_query(&mysql, sql, n) == 0 && (res = mysql_store_result(&mysql))) {
  617. while ((row = mysql_fetch_row(res))) {
  618. uint32 friendUIN = atol(row[0]);
  619. uint8 type = atoi(row[1]);
  620. time_t sendTime = atol(row[2]);
  621. const char *text = row[3];
  622. UdpOutPacket *out = createPacket(UDP_SRV_MESSAGE);
  623. out->write8(type);
  624. out->write32(friendUIN);
  625. out->write32(sendTime);
  626. out->writeString(text);
  627. sendPacket(out);
  628. }
  629. mysql_free_result(res);
  630. }
  631. }
  632. void UdpSession::sendMessage(uint8 type, uint32 dst, uint32 src, UdpSession *dstSession, time_t when, const char *text)
  633. {
  634. if (dstSession) {
  635. UdpOutPacket *out = dstSession->createPacket(UDP_SRV_MESSAGE);
  636. out->write8(type);
  637. out->write32(src);
  638. out->write32(when);
  639. out->writeString(text);
  640. dstSession->sendPacket(out);
  641. } else {
  642. int n = sprintf(sqlStmt, "INSERT INTO message_tbl values(%lu, %lu, %d, %lu, '%s')",
  643. dst, src, (int) type, when, text);
  644. mysql_real_query(&mysql, sqlStmt, n);
  645. }
  646. }
  647. void UdpSession::onUpdateContact(UdpInPacket &in)
  648. {
  649. uint32 dst = in.read32();
  650. MYSQL_RES *res;
  651. MYSQL_ROW row;
  652. int n = sprintf(sqlStmt, "SELECT pic, nick, age, gender, country, province, city, email,"
  653. "address, zipcode, tel, name, blood, college, profession, homepage, intro "
  654. "FROM basic_tbl a, ext_tbl b WHERE a.uin=%lu AND a.uin=b.uin", dst);
  655. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  656. row = mysql_fetch_row(res);
  657. if (row) {
  658. UdpOutPacket *out = createPacket(UDP_UPDATE_CONTACT, in.getSeq());
  659. out->write32(dst);
  660. out->write8(atoi(row[0]));
  661. out->writeString(row[1]);
  662. out->write8(atoi(row[2]));
  663. out->write8(atoi(row[3]));
  664. out->writeString(row[4]);
  665. out->writeString(row[5]);
  666. out->writeString(row[6]);
  667. out->writeString(row[7]);
  668. out->writeString(row[8]);
  669. out->writeString(row[9]);
  670. out->writeString(row[10]);
  671. out->writeString(row[11]);
  672. out->write8(atoi(row[12]));
  673. out->writeString(row[13]);
  674. out->writeString(row[14]);
  675. out->writeString(row[15]);
  676. out->writeString(row[16]);
  677. sendPacket(out);
  678. }
  679. mysql_free_result(res);
  680. }
  681. }
  682. void UdpSession::onUpdateUser(UdpInPacket &in)
  683. {
  684. MYSQL_RES *res;
  685. MYSQL_ROW row;
  686. int n = sprintf(sqlStmt, "SELECT pic, nick, age, gender, country, province, city, email, "
  687. "address, zipcode, tel, name, blood, college, profession, homepage, intro "
  688. "FROM basic_tbl a, ext_tbl b WHERE a.uin=%lu AND a.uin=b.uin", uin);
  689. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  690. row = mysql_fetch_row(res);
  691. if (row) {
  692. UdpOutPacket *out = createPacket(UDP_UPDATE_USER, in.getSeq());
  693. out->write8(atoi(row[0]));
  694. out->writeString(row[1]);
  695. out->write8(atoi(row[2]));
  696. out->write8(atoi(row[3]));
  697. out->writeString(row[4]);
  698. out->writeString(row[5]);
  699. out->writeString(row[6]);
  700. out->writeString(row[7]);
  701. out->writeString(row[8]);
  702. out->writeString(row[9]);
  703. out->writeString(row[10]);
  704. out->writeString(row[11]);
  705. out->write8(atoi(row[12]));
  706. out->writeString(row[13]);
  707. out->writeString(row[14]);
  708. out->writeString(row[15]);
  709. out->writeString(row[16]);
  710. out->write8(auth);
  711. sendPacket(out);
  712. }
  713. mysql_free_result(res);
  714. }
  715. }
  716. void UdpSession::onModifyUser(UdpInPacket &in)
  717. {
  718. int pic = in.read8();
  719. const char *nick = in.readString();
  720. int age = in.read8();
  721. int gender = in.read8();
  722. const char *country = in.readString();
  723. const char *province = in.readString();
  724. const char *city = in.readString();
  725. const char *email = in.readString();
  726. const char *address = in.readString();
  727. const char *zipcode = in.readString();
  728. const char *tel = in.readString();
  729. const char *name = in.readString();
  730. int blood = in.read8();
  731. const char *college = in.readString();
  732. const char *profession = in.readString();
  733. const char *homepage = in.readString();
  734. const char *intro = in.readString();
  735. auth = in.read8();
  736. uint8 modifyPasswd = in.read8();
  737. char *p = sqlStmt;
  738. p += sprintf(p, "UPDATE basic_tbl SET ");
  739. if (modifyPasswd) {
  740. const char *pass = in.readString();
  741. if (*pass)
  742. p += sprintf(p, "passwd=password('%s'), ", pass);
  743. }
  744. p += sprintf(p, "nick='%s', pic=%d, gender=%d, age=%d, country='%s', province='%s', city='%s', email='%s', auth=%d "
  745. "WHERE uin=%lu", nick, pic, gender, age, country, province, city, email, auth, uin);
  746. int n = p - sqlStmt;
  747. mysql_real_query(&mysql, sqlStmt, n);
  748. n = sprintf(sqlStmt, "UPDATE ext_tbl SET "
  749. "address='%s', zipcode='%s', tel='%s', "
  750. "name='%s', blood=%d, college='%s', profession='%s', homepage='%s', intro='%s' WHERE uin=%lu",
  751. address, zipcode, tel, name, blood, college, profession, homepage, intro, uin);
  752. mysql_real_query(&mysql, sqlStmt, n);
  753. }
  754. void UdpSession::dead()
  755. {
  756. if (status != STATUS_INVIS)
  757. offlineNotify();
  758. status = STATUS_OFFLINE;
  759. }
  760. void UdpSession::onLogout(UdpInPacket &in)
  761. {
  762. cout << uin << " logged out." << endl;
  763. dead();
  764. if (sendQueue.isEmpty()) {
  765. // delete all offline messages
  766. int n = sprintf(sqlStmt, "DELETE FROM message_tbl WHERE dst=%lu", uin);
  767. mysql_real_query(&mysql, sqlStmt, n);
  768. n = sprintf(sqlStmt, "DELETE FROM broadmsg_tbl WHERE dst=%lu", uin);
  769. mysql_real_query(&mysql, sqlStmt, n);
  770. }
  771. delete this;
  772. }
  773. void UdpSession::onChangeStatus(UdpInPacket &in)
  774. {
  775. uint32 oldStatus = status;
  776. status = in.read32();
  777. if (oldStatus == STATUS_INVIS && status != STATUS_INVIS)
  778. onlineNotify();
  779. else if (oldStatus != STATUS_INVIS && status == STATUS_INVIS)
  780. offlineNotify();
  781. else if (status != STATUS_INVIS) {
  782. MYSQL_RES *res;
  783. MYSQL_ROW row;
  784. int n = sprintf(sqlStmt, "SELECT uin1 FROM friend_tbl WHERE uin2=%lu", uin);
  785. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  786. while ((row = mysql_fetch_row(res))) {
  787. uint32 friendUIN = atol(row[0]);
  788. UdpSession *session = SessionHash::getAlive(friendUIN);
  789. if (session) {
  790. UdpOutPacket *out = session->createPacket(UDP_SRV_STATUS_CHANGED);
  791. out->write32(uin);
  792. out->write32(status);
  793. session->sendPacket(out);
  794. }
  795. }
  796. mysql_free_result(res);
  797. }
  798. }
  799. }
  800. void UdpSession::onKeepAlive(UdpInPacket &in)
  801. {
  802. cout << uin << " keeps alive" << endl;
  803. expire = time(NULL) + KEEPALIVE_TIMEOUT;
  804. keepAliveItem.remove();
  805. keepAliveList.add(&keepAliveItem);
  806. UdpOutPacket out(this);
  807. createPacket(out, UDP_KEEPALIVE, in.getSeq());
  808. out.write32(sessionCount);
  809. sendDirect(&out);
  810. }
  811. void UdpSession::onSendMessage(UdpInPacket &in)
  812. {
  813. uint8 type = in.read8();
  814. uint32 to = in.read32();
  815. time_t when = in.read32();
  816. const char *text = in.readString();
  817. UdpSession *session = SessionHash::getAlive(to);
  818. sendMessage(type, to, uin, session, when, text);
  819. if (type == MSG_AUTH_ACCEPTED)
  820. addFriend(uin, to, this, session);
  821. }
  822. void UdpSession::onBroadcastMsg(UdpInPacket &in)
  823. {
  824. if (uin >= START_UIN)
  825. return;
  826. uint8 type = in.read8();
  827. uint32 when = in.read32();
  828. time_t expire = in.read32();
  829. const char *text = in.readString();
  830. if (expire <= time(NULL) || !*text)
  831. return;
  832. int n = sprintf(sqlStmt, "INSERT INTO broadmsg_content_tbl "
  833. "VALUES(NULL, %lu, %d, %lu, %lu, '%s')", uin, type, when, expire, text);
  834. mysql_real_query(&mysql, sqlStmt, n);
  835. BROADCAST_MSG *msg = new BROADCAST_MSG;
  836. msg->id = (uint32) mysql_insert_id(&mysql);
  837. msg->type = type;
  838. msg->src = uin;
  839. msg->when = when;
  840. strncpy(msg->text, text, MAX_MSG_LEN);
  841. msg->expire = expire;
  842. msg->to = START_UIN;
  843. MYSQL_RES *res;
  844. MYSQL_ROW row;
  845. static const char sql[] = "SELECT MAX(uin) FROM basic_tbl";
  846. if (mysql_real_query(&mysql, sql, sizeof(sql) - 1) == 0 && (res = mysql_store_result(&mysql))) {
  847. row = mysql_fetch_row(res);
  848. if (row)
  849. msg->maxUIN = atoi(row[0]);
  850. mysql_free_result(res);
  851. }
  852. broadMsgList.add(&msg->listItem);
  853. }
  854. void UdpSession::onGroupSendMessage(UdpInPacket &in)
  855. {
  856. uint8 type = in.read8();
  857. time_t when = in.read32();
  858. const char *text = in.readString();
  859. int n = in.read16();
  860. while (n-- > 0) {
  861. uint32 to = in.read32();
  862. UdpSession *session = SessionHash::getAlive(to);
  863. sendMessage(type, to, uin, session, when, text);
  864. }
  865. }
  866. void UdpSession::onSearchRandom(UdpInPacket &in)
  867. {
  868. uint32 results[MAX_SEARCH_PER_PAGE];
  869. uint16 n = SessionHash::random(results, MAX_SEARCH_PER_PAGE);
  870. UdpOutPacket *out = createPacket(UDP_SRV_SEARCH, in.getSeq());
  871. out->write16(n);
  872. if (n > 0) {
  873. MYSQL_RES *res;
  874. MYSQL_ROW row;
  875. static const char sql[] = "SELECT uin, pic, nick, province FROM basic_tbl WHERE uin IN(";
  876. memcpy(sqlStmt, sql, sizeof(sql));
  877. char *p = sqlStmt + sizeof(sql) - 1;
  878. for (int i = 0; i < n; i++)
  879. p += sprintf(p, "%lu,", results[i]);
  880. *(p - 1) = ')';
  881. if (mysql_real_query(&mysql, sqlStmt, p - sqlStmt) == 0 &&
  882. (res = mysql_store_result(&mysql))) {
  883. while ((row = mysql_fetch_row(res))) {
  884. uint32 contactUIN = atoi(row[0]);
  885. uint8 pic = atoi(row[1]);
  886. const char *nick = row[2];
  887. const char *province = row[3];
  888. out->write32(contactUIN);
  889. out->write8(1); // online
  890. out->write8(pic);
  891. out->writeString(nick);
  892. out->writeString(province);
  893. }
  894. mysql_free_result(res);
  895. }
  896. }
  897. sendPacket(out);
  898. }
  899. void UdpSession::onSearchCustom(UdpInPacket &in)
  900. {
  901. uint32 uin = in.read32();
  902. const char *nick = in.readString();
  903. const char *email = in.readString();
  904. uint32 startUIN = in.read32();
  905. int n;
  906. if (uin != 0) {
  907. n = sprintf(sqlStmt, "SELECT uin, pic, nick, province FROM basic_tbl WHERE uin=%lu LIMIT %d",
  908. uin, MAX_SEARCH_PER_PAGE);
  909. } else if (*nick || *email) {
  910. char *p = sqlStmt;
  911. p += sprintf(p, "SELECT uin, pic, nick, province FROM basic_tbl WHERE uin>%lu AND ", startUIN);
  912. if (*nick)
  913. p += sprintf(p, "nick='%s'", nick);
  914. if (*email) {
  915. if (*nick)
  916. p += sprintf(p, " AND ");
  917. p += sprintf(p, "email='%s'", email);
  918. }
  919. p += sprintf(p, " LIMIT %d", MAX_SEARCH_PER_PAGE);
  920. n = p - sqlStmt;
  921. } else {
  922. cout << "unknown search mode" << endl;
  923. return;
  924. }
  925. MYSQL_RES *res;
  926. MYSQL_ROW row;
  927. UdpOutPacket *out;
  928. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  929. uint16 num = 0;
  930. out = createPacket(UDP_SRV_SEARCH, in.getSeq());
  931. char *old = out->skip(sizeof(num));
  932. while ((row = mysql_fetch_row(res))) {
  933. uint32 contactUIN = atoi(row[0]);
  934. uint8 pic = atoi(row[1]);
  935. const char *nick = row[2];
  936. const char *province = row[3];
  937. out->write32(contactUIN);
  938. out->write8(SessionHash::getAlive(contactUIN) ? 1 : 0);
  939. out->write8(pic);
  940. out->writeString(nick);
  941. out->writeString(province);
  942. num++;
  943. }
  944. mysql_free_result(res);
  945. old = out->setCursor(old);
  946. out->write16(num);
  947. out->setCursor(old);
  948. sendPacket(out);
  949. }
  950. }
  951. void UdpSession::onAddFriend(UdpInPacket &in)
  952. {
  953. uint32 dst = in.read32();
  954. uint8 dstAuth = 0;
  955. UdpSession *dstSession = SessionHash::getAlive(dst);
  956. if (dstSession)
  957. dstAuth = dstSession->auth;
  958. else {
  959. MYSQL_RES *res;
  960. MYSQL_ROW row;
  961. int n = sprintf(sqlStmt, "SELECT auth FROM basic_tbl WHERE uin=%lu", dst);
  962. if (mysql_real_query(&mysql, sqlStmt, n) == 0 && (res = mysql_store_result(&mysql))) {
  963. row = mysql_fetch_row(res);
  964. if (row)
  965. dstAuth = atoi(row[0]);
  966. mysql_free_result(res);
  967. }
  968. }
  969. UdpOutPacket *out = createPacket(UDP_ADD_FRIEND, in.getSeq());
  970. out->write32(dst);
  971. out->write8(dstAuth);
  972. sendPacket(out);
  973. if (dstAuth == ADD_FRIEND_ACCEPTED) {
  974. addFriend(dst, uin, dstSession, this);
  975. sendMessage(MSG_ADDED, dst, uin, dstSession, time(NULL), "");
  976. }
  977. }
  978. void UdpSession::onDelFriend(UdpInPacket &in)
  979. {
  980. uint32 dst = in.read32();
  981. int n = sprintf(sqlStmt, "DELETE FROM friend_tbl WHERE uin1=%lu AND uin2=%lu", uin, dst);
  982. mysql_real_query(&mysql, sqlStmt, n);
  983. }