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
Line 
1
2#include "cf/network.h"
3#include "cf/task.h"
4#include "cf/codec.h"
5#include "cf/file.h"
6#include "crypto.h"
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
18std::map<std::string, SecureSocket> gOnlineUsers;
19std::map<std::string, std::vector<std::string> > gSessionMap;
20
21// --------------------------------------------------------------
22
23int cb_getFriendList(void * userArg, int argc, char ** argv, char ** colName)
24{
25 std::vector<std::string> * friendList = (std::vector<std::string> *) userArg;
26
27 friendList->push_back(argv[1]);
28
29 return 0;
30}
31
32int cb_getSMS(void * userArg, int argc, char ** argv, char ** colName)
33{
34 std::string * sms = (std::string *) userArg;
35
36 for (int i = 0; i < argc; i++)
37 {
38 if (!stricmp(colName[i], "sms"))
39 {
40 *sms = argv[i];
41 break;
42 }
43 }
44
45 return 0;
46}
47
48int cb_exist(void * userArg, int argc, char ** argv, char ** colName)
49{
50 // È£ÃâµÇ¸é Àִ°Å
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 {
72 Init();
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 "("
103 " id TEXT PRIMARY KEY,"
104 " pw TEXT"
105 ")";
106 sqlCreateAuthTable =
107 "CREATE TABLE T_AUTH"
108 "("
109 " id TEXT NOT NULL,"
110 " ip TEXT NOT NULL,"
111 " sms TEXT NOT NULL"
112 ")";
113 sqlCreateFriendsTable =
114 "CREATE TABLE T_FRIENDS"
115 "("
116 " id TEXT NOT NULL,"
117 " friend TEXT NOT NULL"
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 {
128 int result = 0;
129 char * errMsg = NULL;
130
131 result = sqlite3_exec(db, query.c_str(), cb, userArg, &errMsg);
132 LOG("[SQL] " + query);
133
134 if (result != SQLITE_OK)
135 THROW_EXCEPTION("[DBERROR][" << result << "] " << errMsg);
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
146 this->exec(query, cb_exist, &result);
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 {
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 + "')";
165
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 + "')";
170
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 }
184 }
185 catch (cf::exception & e)
186 {
187 FORWARD_EXCEPTION(e);
188 }
189
190 return true;
191 }
192
193 std::vector<std::string> getFriendList(std::string & id)
194 throw(cf::exception)
195 {
196 int result = 0;
197 std::vector<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 return friendList;
206 }
207 catch (cf::exception & e)
208 {
209 FORWARD_EXCEPTION(e);
210 }
211 }
212
213 std::string getSMS(std::string & id, std::string & ip)
214 {
215 try
216 {
217 std::string sms;
218 std::string query = "select * from T_AUTH where id = '" + id + "' and ip = '" + ip + "'";
219
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;
225 }
226 catch (cf::exception & e)
227 {
228 FORWARD_EXCEPTION(e);
229 }
230 }
231
232 bool addFriend(std::string & id, std::string & friendID)
233 {
234 try
235 {
236 std::string query = "insert into T_FRIENDS values('" + id + "', '" + friendID + "')";
237
238 this->exec(query, NULL, NULL);
239
240 return true;
241 }
242 catch (cf::exception & e)
243 {
244 FORWARD_EXCEPTION(e);
245 return false;
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{
291 gOnlineUsers[id].close();
292 gOnlineUsers.erase(id);
293 LOG(STR(id << " was logged out"));
294}
295
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
312static std::string generateSMSCode()
313{
314 return "545454";
315 char random[8] = {0x00,};
316 sprintf(random, "%06d", generateSeed() % 1000000);
317
318 return random;
319}
320
321static std::string httpSMS(const Protocol::Message & parser)
322{
323 std::string phone = parser.get<std::string>(ProtocolType::PHONE);
324
325#define CRLF "\r\n"
326 std::string code = generateSMSCode();
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);
340 smsSock.receive();
341 smsSock.close();
342
343 return code;
344}
345
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
354 std::string auth = crypt.decrypt(cf::codec::hex::getInstance()->decode(secret)).toString();
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
365 Protocol::Message authParser;
366
367 authParser.parse(auth);
368
369 return authParser;
370}
371
372static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
373 throw (cf::exception)
374{
375 Protocol::Message & authParser = getSecret(parser, sms);
376
377 if (sms != authParser.get<std::string>(ProtocolType::SMS))
378 THROW_EXCEPTION("SMS is not same");
379
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");
386
387 return dbmgr.join(id, pw, sms, address);
388}
389
390static bool login(const Protocol::Message & parser, cf::network::tcp & sock, std::string & ip, std::string sms)
391 throw (cf::exception)
392{
393 Protocol::Message & authParser = getSecret(parser, sms);
394
395 std::string id = authParser.get<std::string>(ProtocolType::ID);
396 std::string pw = authParser.get<std::string>(ProtocolType::PW);
397
398 bool result = dbmgr.login(id, pw);
399 if (result)
400 gOnlineUsers[id] = SecureSocket(&sock, ip, sms);
401
402 return result;
403}
404
405static bool chat(const Protocol::Message & message)
406{
407 bool result = false;
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();
414
415 for (size_t iter = 0; iter < idList.size(); iter++)
416 {
417 std::string id = idList[iter];
418
419 if (sender != id && isOnline(id))
420 gOnlineUsers[id].send(serialized);
421
422 result = true;
423 }
424
425 return result;
426}
427
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
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
455static bool openSession(const Protocol::Message & message)
456{
457 Protocol::Message parser = message;
458 bool result = false;
459 std::string sessid;
460 std::vector<std::string> idList = parser.getList<std::string>(ProtocolType::ID_LIST);
461 std::string concat = joinStrings(idList);
462
463 sessid = createSessionID(concat);
464
465 parser.mObject[ProtocolType::SESSION_ID] = sessid;
466 std::string serialized = parser.serialize();
467
468 for (size_t iter = 0; iter < idList.size(); iter++)
469 {
470 std::string id = idList[iter];
471 if (isOnline(id))
472 gOnlineUsers[id].send(serialized);
473
474 result = true;
475 }
476
477 gSessionMap[sessid] = idList;
478
479 return result;
480}
481
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
492static std::string authenticator(cf::network::tcp & sock)
493{
494 Protocol::Message parser;
495
496 try
497 {
498 std::string sms;
499 std::string id;
500 bool loggedIn = false;
501 bool result = false;
502
503 while (true)
504 {
505 result = false;
506 std::string message = sock.receive().toString();
507 if (message[0] != '{')
508 THROW_EXCEPTION("unknown message");
509
510 parser.parse(message);
511
512 LOG(message);
513
514 if (parser.type() == ProtocolType::SMS)
515 {
516 sms = httpSMS(parser);
517 result = true;
518 }
519 else if (parser.type() == ProtocolType::JOIN)
520 {
521 if (!join(parser, sms, sock.peer().address()))
522 THROW_EXCEPTION("user(" << parser.get<std::string>(ProtocolType::ID) << ") cannot join");
523 result = true;
524 }
525 else if (parser.type() == ProtocolType::LOGIN)
526 {
527 id = parser.get<std::string>(ProtocolType::ID);
528 std::string ip = sock.peer().address();
529 sms = dbmgr.getSMS(id, ip);
530
531 // ½ÇÆÐÇÏ¸é ¾Õ¿¡¼­ exceptionÀ¸·Î ´Ù 󸮵Ê
532 // ¿©±ä ¼º°øÇÒ ¶§¿¡¸¸ ¿È
533 if (login(parser, sock, ip, sms))
534 {
535 loggedIn = true;
536 result = true;
537 }
538 }
539
540 // success
541 sock.send(Protocol::Response().result(parser.type(), result));
542 if (loggedIn)
543 return id;
544 }
545 }
546 catch (cf::exception & e)
547 {
548 sock.send(Protocol::Response().result(parser.type(), false));
549 FORWARD_EXCEPTION(e);
550 }
551}
552
553static int deliverer(void * arg)
554{
555 Runner * runner = reinterpret_cast<Runner *>(arg);
556 cf::network::tcp * sock = runner->mSock;
557
558 std::string id;
559
560 try
561 {
562 id = authenticator(*sock);
563 SecureSocket * secsock = &gOnlineUsers[id];
564
565 while (true)
566 {
567 bool result = true;
568 Protocol::Response response;
569 std::string message = secsock->receive();
570 Protocol::Message parser;
571 parser.parse(message);
572
573 LOG(message);
574
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 }
583 else if (parser.type() == ProtocolType::ADD_FRIEND)
584 {
585 result = addFriend(parser);
586 }
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 }
599
600 secsock->send(response.result(parser.type(), result));
601 }
602 }
603 catch (cf::exception & e)
604 {
605 LOG(e.stackTrace());
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();
633 Runner * runner = new(std::nothrow) Runner(client, deliverer);
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{
648 if (argc != 2)
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.