source: chevmsgr/trunk/msgclnt.cpp@ 33

Last change on this file since 33 was 33, checked in by cheese, 8 years ago
  1. 클라이언트 UI ID/PW 입력 안하면 죽는 문제 수정
  2. UI 종료 함수 exit로 수정
  3. 그룹챗 인덱스 변수 형 때문에 나오는 warning 수정
  4. toOpenSession 함수 가끔 죽는 문제 수정
  5. generateRandom()에서 time()하던거 rand()로 수정
  6. sms 전송 url 및 sms 코드 생성 로직 대폭 수정 (주석 참고)
File size: 8.6 KB
Line 
1#include "cf/codec.h"
2
3#include "msgclnt.h"
4#include "msg.hpp"
5
6#include <algorithm>
7
8#include <stdlib.h>
9
10// --------------------------------------------------------------
11
12typedef struct SMessageQWorkerArg
13{
14 SecureSocket * secureSocket;
15 MessageQ * messageQ;
16} SMessageQWorkerArg;
17
18typedef struct SCallbackWorkerArg
19{
20 MessageQ * messageQ;
21 SCallback callback;
22 std::map<std::string, cf::bin> * keyMap;
23} SCallbackWorkerArg;
24
25static int messageQworker(void * arg)
26{
27 SMessageQWorkerArg * inst = (SMessageQWorkerArg *)arg;
28
29 Protocol::Message message;
30 cf::size_t size;
31
32 while (true)
33 {
34 try
35 {
36 size = 0;
37
38 cf::bin raw = inst->secureSocket->receive();
39 size = raw.size();
40
41 message.parse(raw.toString());
42 }
43 catch (cf::exception & e)
44 {
45 LOG(e.stackTrace());
46
47 // closed
48 if (size == 0)
49 {
50 // dummy logout
51 message.parse(Protocol::Request().logout());
52 inst->messageQ->push(message);
53 break;
54 }
55 }
56
57 inst->messageQ->push(message);
58 }
59
60 free(inst);
61
62 return 0;
63}
64
65static inline SConversation toConversation(const Protocol::Message & message, std::map<std::string, cf::bin> * keyMap)
66{
67 SConversation c;
68
69 c.sessid = message.get<std::string>(ProtocolType::SESSION_ID);
70 c.sensitive = message.get<bool>(ProtocolType::SENSITIVE);
71 c.from = message.get<std::string>(ProtocolType::FROM);
72
73 std::string chat = message.get<std::string>(ProtocolType::MESSAGE);
74 if (chat.length() > 0)
75 {
76 crypto aria;
77 aria.setKey((*keyMap)[c.sessid]);
78
79 cf::bin decoded = cf::codec::hex::getInstance()->decode(chat);
80 cf::bin plain = aria.decrypt(decoded);
81 c.message = plain.toString();
82 }
83
84 return c;
85}
86
87static inline SOpenSession toOpenSession(const Protocol::Message & message, std::map<std::string, cf::bin> * keyMap)
88{
89 SOpenSession o;
90
91 if (message.mObject.HasKey(ProtocolType::SESSION_KEY))
92 {
93 o.sessid = message.get<std::string>(ProtocolType::SESSION_ID);
94 o.idList = message.getList<std::string>(ProtocolType::ID_LIST);
95
96 std::string encodedKey = message.get<std::string>(ProtocolType::SESSION_KEY);
97 cf::bin key = cf::codec::hex::getInstance()->decode(encodedKey);
98 (*keyMap)[o.sessid] = key;
99 }
100
101 return o;
102}
103
104static int callbackWorker(void * arg)
105{
106 SCallbackWorkerArg * inst = (SCallbackWorkerArg *)arg;
107
108 Protocol::Message message;
109
110 while (true)
111 {
112 message = inst->messageQ->pop(ProtocolType::LISTEN, false);
113 if (message.type() != ProtocolType::NONE)
114 inst->callback.onListen(toConversation(message, inst->keyMap));
115
116 message = inst->messageQ->pop(ProtocolType::OPEN_SESSION, false);
117 if (message.type() != ProtocolType::NONE)
118 inst->callback.onOpenSession(toOpenSession(message, inst->keyMap));
119
120 message = inst->messageQ->pop(ProtocolType::LOGOUT, false);
121 if (message.type() != ProtocolType::NONE)
122 break;
123 }
124
125 free(inst);
126
127 return 0;
128}
129
130static inline std::string hashPassword(const std::string & pw)
131{
132 return cf::codec::hex::getInstance()->encode(crypto().sha256(cf::bin(pw)));
133}
134
135static inline std::string getSecret(const std::string & msg, const std::string & sms, const std::string & random)
136{
137 crypto crypt;
138 crypt.setKey(cf::bin(sms + DELIMITER + random));
139
140 return cf::codec::hex::getInstance()->encode(crypt.encrypt(cf::bin(msg)));
141}
142
143// --------------------------------------------------------------
144
145void MessageQ::push(const Protocol::Message & message)
146{
147 mutex.lock();
148 messageQ.push_back(message);
149 mutex.unlock();
150}
151
152Protocol::Message MessageQ::pop(const std::string & requestType, bool isWait)
153{
154 Protocol::Message msg;
155
156 do
157 {
158 if (messageQ.size() == 0)
159 continue;
160
161 mutex.lock();
162 if (messageQ.size() > 0)
163 {
164 if (messageQ.front().type() == requestType)
165 {
166 msg = messageQ.front();
167 messageQ.pop_front();
168 isWait = false;
169 }
170 }
171 mutex.unlock();
172 } while (isWait);
173
174 return msg;
175}
176
177static bool handshake(cf::network::tcp & socket, std::string & msg, const std::string & type)
178{
179 Protocol::Message parser;
180
181 socket.send(msg);
182
183 parser.parse(socket.receive().toString());
184 if (parser.type() != type)
185 THROW_EXCEPTION("invalid message type");
186
187 return parser.get<bool>(ProtocolType::RESULT);
188}
189
190// --------------------------------------------------------------
191
192chev::chev()
193 : listener(messageQworker),
194 caller(callbackWorker)
195{
196}
197
198chev::~chev()
199{
200 secureSocket.close();
201}
202
203const std::string & chev::getLastError() const
204{
205 return error;
206}
207
208bool chev::connect(const std::string & host, const unsigned short port)
209{
210 try
211 {
212 socket.connect(host, port);
213 secureSocket.setSocket(&socket);
214 return true;
215 }
216 catch (cf::exception & e)
217 {
218 LOG(e.what());
219 error = e.stackTrace();
220 return false;
221 }
222}
223
224bool chev::join(const std::string & id, const std::string & pw, const std::string & sms)
225{
226 try
227 {
228 std::string random = cf::codec::hex::getInstance()->encode(generateRandom());
229 std::string sub = request.join(id, hashPassword(pw), sms);
230 std::string secret = getSecret(sub, sms, random);
231 std::string msg = request.secretWithRandom(ProtocolType::JOIN, id, secret, random);
232
233 return handshake(socket, msg, ProtocolType::JOIN);
234 }
235 catch (cf::exception & e)
236 {
237 LOG(e.what());
238 error = e.stackTrace();
239 return false;
240 }
241}
242
243bool chev::login(const std::string & id, const std::string & pw, const std::string & sms)
244{
245 try
246 {
247 request.setUserID(id);
248
249 std::string random = cf::codec::hex::getInstance()->encode(generateRandom());
250 std::string sub = request.login(hashPassword(pw));
251 std::string secret = getSecret(sub, sms, random);
252 std::string msg = request.secretWithRandom(ProtocolType::LOGIN, id, secret, random);
253
254 bool succeed = handshake(socket, msg, ProtocolType::LOGIN);
255 if (succeed)
256 {
257 secureSocket.setKey(socket.local().address(), sms);
258
259 SMessageQWorkerArg * arg = (SMessageQWorkerArg *)malloc(sizeof(SMessageQWorkerArg));
260 if (!arg)
261 return false;
262
263 arg->secureSocket = &secureSocket;
264 arg->messageQ = &messageQ;
265
266 listener.start(arg);
267 }
268 return succeed;
269 }
270 catch (cf::exception & e)
271 {
272 LOG(e.what());
273 error = e.stackTrace();
274 return false;
275 }
276}
277
278bool chev::sms(const std::string & phone)
279{
280 try
281 {
282 return handshake(socket, request.sms(phone), ProtocolType::SMS);
283 }
284 catch (cf::exception & e)
285 {
286 LOG(e.what());
287 error = e.stackTrace();
288 return false;
289 }
290}
291
292bool chev::addFriend(const std::string & id)
293{
294 try
295 {
296 secureSocket.send(request.addFriend(id));
297
298 return messageQ.pop(ProtocolType::ADD_FRIEND).get<bool>(ProtocolType::RESULT);
299 }
300 catch (cf::exception & e)
301 {
302 LOG(e.what());
303 error = e.stackTrace();
304 return false;
305 }
306}
307
308bool chev::getFriendList(std::vector<SFriend> & friends)
309{
310 std::vector<SFriend> friendList;
311
312 try
313 {
314 secureSocket.send(request.getFriendList());
315
316 friends = messageQ.pop(ProtocolType::FRIEND_LIST).getFriendList();
317
318 return messageQ.pop(ProtocolType::GET_FRIEND_LIST).get<bool>(ProtocolType::RESULT);
319 }
320 catch (cf::exception & e)
321 {
322 LOG(e.what());
323 error = e.stackTrace();
324 return false;
325 }
326}
327
328std::string chev::getSessionID(const std::vector<std::string> & idList)
329{
330 try
331 {
332 std::vector<std::string> toList = idList;
333 toList.insert(toList.begin(), request.getUserID());
334
335 std::string concat = joinStrings(toList);
336
337 if (sessionMap.find(concat) == sessionMap.end())
338 {
339 secureSocket.send(request.openSession(toList));
340
341 Protocol::Message message = messageQ.pop(ProtocolType::OPEN_SESSION);
342
343 if (message.mObject.HasKey(ProtocolType::SESSION_KEY))
344 {
345 std::string sessionID = message.get<std::string>(ProtocolType::SESSION_ID);
346 sessionMap[concat] = sessionID;
347 std::string encodedKey = message.get<std::string>(ProtocolType::SESSION_KEY);
348 cf::bin key = cf::codec::hex::getInstance()->decode(encodedKey);
349 keyMap[sessionID] = key;
350 }
351
352 bool result = messageQ.pop(ProtocolType::OPEN_SESSION).get<bool>(ProtocolType::RESULT);
353 if (!result)
354 THROW_EXCEPTION("cannot open session for [" + concat + "]");
355 }
356
357 return sessionMap[concat];
358 }
359 catch (cf::exception & e)
360 {
361 LOG(e.what());
362 error = e.stackTrace();
363 return "";
364 }
365}
366
367bool chev::tell(const SConversation & conversation)
368{
369 try
370 {
371 SConversation c = conversation;
372
373 if (c.message.length() > 0)
374 {
375 crypto aria;
376 aria.setKey(keyMap[c.sessid]);
377
378 cf::bin cipher = aria.encrypt(cf::bin(c.message));
379 c.message = cf::codec::hex::getInstance()->encode(cipher);
380 }
381
382 secureSocket.send(request.tell(c.sessid, c.message, c.sensitive));
383
384 return messageQ.pop(ProtocolType::TELL).get<bool>(ProtocolType::RESULT);
385 }
386 catch (cf::exception & e)
387 {
388 LOG(e.what());
389 error = e.stackTrace();
390 return false;
391 }
392}
393
394bool chev::listen(const SCallback & callback)
395{
396 SCallbackWorkerArg * arg = (SCallbackWorkerArg *)malloc(sizeof(SCallbackWorkerArg));
397 if (!arg)
398 return false;
399
400 arg->messageQ = &messageQ;
401 arg->callback = callback;
402 arg->keyMap = &keyMap;
403
404 caller.start(arg);
405
406 return true;
407}
Note: See TracBrowser for help on using the repository browser.