source: chevmsgr/trunk/msgsrv.cpp@ 23

Last change on this file since 23 was 23, checked in by cheese, 9 years ago

bug fix

File size: 12.6 KB
RevLine 
[4]1
2#include "cf/network.h"
3#include "cf/task.h"
4#include "cf/codec.h"
5#include "cf/file.h"
[13]6#include "crypto.h"
[4]7
8#include "msg.hpp"
9
10#include <map>
11#include <stdlib.h>
12#include <time.h>
13
14#include "sqlite3.h"
15
16// --------------------------------------------------------------
17
[17]18std::map<std::string, SecureSocket> gOnlineUsers;
[14]19std::map<std::string, std::vector<std::string> > gSessionMap;
[4]20
[14]21// --------------------------------------------------------------
[4]22
23int cb_getFriendList(void * userArg, int argc, char ** argv, char ** colName)
24{
25 std::string * friendList = (std::string *) userArg;
26
27 *friendList = argv[1];
28
29 return 0;
30}
31
32int cb_getSMS(void * userArg, int argc, char ** argv, char ** colName)
33{
[16]34 std::string * sms = (std::string *) userArg;
[4]35
[16]36 for (int i = 0; i < argc; i++)
37 {
[23]38 if (!stricmp(colName[i], "sms"))
[16]39 {
40 *sms = argv[i];
41 break;
42 }
43 }
[4]44
45 return 0;
46}
47
[16]48int cb_exist(void * userArg, int argc, char ** argv, char ** colName)
[4]49{
[16]50 // È£ÃâµÇ¸é Àִ°Å
[4]51 bool * result = (bool *)userArg;
52
53 *result = true;
54
55 return 0;
56}
57
58
59//===============================================================
60
61// --------------------------------------------------------------
62class DBManager
63{
64private:
65 sqlite3 * db;
66
67public:
68 DBManager()
69 throw (cf::exception)
70 : db(NULL)
71 {
[14]72 Init();
[4]73 }
74
75 void Init()
76 throw (cf::exception)
77 {
78 int result = 0;
79 bool isExist = false;
80
81 isExist = cf::file("account.db").exists();
82
83 result = sqlite3_open("account.db", &db);
84 if (result)
85 fprintf(stderr, "sqlite_open failed\n");
86
87 if (!isExist)
88 setup();
89 }
90
91 void setup()
92 throw (cf::exception)
93 {
94 std::string sqlCreateAccountTable;
95 std::string sqlCreateAuthTable;
96 std::string sqlCreateFriendsTable;
97 // declaration
98
99 // query
100 sqlCreateAccountTable =
101 "CREATE TABLE T_ACCOUNT"
102 "("
[16]103 " id TEXT PRIMARY KEY,"
104 " pw TEXT"
[4]105 ")";
106 sqlCreateAuthTable =
107 "CREATE TABLE T_AUTH"
108 "("
[16]109 " id TEXT NOT NULL,"
110 " ip TEXT NOT NULL,"
111 " sms TEXT NOT NULL"
[4]112 ")";
113 sqlCreateFriendsTable =
114 "CREATE TABLE T_FRIENDS"
115 "("
[16]116 " id TEXT NOT NULL,"
117 " friend TEXT NOT NULL"
[4]118 ")";
119
120 exec(sqlCreateAccountTable, NULL, NULL);
121 exec(sqlCreateAuthTable, NULL, NULL);
122 exec(sqlCreateFriendsTable, NULL, NULL);
123 }
124
125 void exec(std::string & query, sqlite3_callback cb, void * userArg)
126 throw(cf::exception)
127 {
[14]128 int result = 0;
129 char * errMsg = NULL;
[4]130
131 result = sqlite3_exec(db, query.c_str(), cb, userArg, &errMsg);
[18]132 LOG("[SQL] " + query);
[4]133
134 if (result != SQLITE_OK)
[14]135 THROW_EXCEPTION("[DBERROR][" << result << "] " << errMsg);
[4]136 }
137
138 bool login(const std::string & id, const std::string & pw)
139 throw (cf::exception)
140 {
141 try
142 {
143 bool result = false;
144 std::string query = "select * from T_ACCOUNT where id ='" + id + "' and pw = '" + pw + "'";
145
[16]146 this->exec(query, cb_exist, &result);
[4]147
148 return result;
149 }
150 catch (cf::exception & e)
151 {
152 FORWARD_EXCEPTION(e);
153 }
154 }
155
156 bool join(const std::string & id, const std::string & pw, const std::string & sms, const std::string & ip)
157 throw (cf::exception)
158 {
159 try
160 {
[16]161 // account
162 bool isExistAccount = false;
163 std::string existAccount = "select * from T_ACCOUNT where id = '" + id + "'";
164 std::string insertAccount = "insert into T_ACCOUNT(id, pw) values('" + id + "', '" + pw + "')";
[4]165
[16]166 // auth
167 bool isExistAuth = false;
168 std::string existAuth = "select * from T_AUTH where id = '" + id + "' and sms = '" + sms + "' and ip = '" + ip + "'";
169 std::string insertAuth = "insert into T_AUTH(id, sms, ip) values( '" + id + "', '" + sms + "' , '" + ip + "')";
[4]170
[16]171 this->exec(existAccount, cb_exist, &isExistAccount);
172
173 if (!isExistAccount)
174 {
175 this->exec(insertAccount, NULL, NULL);
176 this->exec(insertAuth, NULL, NULL);
177 }
178 else
179 {
180 this->exec(existAuth, cb_exist, &isExistAuth);
181 if (!isExistAuth)
182 this->exec(insertAuth, NULL, NULL);
183 }
[4]184 }
185 catch (cf::exception & e)
186 {
187 FORWARD_EXCEPTION(e);
188 }
189
190 return true;
191 }
192
[16]193 std::vector<std::string> getFriendList(std::string & id)
[4]194 throw(cf::exception)
195 {
196 int result = 0;
197 std::string friendList;
198
199 try
200 {
201 std::string query = "select * from T_FRIENDS where id = '" + id + "'";
202
203 this->exec(query, cb_getFriendList, &friendList);
204 }
205 catch (cf::exception & e)
206 {
207 FORWARD_EXCEPTION(e);
208 }
209 }
210
[16]211 std::string getSMS(std::string & id, std::string & ip)
[4]212 {
213 try
214 {
[16]215 std::string sms;
216 std::string query = "select * from T_AUTH where id = '" + id + "' and ip = '" + ip + "'";
[4]217
[16]218 this->exec(query, cb_getSMS, &sms);
219 if (sms.length() == 0)
220 THROW_EXCEPTION("sms was not found for '" + id + "' from '" + ip + "'");
221
222 return sms;
[4]223 }
224 catch (cf::exception & e)
225 {
226 FORWARD_EXCEPTION(e);
227 }
228 }
229
[16]230 bool addFriend(std::string & id, std::string & friendID)
[4]231 {
232 try
233 {
[16]234 std::string query = "insert into T_FRIENDS values('" + id + "', '" + friendID + "')";
[4]235
236 this->exec(query, NULL, NULL);
[16]237
238 return true;
[4]239 }
240 catch (cf::exception & e)
241 {
242 FORWARD_EXCEPTION(e);
[16]243 return false;
[4]244 }
245 }
246};
247DBManager dbmgr;
248
249class Runner
250{
251public:
252 cf::network::tcp * mSock;
253 cf::task::thread * mThread;
254
255 Runner(cf::network::tcp & sock, int (*worker)(void *))
256 : mSock(NULL)
257 , mThread(NULL)
258 {
259 mSock = new(std::nothrow) cf::network::tcp(sock.detach());
260 mThread = new(std::nothrow) cf::task::thread(worker);
261 if (!mSock || !mThread)
262 {
263 dispose();
264 THROW_EXCEPTION("cannot allocate thread arguments");
265 }
266
267 mThread->start(this);
268 }
269
270 ~Runner()
271 {
272 dispose();
273 }
274
275 void dispose()
276 {
277 if (mSock) delete mSock;
278 if (mThread) delete mThread;
279 }
280};
281
282static bool isOnline(const std::string & id)
283{
284 return gOnlineUsers.find(id) != gOnlineUsers.end();
285}
286
287static void logout(const std::string & id)
288{
[18]289 gOnlineUsers[id].close();
[4]290 gOnlineUsers.erase(id);
[14]291 LOG(STR(id << " was logged out"));
[4]292}
293
[14]294static unsigned int generateSeed()
295{
296 unsigned int ret = 0;
297 int t = (int)time(NULL);
298
299 cf::bin b;
300 b.resize(sizeof(int));
301
302 b.set((cf::uint8_t*)&t, sizeof(int));
303 cf::bin s = crypto().sha256(b);
304
305 memcpy(&ret, s.buffer(), sizeof(unsigned int));
306
307 return ret;
308}
309
[20]310static std::string generateSMSCode()
[4]311{
[21]312 return "545454";
[4]313 char random[8] = {0x00,};
[14]314 sprintf(random, "%06d", generateSeed() % 1000000);
[4]315
316 return random;
317}
318
[6]319static std::string httpSMS(const Protocol::Message & parser)
[4]320{
[14]321 std::string phone = parser.get<std::string>(ProtocolType::PHONE);
[4]322
323#define CRLF "\r\n"
[20]324 std::string code = generateSMSCode();
[4]325 std::string url = "unsigned.kr";
326 std::string uri = "/~cheese/sms/req.php?to=" + phone + "&code=" + code;
327 std::string http =
328 "GET " + uri + " HTTP/1.1" CRLF
329 "Host: " + url + CRLF
330 "Accept: text/plain" CRLF
331 CRLF;
332
333 cf::network::tcp smsSock;
334 cf::network::host smsServer(url, 80);
335
336 smsSock.connect(smsServer);
337 smsSock.send(http);
[14]338 smsSock.receive();
[4]339 smsSock.close();
340
341 return code;
342}
343
[18]344static Protocol::Message getSecret(const Protocol::Message & parser, const std::string & sms)
345{
346 std::string secret = parser.get<std::string>(ProtocolType::SECRET);
347 std::string random = parser.get<std::string>(ProtocolType::RANDOM);
348
349 crypto crypt;
350 crypt.setKey(cf::bin(sms + DELIMITER + random));
351
[21]352 std::string auth = crypt.decrypt(cf::codec::hex::getInstance()->decode(secret)).toString();
[20]353
354 if (auth[0] != '{')
355 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
356
357 for (size_t iter = 0; iter < auth.length(); iter++)
358 {
359 if (auth[iter] < ' ' || '~' < auth[iter])
360 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
361 }
362
[18]363 Protocol::Message authParser;
364
365 authParser.parse(auth);
366
367 return authParser;
368}
369
[6]370static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
[4]371 throw (cf::exception)
372{
[20]373 Protocol::Message & authParser = getSecret(parser, sms);
374
375 if (sms != authParser.get<std::string>(ProtocolType::SMS))
[4]376 THROW_EXCEPTION("SMS is not same");
377
[18]378 std::string id = authParser.get<std::string>(ProtocolType::ID);
379 std::string pw = authParser.get<std::string>(ProtocolType::PW);
380 std::string rcvdSMS = authParser.get<std::string>(ProtocolType::SMS);
381
382 if (sms != rcvdSMS)
383 THROW_EXCEPTION("invalid sms code");
[4]384
385 return dbmgr.join(id, pw, sms, address);
386}
387
[16]388static bool login(const Protocol::Message & parser, cf::network::tcp & sock, std::string & ip, std::string sms)
[4]389 throw (cf::exception)
390{
[18]391 Protocol::Message & authParser = getSecret(parser, sms);
[4]392
[18]393 std::string id = authParser.get<std::string>(ProtocolType::ID);
394 std::string pw = authParser.get<std::string>(ProtocolType::PW);
395
[14]396 bool result = dbmgr.login(id, pw);
397 if (result)
[18]398 gOnlineUsers[id] = SecureSocket(&sock, ip, sms);
[16]399
[14]400 return result;
[4]401}
402
[14]403static bool chat(const Protocol::Message & message)
[4]404{
405 bool result = false;
[14]406 Protocol::Message parser = message;
407 std::string sessid = parser.get<std::string>(ProtocolType::SESSION_ID);
408 std::vector<std::string> idList = gSessionMap[sessid];
409 std::string sender = parser.get<std::string>(ProtocolType::ID);
410 parser.mObject[ProtocolType::TYPE] = ProtocolType::LISTEN;
411 std::string serialized = parser.serialize();
[4]412
[14]413 for (size_t iter = 0; iter < idList.size(); iter++)
[4]414 {
[14]415 std::string id = idList[iter];
416
417 if (sender != id && isOnline(id))
[17]418 gOnlineUsers[id].send(serialized);
[14]419
[4]420 result = true;
421 }
422
423 return result;
424}
425
[13]426static std::string createSessionID(std::string & idList)
427{
428 cf::bin sessid;
429
430 sessid = crypto().sha256(cf::bin(idList));
431
432 return cf::codec::hex::getInstance()->encode(sessid);
433}
434
[14]435static bool openSession(const Protocol::Message & message)
[13]436{
[14]437 Protocol::Message parser = message;
[13]438 bool result = false;
439 std::string sessid;
440 std::vector<std::string> idList = parser.getList<std::string>(ProtocolType::ID_LIST);
[14]441 std::string concat = joinStrings(idList);
[13]442
443 sessid = createSessionID(concat);
444
[14]445 parser.mObject[ProtocolType::SESSION_ID] = sessid;
446 std::string serialized = parser.serialize();
447
[13]448 for (size_t iter = 0; iter < idList.size(); iter++)
449 {
[14]450 std::string id = idList[iter];
451 if (isOnline(id))
[17]452 gOnlineUsers[id].send(serialized);
[13]453
454 result = true;
455 }
456
[14]457 gSessionMap[sessid] = idList;
458
[13]459 return result;
460}
461
[16]462static bool addFriend(const Protocol::Message & message)
463{
464 Protocol::Message parser = message;
465 std::string result = "";
466 std::string id = parser.get<std::string>(ProtocolType::ID);
467 std::string friendID = parser.get<std::string>(ProtocolType::FRIEND_ID);
468
469 return dbmgr.addFriend(id, friendID);
470}
471
[18]472static std::string authenticator(cf::network::tcp & sock)
[4]473{
[14]474 Protocol::Message parser;
475
[4]476 try
477 {
478 std::string sms;
[14]479 std::string id;
480 bool loggedIn = false;
[20]481 bool result = false;
[4]482
483 while (true)
484 {
[20]485 result = false;
[4]486 parser.parse(sock.receive().toString());
487
[14]488 if (parser.type() == ProtocolType::SMS)
[4]489 {
490 sms = httpSMS(parser);
[20]491 result = true;
[4]492 }
[14]493 else if (parser.type() == ProtocolType::JOIN)
[4]494 {
[14]495 if (!join(parser, sms, sock.peer().address()))
[13]496 THROW_EXCEPTION("user(" << parser.get<std::string>(ProtocolType::ID) << ") cannot join");
[20]497 result = true;
[4]498 }
[14]499 else if (parser.type() == ProtocolType::LOGIN)
[4]500 {
[16]501 id = parser.get<std::string>(ProtocolType::ID);
[14]502 std::string ip = sock.peer().address();
[16]503 sms = dbmgr.getSMS(id, ip);
[14]504
[16]505 // ½ÇÆÐÇÏ¸é ¾Õ¿¡¼­ exceptionÀ¸·Î ´Ù 󸮵Ê
506 // ¿©±ä ¼º°øÇÒ ¶§¿¡¸¸ ¿È
507 if (login(parser, sock, ip, sms))
[20]508 {
[16]509 loggedIn = true;
[20]510 result = true;
511 }
[14]512 }
513
514 // success
[20]515 sock.send(Protocol::Response().result(parser.type(), result));
[14]516 if (loggedIn)
517 return id;
[4]518 }
519 }
520 catch (cf::exception & e)
521 {
[14]522 sock.send(Protocol::Response().result(parser.type(), false));
[4]523 FORWARD_EXCEPTION(e);
524 }
525}
526
[18]527static int deliverer(void * arg)
[4]528{
529 Runner * runner = reinterpret_cast<Runner *>(arg);
530 cf::network::tcp * sock = runner->mSock;
531
532 std::string id;
533
534 try
535 {
[18]536 id = authenticator(*sock);
[4]537
[6]538 Protocol::Response response;
[4]539 bool result = true;
540
541 while (result)
542 {
[23]543 std::string message = gOnlineUsers[id].receive();
[6]544 Protocol::Message parser;
[4]545 parser.parse(message);
546
547 LOG(message);
548
[14]549 if (parser.type() == ProtocolType::TELL)
550 {
551 result = chat(parser);
552 }
553 else if (parser.type() == ProtocolType::OPEN_SESSION)
554 {
555 result = openSession(parser);
556 }
[16]557 else if (parser.type() == ProtocolType::ADD_FRIEND)
558 {
559 result = addFriend(parser);
560 }
561
[23]562 gOnlineUsers[id].send(response.result(parser.type(), result));
[4]563 }
564 }
565 catch (cf::exception & e)
566 {
[14]567 LOG(e.stackTrace());
[4]568 }
569
570 logout(id);
571 delete runner;
572 return 0;
573}
574
575static int server(unsigned short port)
576{
577 cf::network::tcp sock;
578
579 try
580 {
581 sock.bind(port);
582 sock.listen();
583 }
584 catch(cf::exception & e)
585 {
586 LOG(e.what());
587 return -1;
588 }
589
590 while (true)
591 {
592 try
593 {
594 cf::network::tcp client = sock.accept();
[18]595 Runner * runner = new(std::nothrow) Runner(client, deliverer);
[4]596 if (!runner)
597 THROW_EXCEPTION("cannot create thread argument");
598 }
599 catch(cf::exception & e)
600 {
601 LOG(e.what());
602 }
603 }
604
605 return 0;
606}
607
608int main(int argc, char ** argv)
609{
[20]610 if (argc != 2)
[4]611 {
612 std::cerr << "-_-^" << std::endl;
613 return -1;
614 }
615
616 return server((unsigned short)atoi(argv[1]));
617}
Note: See TracBrowser for help on using the repository browser.