source: chevmsgr/trunk/msgsrv.cpp@ 14

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

테스트 코드 추가
UI 모양 작업
채팅되는 상태까지 확인

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