source: chevmsgr/trunk/msgsrv.cpp@ 25

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

UI
친구목록 가져오기

File size: 13.5 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]25 std::vector<std::string> * friendList = (std::vector<std::string> *) userArg;
[4]26
[25]27 friendList->push_back(argv[1]);
[4]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;
[25]197 std::vector<std::string> friendList;
[4]198
199 try
200 {
201 std::string query = "select * from T_FRIENDS where id = '" + id + "'";
202
203 this->exec(query, cb_getFriendList, &friendList);
[25]204
205 return friendList;
[4]206 }
207 catch (cf::exception & e)
208 {
209 FORWARD_EXCEPTION(e);
[25]210 }
[4]211 }
212
[16]213 std::string getSMS(std::string & id, std::string & ip)
[4]214 {
215 try
216 {
[16]217 std::string sms;
218 std::string query = "select * from T_AUTH where id = '" + id + "' and ip = '" + ip + "'";
[4]219
[16]220 this->exec(query, cb_getSMS, &sms);
221 if (sms.length() == 0)
222 THROW_EXCEPTION("sms was not found for '" + id + "' from '" + ip + "'");
223
224 return sms;
[4]225 }
226 catch (cf::exception & e)
227 {
228 FORWARD_EXCEPTION(e);
229 }
230 }
231
[16]232 bool addFriend(std::string & id, std::string & friendID)
[4]233 {
234 try
235 {
[16]236 std::string query = "insert into T_FRIENDS values('" + id + "', '" + friendID + "')";
[4]237
238 this->exec(query, NULL, NULL);
[16]239
240 return true;
[4]241 }
242 catch (cf::exception & e)
243 {
244 FORWARD_EXCEPTION(e);
[16]245 return false;
[4]246 }
247 }
248};
249DBManager dbmgr;
250
251class Runner
252{
253public:
254 cf::network::tcp * mSock;
255 cf::task::thread * mThread;
256
257 Runner(cf::network::tcp & sock, int (*worker)(void *))
258 : mSock(NULL)
259 , mThread(NULL)
260 {
261 mSock = new(std::nothrow) cf::network::tcp(sock.detach());
262 mThread = new(std::nothrow) cf::task::thread(worker);
263 if (!mSock || !mThread)
264 {
265 dispose();
266 THROW_EXCEPTION("cannot allocate thread arguments");
267 }
268
269 mThread->start(this);
270 }
271
272 ~Runner()
273 {
274 dispose();
275 }
276
277 void dispose()
278 {
279 if (mSock) delete mSock;
280 if (mThread) delete mThread;
281 }
282};
283
284static bool isOnline(const std::string & id)
285{
286 return gOnlineUsers.find(id) != gOnlineUsers.end();
287}
288
289static void logout(const std::string & id)
290{
[18]291 gOnlineUsers[id].close();
[4]292 gOnlineUsers.erase(id);
[14]293 LOG(STR(id << " was logged out"));
[4]294}
295
[14]296static unsigned int generateSeed()
297{
298 unsigned int ret = 0;
299 int t = (int)time(NULL);
300
301 cf::bin b;
302 b.resize(sizeof(int));
303
304 b.set((cf::uint8_t*)&t, sizeof(int));
305 cf::bin s = crypto().sha256(b);
306
307 memcpy(&ret, s.buffer(), sizeof(unsigned int));
308
309 return ret;
310}
311
[20]312static std::string generateSMSCode()
[4]313{
[21]314 return "545454";
[4]315 char random[8] = {0x00,};
[14]316 sprintf(random, "%06d", generateSeed() % 1000000);
[4]317
318 return random;
319}
320
[6]321static std::string httpSMS(const Protocol::Message & parser)
[4]322{
[14]323 std::string phone = parser.get<std::string>(ProtocolType::PHONE);
[4]324
325#define CRLF "\r\n"
[20]326 std::string code = generateSMSCode();
[4]327 std::string url = "unsigned.kr";
328 std::string uri = "/~cheese/sms/req.php?to=" + phone + "&code=" + code;
329 std::string http =
330 "GET " + uri + " HTTP/1.1" CRLF
331 "Host: " + url + CRLF
332 "Accept: text/plain" CRLF
333 CRLF;
334
335 cf::network::tcp smsSock;
336 cf::network::host smsServer(url, 80);
337
338 smsSock.connect(smsServer);
339 smsSock.send(http);
[14]340 smsSock.receive();
[4]341 smsSock.close();
342
343 return code;
344}
345
[18]346static Protocol::Message getSecret(const Protocol::Message & parser, const std::string & sms)
347{
348 std::string secret = parser.get<std::string>(ProtocolType::SECRET);
349 std::string random = parser.get<std::string>(ProtocolType::RANDOM);
350
351 crypto crypt;
352 crypt.setKey(cf::bin(sms + DELIMITER + random));
353
[21]354 std::string auth = crypt.decrypt(cf::codec::hex::getInstance()->decode(secret)).toString();
[20]355
356 if (auth[0] != '{')
357 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
358
359 for (size_t iter = 0; iter < auth.length(); iter++)
360 {
361 if (auth[iter] < ' ' || '~' < auth[iter])
362 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
363 }
364
[18]365 Protocol::Message authParser;
366
367 authParser.parse(auth);
368
369 return authParser;
370}
371
[6]372static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
[4]373 throw (cf::exception)
374{
[20]375 Protocol::Message & authParser = getSecret(parser, sms);
376
377 if (sms != authParser.get<std::string>(ProtocolType::SMS))
[4]378 THROW_EXCEPTION("SMS is not same");
379
[18]380 std::string id = authParser.get<std::string>(ProtocolType::ID);
381 std::string pw = authParser.get<std::string>(ProtocolType::PW);
382 std::string rcvdSMS = authParser.get<std::string>(ProtocolType::SMS);
383
384 if (sms != rcvdSMS)
385 THROW_EXCEPTION("invalid sms code");
[4]386
387 return dbmgr.join(id, pw, sms, address);
388}
389
[16]390static bool login(const Protocol::Message & parser, cf::network::tcp & sock, std::string & ip, std::string sms)
[4]391 throw (cf::exception)
392{
[18]393 Protocol::Message & authParser = getSecret(parser, sms);
[4]394
[18]395 std::string id = authParser.get<std::string>(ProtocolType::ID);
396 std::string pw = authParser.get<std::string>(ProtocolType::PW);
397
[14]398 bool result = dbmgr.login(id, pw);
399 if (result)
[18]400 gOnlineUsers[id] = SecureSocket(&sock, ip, sms);
[16]401
[14]402 return result;
[4]403}
404
[14]405static bool chat(const Protocol::Message & message)
[4]406{
407 bool result = false;
[14]408 Protocol::Message parser = message;
409 std::string sessid = parser.get<std::string>(ProtocolType::SESSION_ID);
410 std::vector<std::string> idList = gSessionMap[sessid];
411 std::string sender = parser.get<std::string>(ProtocolType::ID);
412 parser.mObject[ProtocolType::TYPE] = ProtocolType::LISTEN;
413 std::string serialized = parser.serialize();
[4]414
[14]415 for (size_t iter = 0; iter < idList.size(); iter++)
[4]416 {
[14]417 std::string id = idList[iter];
418
419 if (sender != id && isOnline(id))
[17]420 gOnlineUsers[id].send(serialized);
[14]421
[4]422 result = true;
423 }
424
425 return result;
426}
427
[25]428static std::vector<SFriend> getFriendList(const Protocol::Message & parser)
429 throw (cf::exception)
430{
431 std::string id = parser.get<std::string>(ProtocolType::ID);
432 std::vector<std::string> fl = dbmgr.getFriendList(id);
433 std::vector<SFriend> fv;
434
435 for (size_t iter = 0; iter < fl.size(); iter++)
436 {
437 SFriend s;
438 s.id = fl[iter];
439 s.name = fl[iter]; // username?
440 fv.push_back(s);
441 }
442
443 return fv;
444}
445
[13]446static std::string createSessionID(std::string & idList)
447{
448 cf::bin sessid;
449
450 sessid = crypto().sha256(cf::bin(idList));
451
452 return cf::codec::hex::getInstance()->encode(sessid);
453}
454
[14]455static bool openSession(const Protocol::Message & message)
[13]456{
[14]457 Protocol::Message parser = message;
[13]458 bool result = false;
459 std::string sessid;
460 std::vector<std::string> idList = parser.getList<std::string>(ProtocolType::ID_LIST);
[14]461 std::string concat = joinStrings(idList);
[13]462
463 sessid = createSessionID(concat);
464
[14]465 parser.mObject[ProtocolType::SESSION_ID] = sessid;
466 std::string serialized = parser.serialize();
467
[13]468 for (size_t iter = 0; iter < idList.size(); iter++)
469 {
[14]470 std::string id = idList[iter];
471 if (isOnline(id))
[17]472 gOnlineUsers[id].send(serialized);
[13]473
474 result = true;
475 }
476
[14]477 gSessionMap[sessid] = idList;
478
[13]479 return result;
480}
481
[16]482static bool addFriend(const Protocol::Message & message)
483{
484 Protocol::Message parser = message;
485 std::string result = "";
486 std::string id = parser.get<std::string>(ProtocolType::ID);
487 std::string friendID = parser.get<std::string>(ProtocolType::FRIEND_ID);
488
489 return dbmgr.addFriend(id, friendID);
490}
491
[18]492static std::string authenticator(cf::network::tcp & sock)
[4]493{
[14]494 Protocol::Message parser;
495
[4]496 try
497 {
498 std::string sms;
[14]499 std::string id;
500 bool loggedIn = false;
[20]501 bool result = false;
[4]502
503 while (true)
504 {
[20]505 result = false;
[25]506 std::string message = sock.receive().toString();
507 if (message[0] != '{')
508 THROW_EXCEPTION("unknown message");
[4]509
[25]510 parser.parse(message);
511
512 LOG(message);
513
[14]514 if (parser.type() == ProtocolType::SMS)
[4]515 {
516 sms = httpSMS(parser);
[20]517 result = true;
[4]518 }
[14]519 else if (parser.type() == ProtocolType::JOIN)
[4]520 {
[14]521 if (!join(parser, sms, sock.peer().address()))
[13]522 THROW_EXCEPTION("user(" << parser.get<std::string>(ProtocolType::ID) << ") cannot join");
[20]523 result = true;
[4]524 }
[14]525 else if (parser.type() == ProtocolType::LOGIN)
[4]526 {
[16]527 id = parser.get<std::string>(ProtocolType::ID);
[14]528 std::string ip = sock.peer().address();
[16]529 sms = dbmgr.getSMS(id, ip);
[14]530
[16]531 // ½ÇÆÐÇÏ¸é ¾Õ¿¡¼­ exceptionÀ¸·Î ´Ù 󸮵Ê
532 // ¿©±ä ¼º°øÇÒ ¶§¿¡¸¸ ¿È
533 if (login(parser, sock, ip, sms))
[20]534 {
[16]535 loggedIn = true;
[20]536 result = true;
537 }
[14]538 }
539
540 // success
[20]541 sock.send(Protocol::Response().result(parser.type(), result));
[14]542 if (loggedIn)
543 return id;
[4]544 }
545 }
546 catch (cf::exception & e)
547 {
[14]548 sock.send(Protocol::Response().result(parser.type(), false));
[4]549 FORWARD_EXCEPTION(e);
550 }
551}
552
[18]553static int deliverer(void * arg)
[4]554{
555 Runner * runner = reinterpret_cast<Runner *>(arg);
556 cf::network::tcp * sock = runner->mSock;
557
558 std::string id;
559
560 try
561 {
[18]562 id = authenticator(*sock);
[25]563 SecureSocket * secsock = &gOnlineUsers[id];
[4]564
[25]565 while (true)
[4]566 {
[25]567 bool result = true;
568 Protocol::Response response;
569 std::string message = secsock->receive();
[6]570 Protocol::Message parser;
[4]571 parser.parse(message);
572
573 LOG(message);
574
[14]575 if (parser.type() == ProtocolType::TELL)
576 {
577 result = chat(parser);
578 }
579 else if (parser.type() == ProtocolType::OPEN_SESSION)
580 {
581 result = openSession(parser);
582 }
[16]583 else if (parser.type() == ProtocolType::ADD_FRIEND)
584 {
585 result = addFriend(parser);
586 }
[25]587 else if (parser.type() == ProtocolType::GET_FRIEND_LIST)
588 {
589 try
590 {
591 std::vector<SFriend> fl = getFriendList(parser);
592 secsock->send(response.friendList(fl));
593 } catch (cf::exception & e)
594 {
595 LOG(e.stackTrace());
596 result = false;
597 }
598 }
[16]599
[25]600 secsock->send(response.result(parser.type(), result));
[4]601 }
602 }
603 catch (cf::exception & e)
604 {
[14]605 LOG(e.stackTrace());
[4]606 }
607
608 logout(id);
609 delete runner;
610 return 0;
611}
612
613static int server(unsigned short port)
614{
615 cf::network::tcp sock;
616
617 try
618 {
619 sock.bind(port);
620 sock.listen();
621 }
622 catch(cf::exception & e)
623 {
624 LOG(e.what());
625 return -1;
626 }
627
628 while (true)
629 {
630 try
631 {
632 cf::network::tcp client = sock.accept();
[18]633 Runner * runner = new(std::nothrow) Runner(client, deliverer);
[4]634 if (!runner)
635 THROW_EXCEPTION("cannot create thread argument");
636 }
637 catch(cf::exception & e)
638 {
639 LOG(e.what());
640 }
641 }
642
643 return 0;
644}
645
646int main(int argc, char ** argv)
647{
[20]648 if (argc != 2)
[4]649 {
650 std::cerr << "-_-^" << std::endl;
651 return -1;
652 }
653
654 return server((unsigned short)atoi(argv[1]));
655}
Note: See TracBrowser for help on using the repository browser.