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
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::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{
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::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
211 std::string getSMS(std::string & id, std::string & ip)
212 {
213 try
214 {
215 std::string sms;
216 std::string query = "select * from T_AUTH where id = '" + id + "' and ip = '" + ip + "'";
217
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;
223 }
224 catch (cf::exception & e)
225 {
226 FORWARD_EXCEPTION(e);
227 }
228 }
229
230 bool addFriend(std::string & id, std::string & friendID)
231 {
232 try
233 {
234 std::string query = "insert into T_FRIENDS values('" + id + "', '" + friendID + "')";
235
236 this->exec(query, NULL, NULL);
237
238 return true;
239 }
240 catch (cf::exception & e)
241 {
242 FORWARD_EXCEPTION(e);
243 return false;
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{
289 gOnlineUsers[id].close();
290 gOnlineUsers.erase(id);
291 LOG(STR(id << " was logged out"));
292}
293
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
310static std::string generateSMSCode()
311{
312 return "545454";
313 char random[8] = {0x00,};
314 sprintf(random, "%06d", generateSeed() % 1000000);
315
316 return random;
317}
318
319static std::string httpSMS(const Protocol::Message & parser)
320{
321 std::string phone = parser.get<std::string>(ProtocolType::PHONE);
322
323#define CRLF "\r\n"
324 std::string code = generateSMSCode();
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);
338 smsSock.receive();
339 smsSock.close();
340
341 return code;
342}
343
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
352 std::string auth = crypt.decrypt(cf::codec::hex::getInstance()->decode(secret)).toString();
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
363 Protocol::Message authParser;
364
365 authParser.parse(auth);
366
367 return authParser;
368}
369
370static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
371 throw (cf::exception)
372{
373 Protocol::Message & authParser = getSecret(parser, sms);
374
375 if (sms != authParser.get<std::string>(ProtocolType::SMS))
376 THROW_EXCEPTION("SMS is not same");
377
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");
384
385 return dbmgr.join(id, pw, sms, address);
386}
387
388static bool login(const Protocol::Message & parser, cf::network::tcp & sock, std::string & ip, std::string sms)
389 throw (cf::exception)
390{
391 Protocol::Message & authParser = getSecret(parser, sms);
392
393 std::string id = authParser.get<std::string>(ProtocolType::ID);
394 std::string pw = authParser.get<std::string>(ProtocolType::PW);
395
396 bool result = dbmgr.login(id, pw);
397 if (result)
398 gOnlineUsers[id] = SecureSocket(&sock, ip, sms);
399
400 return result;
401}
402
403static bool chat(const Protocol::Message & message)
404{
405 bool result = false;
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();
412
413 for (size_t iter = 0; iter < idList.size(); iter++)
414 {
415 std::string id = idList[iter];
416
417 if (sender != id && isOnline(id))
418 gOnlineUsers[id].send(serialized);
419
420 result = true;
421 }
422
423 return result;
424}
425
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
435static bool openSession(const Protocol::Message & message)
436{
437 Protocol::Message parser = message;
438 bool result = false;
439 std::string sessid;
440 std::vector<std::string> idList = parser.getList<std::string>(ProtocolType::ID_LIST);
441 std::string concat = joinStrings(idList);
442
443 sessid = createSessionID(concat);
444
445 parser.mObject[ProtocolType::SESSION_ID] = sessid;
446 std::string serialized = parser.serialize();
447
448 for (size_t iter = 0; iter < idList.size(); iter++)
449 {
450 std::string id = idList[iter];
451 if (isOnline(id))
452 gOnlineUsers[id].send(serialized);
453
454 result = true;
455 }
456
457 gSessionMap[sessid] = idList;
458
459 return result;
460}
461
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
472static std::string authenticator(cf::network::tcp & sock)
473{
474 Protocol::Message parser;
475
476 try
477 {
478 std::string sms;
479 std::string id;
480 bool loggedIn = false;
481 bool result = false;
482
483 while (true)
484 {
485 result = false;
486 parser.parse(sock.receive().toString());
487
488 if (parser.type() == ProtocolType::SMS)
489 {
490 sms = httpSMS(parser);
491 result = true;
492 }
493 else if (parser.type() == ProtocolType::JOIN)
494 {
495 if (!join(parser, sms, sock.peer().address()))
496 THROW_EXCEPTION("user(" << parser.get<std::string>(ProtocolType::ID) << ") cannot join");
497 result = true;
498 }
499 else if (parser.type() == ProtocolType::LOGIN)
500 {
501 id = parser.get<std::string>(ProtocolType::ID);
502 std::string ip = sock.peer().address();
503 sms = dbmgr.getSMS(id, ip);
504
505 // ½ÇÆÐÇÏ¸é ¾Õ¿¡¼­ exceptionÀ¸·Î ´Ù 󸮵Ê
506 // ¿©±ä ¼º°øÇÒ ¶§¿¡¸¸ ¿È
507 if (login(parser, sock, ip, sms))
508 {
509 loggedIn = true;
510 result = true;
511 }
512 }
513
514 // success
515 sock.send(Protocol::Response().result(parser.type(), result));
516 if (loggedIn)
517 return id;
518 }
519 }
520 catch (cf::exception & e)
521 {
522 sock.send(Protocol::Response().result(parser.type(), false));
523 FORWARD_EXCEPTION(e);
524 }
525}
526
527static int deliverer(void * arg)
528{
529 Runner * runner = reinterpret_cast<Runner *>(arg);
530 cf::network::tcp * sock = runner->mSock;
531
532 std::string id;
533
534 try
535 {
536 id = authenticator(*sock);
537
538 Protocol::Response response;
539 bool result = true;
540
541 while (result)
542 {
543 std::string message = gOnlineUsers[id].receive();
544 Protocol::Message parser;
545 parser.parse(message);
546
547 LOG(message);
548
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 }
557 else if (parser.type() == ProtocolType::ADD_FRIEND)
558 {
559 result = addFriend(parser);
560 }
561
562 gOnlineUsers[id].send(response.result(parser.type(), result));
563 }
564 }
565 catch (cf::exception & e)
566 {
567 LOG(e.stackTrace());
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();
595 Runner * runner = new(std::nothrow) Runner(client, deliverer);
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{
610 if (argc != 2)
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.