0515f2389e1ec84ed92e0cd3a255745a1602b655
[public/netxms.git] / src / server / core / mdsession.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: mdsession.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 #ifdef _WIN32
26 #include <psapi.h>
27 #endif
28
29 // WARNING! this hack works only for d2i_X509(); be carefull when adding new code
30 #ifdef OPENSSL_CONST
31 # undef OPENSSL_CONST
32 #endif
33 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
34 # define OPENSSL_CONST const
35 #else
36 # define OPENSSL_CONST
37 #endif
38
39 /**
40 * Constants
41 */
42 #define MAX_MSG_SIZE 65536
43
44 /**
45 * Externals
46 */
47 void UnregisterMobileDeviceSession(int id);
48
49 /**
50 * Client communication read thread starter
51 */
52 THREAD_RESULT THREAD_CALL MobileDeviceSession::readThreadStarter(void *pArg)
53 {
54 ((MobileDeviceSession *)pArg)->readThread();
55
56 // When MobileDeviceSession::readThread exits, all other session
57 // threads are already stopped, so we can safely destroy
58 // session object
59 UnregisterMobileDeviceSession(((MobileDeviceSession *)pArg)->getId());
60 delete (MobileDeviceSession *)pArg;
61 return THREAD_OK;
62 }
63
64 /**
65 * Client communication write thread starter
66 */
67 THREAD_RESULT THREAD_CALL MobileDeviceSession::writeThreadStarter(void *pArg)
68 {
69 ((MobileDeviceSession *)pArg)->writeThread();
70 return THREAD_OK;
71 }
72
73 /**
74 * Received message processing thread starter
75 */
76 THREAD_RESULT THREAD_CALL MobileDeviceSession::processingThreadStarter(void *pArg)
77 {
78 ((MobileDeviceSession *)pArg)->processingThread();
79 return THREAD_OK;
80 }
81
82 /**
83 * Mobile device session class constructor
84 */
85 MobileDeviceSession::MobileDeviceSession(SOCKET hSocket, struct sockaddr *addr)
86 {
87 m_pSendQueue = new Queue;
88 m_pMessageQueue = new Queue;
89 m_hSocket = hSocket;
90 m_id = -1;
91 m_state = SESSION_STATE_INIT;
92 m_pMsgBuffer = (NXCP_BUFFER *)malloc(sizeof(NXCP_BUFFER));
93 m_pCtx = NULL;
94 m_hWriteThread = INVALID_THREAD_HANDLE;
95 m_hProcessingThread = INVALID_THREAD_HANDLE;
96 m_mutexSocketWrite = MutexCreate();
97 m_clientAddr = (struct sockaddr *)nx_memdup(addr, (addr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
98 if (addr->sa_family == AF_INET)
99 IpToStr(ntohl(((struct sockaddr_in *)m_clientAddr)->sin_addr.s_addr), m_szHostName);
100 #ifdef WITH_IPV6
101 else
102 Ip6ToStr(((struct sockaddr_in6 *)m_clientAddr)->sin6_addr.s6_addr, m_szHostName);
103 #endif
104 _tcscpy(m_szUserName, _T("<not logged in>"));
105 _tcscpy(m_szClientInfo, _T("n/a"));
106 m_dwUserId = INVALID_INDEX;
107 m_deviceObjectId = 0;
108 m_dwEncryptionRqId = 0;
109 m_condEncryptionSetup = INVALID_CONDITION_HANDLE;
110 m_dwRefCount = 0;
111 m_isAuthenticated = false;
112 m_wCurrentCmd = 0;
113 m_dwEncryptionResult = 0;
114 }
115
116 /**
117 * Destructor
118 */
119 MobileDeviceSession::~MobileDeviceSession()
120 {
121 if (m_hSocket != -1)
122 closesocket(m_hSocket);
123 delete m_pSendQueue;
124 delete m_pMessageQueue;
125 safe_free(m_pMsgBuffer);
126 safe_free(m_clientAddr);
127 MutexDestroy(m_mutexSocketWrite);
128 if (m_pCtx != NULL)
129 m_pCtx->decRefCount();
130 if (m_condEncryptionSetup != INVALID_CONDITION_HANDLE)
131 ConditionDestroy(m_condEncryptionSetup);
132 }
133
134 /**
135 * Start all threads
136 */
137 void MobileDeviceSession::run()
138 {
139 m_hWriteThread = ThreadCreateEx(writeThreadStarter, 0, this);
140 m_hProcessingThread = ThreadCreateEx(processingThreadStarter, 0, this);
141 ThreadCreate(readThreadStarter, 0, this);
142 }
143
144 /**
145 * Print debug information
146 */
147 void MobileDeviceSession::debugPrintf(int level, const TCHAR *format, ...)
148 {
149 if (level <= nxlog_get_debug_level())
150 {
151 va_list args;
152 TCHAR buffer[4096];
153
154 va_start(args, format);
155 _vsntprintf(buffer, 4096, format, args);
156 va_end(args);
157 DbgPrintf(level, _T("[MDSN-%d] %s"), m_id, buffer);
158 }
159 }
160
161 /**
162 * Read thread
163 */
164 void MobileDeviceSession::readThread()
165 {
166 SocketMessageReceiver receiver(m_hSocket, 4096, MAX_MSG_SIZE);
167 while(1)
168 {
169 MessageReceiverResult result;
170 NXCPMessage *msg = receiver.readMessage(900000, &result);
171
172 // Check for decryption error
173 if (result == MSGRECV_DECRYPTION_FAILURE)
174 {
175 debugPrintf(4, _T("Unable to decrypt received message"));
176 continue;
177 }
178
179 // Receive error
180 if (msg == NULL)
181 {
182 debugPrintf(5, _T("readThread: message receiving error (%s)"), AbstractMessageReceiver::resultToText(result));
183 break;
184 }
185
186 if (nxlog_get_debug_level() >= 8)
187 {
188 String msgDump = NXCPMessage::dump(receiver.getRawMessageBuffer(), NXCP_VERSION);
189 debugPrintf(8, _T("Message dump:\n%s"), (const TCHAR *)msgDump);
190 }
191
192 TCHAR szBuffer[256];
193 if ((msg->getCode() == CMD_SESSION_KEY) && (msg->getId() == m_dwEncryptionRqId))
194 {
195 debugPrintf(6, _T("Received message %s"), NXCPMessageCodeName(msg->getCode(), szBuffer));
196 m_dwEncryptionResult = SetupEncryptionContext(msg, &m_pCtx, NULL, g_pServerKey, NXCP_VERSION);
197 receiver.setEncryptionContext(m_pCtx);
198 ConditionSet(m_condEncryptionSetup);
199 m_dwEncryptionRqId = 0;
200 delete msg;
201 }
202 else if (msg->getCode() == CMD_KEEPALIVE)
203 {
204 debugPrintf(6, _T("Received message %s"), NXCPMessageCodeName(msg->getCode(), szBuffer));
205 respondToKeepalive(msg->getId());
206 delete msg;
207 }
208 else
209 {
210 m_pMessageQueue->put(msg);
211 }
212 }
213
214 // Notify other threads to exit
215 NXCP_MESSAGE *rawMsg;
216 while((rawMsg = (NXCP_MESSAGE *)m_pSendQueue->get()) != NULL)
217 free(rawMsg);
218 m_pSendQueue->put(INVALID_POINTER_VALUE);
219
220 NXCPMessage *msg;
221 while((msg = (NXCPMessage *)m_pMessageQueue->get()) != NULL)
222 delete msg;
223 m_pMessageQueue->put(INVALID_POINTER_VALUE);
224
225 // Wait for other threads to finish
226 ThreadJoin(m_hWriteThread);
227 ThreadJoin(m_hProcessingThread);
228
229 // Waiting while reference count becomes 0
230 if (m_dwRefCount > 0)
231 {
232 debugPrintf(3, _T("Waiting for pending requests..."));
233 do
234 {
235 ThreadSleep(1);
236 } while(m_dwRefCount > 0);
237 }
238
239 WriteAuditLog(AUDIT_SECURITY, TRUE, m_dwUserId, m_szHostName, m_id, 0, _T("Mobile device logged out (client: %s)"), m_szClientInfo);
240 debugPrintf(3, _T("Session closed"));
241 }
242
243 /**
244 * Network write thread
245 */
246 void MobileDeviceSession::writeThread()
247 {
248 NXCP_MESSAGE *pRawMsg;
249 NXCP_ENCRYPTED_MESSAGE *pEnMsg;
250 TCHAR szBuffer[128];
251 BOOL bResult;
252
253 while(1)
254 {
255 pRawMsg = (NXCP_MESSAGE *)m_pSendQueue->getOrBlock();
256 if (pRawMsg == INVALID_POINTER_VALUE) // Session termination indicator
257 break;
258
259 if (ntohs(pRawMsg->code) != CMD_ADM_MESSAGE)
260 debugPrintf(6, _T("Sending message %s"), NXCPMessageCodeName(ntohs(pRawMsg->code), szBuffer));
261
262 if (m_pCtx != NULL)
263 {
264 pEnMsg = m_pCtx->encryptMessage(pRawMsg);
265 if (pEnMsg != NULL)
266 {
267 bResult = (SendEx(m_hSocket, (char *)pEnMsg, ntohl(pEnMsg->size), 0, m_mutexSocketWrite) == (int)ntohl(pEnMsg->size));
268 free(pEnMsg);
269 }
270 else
271 {
272 bResult = FALSE;
273 }
274 }
275 else
276 {
277 bResult = (SendEx(m_hSocket, (const char *)pRawMsg, ntohl(pRawMsg->size), 0, m_mutexSocketWrite) == (int)ntohl(pRawMsg->size));
278 }
279 free(pRawMsg);
280
281 if (!bResult)
282 {
283 closesocket(m_hSocket);
284 m_hSocket = -1;
285 break;
286 }
287 }
288 }
289
290 /**
291 * Message processing thread
292 */
293 void MobileDeviceSession::processingThread()
294 {
295 NXCPMessage *msg;
296 TCHAR szBuffer[128];
297 UINT32 i;
298 int status;
299
300 while(1)
301 {
302 msg = (NXCPMessage *)m_pMessageQueue->getOrBlock();
303 if (msg == INVALID_POINTER_VALUE) // Session termination indicator
304 break;
305
306 m_wCurrentCmd = msg->getCode();
307 debugPrintf(6, _T("Received message %s"), NXCPMessageCodeName(m_wCurrentCmd, szBuffer));
308 if (!m_isAuthenticated &&
309 (m_wCurrentCmd != CMD_LOGIN) &&
310 (m_wCurrentCmd != CMD_GET_SERVER_INFO) &&
311 (m_wCurrentCmd != CMD_REQUEST_ENCRYPTION))
312 {
313 delete msg;
314 continue;
315 }
316
317 m_state = SESSION_STATE_PROCESSING;
318 switch(m_wCurrentCmd)
319 {
320 case CMD_GET_SERVER_INFO:
321 sendServerInfo(msg->getId());
322 break;
323 case CMD_LOGIN:
324 login(msg);
325 break;
326 case CMD_REQUEST_ENCRYPTION:
327 setupEncryption(msg);
328 break;
329 case CMD_REPORT_DEVICE_INFO:
330 updateDeviceInfo(msg);
331 break;
332 case CMD_REPORT_DEVICE_STATUS:
333 updateDeviceStatus(msg);
334 break;
335 case CMD_PUSH_DCI_DATA:
336 pushData(msg);
337 break;
338 default:
339 // Pass message to loaded modules
340 for(i = 0; i < g_dwNumModules; i++)
341 {
342 if (g_pModuleList[i].pfMobileDeviceCommandHandler != NULL)
343 {
344 status = g_pModuleList[i].pfMobileDeviceCommandHandler(m_wCurrentCmd, msg, this);
345 if (status != NXMOD_COMMAND_IGNORED)
346 {
347 if (status == NXMOD_COMMAND_ACCEPTED_ASYNC)
348 {
349 msg = NULL; // Prevent deletion
350 }
351 break; // Message was processed by the module
352 }
353 }
354 }
355 if (i == g_dwNumModules)
356 {
357 NXCPMessage response;
358
359 response.setId(msg->getId());
360 response.setCode(CMD_REQUEST_COMPLETED);
361 response.setField(VID_RCC, RCC_NOT_IMPLEMENTED);
362 sendMessage(&response);
363 }
364 break;
365 }
366 delete msg;
367 m_state = m_isAuthenticated ? SESSION_STATE_IDLE : SESSION_STATE_INIT;
368 }
369 }
370
371 /**
372 * Respond to client's keepalive message
373 */
374 void MobileDeviceSession::respondToKeepalive(UINT32 dwRqId)
375 {
376 NXCPMessage msg;
377
378 msg.setCode(CMD_REQUEST_COMPLETED);
379 msg.setId(dwRqId);
380 msg.setField(VID_RCC, RCC_SUCCESS);
381 sendMessage(&msg);
382 }
383
384 /**
385 * Send message to client
386 */
387 void MobileDeviceSession::sendMessage(NXCPMessage *msg)
388 {
389 TCHAR szBuffer[128];
390 BOOL bResult;
391
392 debugPrintf(6, _T("Sending message %s"), NXCPMessageCodeName(msg->getCode(), szBuffer));
393 NXCP_MESSAGE *pRawMsg = msg->createMessage();
394 if (nxlog_get_debug_level() >= 8)
395 {
396 String msgDump = NXCPMessage::dump(pRawMsg, NXCP_VERSION);
397 debugPrintf(8, _T("Message dump:\n%s"), (const TCHAR *)msgDump);
398 }
399 if (m_pCtx != NULL)
400 {
401 NXCP_ENCRYPTED_MESSAGE *pEnMsg = m_pCtx->encryptMessage(pRawMsg);
402 if (pEnMsg != NULL)
403 {
404 bResult = (SendEx(m_hSocket, (char *)pEnMsg, ntohl(pEnMsg->size), 0, m_mutexSocketWrite) == (int)ntohl(pEnMsg->size));
405 free(pEnMsg);
406 }
407 else
408 {
409 bResult = FALSE;
410 }
411 }
412 else
413 {
414 bResult = (SendEx(m_hSocket, (const char *)pRawMsg, ntohl(pRawMsg->size), 0, m_mutexSocketWrite) == (int)ntohl(pRawMsg->size));
415 }
416 free(pRawMsg);
417
418 if (!bResult)
419 {
420 closesocket(m_hSocket);
421 m_hSocket = -1;
422 }
423 }
424
425 /**
426 * Send server information to client
427 */
428 void MobileDeviceSession::sendServerInfo(UINT32 dwRqId)
429 {
430 NXCPMessage msg;
431
432 // Prepare response message
433 msg.setCode(CMD_REQUEST_COMPLETED);
434 msg.setId(dwRqId);
435
436 // Generate challenge for certificate authentication
437 #ifdef _WITH_ENCRYPTION
438 RAND_bytes(m_challenge, CLIENT_CHALLENGE_SIZE);
439 #else
440 memset(m_challenge, 0, CLIENT_CHALLENGE_SIZE);
441 #endif
442
443 // Fill message with server info
444 msg.setField(VID_RCC, RCC_SUCCESS);
445 msg.setField(VID_SERVER_VERSION, NETXMS_VERSION_STRING);
446 msg.setField(VID_SERVER_ID, g_serverId);
447 msg.setField(VID_PROTOCOL_VERSION, (UINT32)MOBILE_DEVICE_PROTOCOL_VERSION);
448 msg.setField(VID_CHALLENGE, m_challenge, CLIENT_CHALLENGE_SIZE);
449
450 // Send response
451 sendMessage(&msg);
452 }
453
454 /**
455 * Authenticate client
456 */
457 void MobileDeviceSession::login(NXCPMessage *pRequest)
458 {
459 NXCPMessage msg;
460 TCHAR szLogin[MAX_USER_NAME], szPassword[1024];
461 int nAuthType;
462 bool changePasswd = false, intruderLockout = false, closeOtherSessions = false;
463 UINT32 dwResult;
464 #ifdef _WITH_ENCRYPTION
465 X509 *pCert;
466 #endif
467
468 // Prepare response message
469 msg.setCode(CMD_LOGIN_RESP);
470 msg.setId(pRequest->getId());
471
472 // Get client info string
473 if (pRequest->isFieldExist(VID_CLIENT_INFO))
474 {
475 TCHAR szClientInfo[32], szOSInfo[32], szLibVersion[16];
476
477 pRequest->getFieldAsString(VID_CLIENT_INFO, szClientInfo, 32);
478 pRequest->getFieldAsString(VID_OS_INFO, szOSInfo, 32);
479 pRequest->getFieldAsString(VID_LIBNXCL_VERSION, szLibVersion, 16);
480 _sntprintf(m_szClientInfo, 96, _T("%s (%s; libnxcl %s)"),
481 szClientInfo, szOSInfo, szLibVersion);
482 }
483
484 if (!m_isAuthenticated)
485 {
486 pRequest->getFieldAsString(VID_LOGIN_NAME, szLogin, MAX_USER_NAME);
487 nAuthType = (int)pRequest->getFieldAsUInt16(VID_AUTH_TYPE);
488 UINT64 userRights;
489 UINT32 graceLogins;
490 switch(nAuthType)
491 {
492 case NETXMS_AUTH_TYPE_PASSWORD:
493 #ifdef UNICODE
494 pRequest->getFieldAsString(VID_PASSWORD, szPassword, 256);
495 #else
496 pRequest->getFieldAsUtf8String(VID_PASSWORD, szPassword, 1024);
497 #endif
498 dwResult = AuthenticateUser(szLogin, szPassword, 0, NULL, NULL, &m_dwUserId,
499 &userRights, &changePasswd, &intruderLockout,
500 &closeOtherSessions, false, &graceLogins);
501 break;
502 case NETXMS_AUTH_TYPE_CERTIFICATE:
503 #ifdef _WITH_ENCRYPTION
504 pCert = CertificateFromLoginMessage(pRequest);
505 if (pCert != NULL)
506 {
507 BYTE signature[256];
508 UINT32 dwSigLen;
509
510 dwSigLen = pRequest->getFieldAsBinary(VID_SIGNATURE, signature, 256);
511 dwResult = AuthenticateUser(szLogin, (TCHAR *)signature, dwSigLen, pCert,
512 m_challenge, &m_dwUserId, &userRights,
513 &changePasswd, &intruderLockout,
514 &closeOtherSessions, false, &graceLogins);
515 X509_free(pCert);
516 }
517 else
518 {
519 dwResult = RCC_BAD_CERTIFICATE;
520 }
521 #else
522 dwResult = RCC_NOT_IMPLEMENTED;
523 #endif
524 break;
525 default:
526 dwResult = RCC_UNSUPPORTED_AUTH_TYPE;
527 break;
528 }
529
530 if (dwResult == RCC_SUCCESS)
531 {
532 if (userRights & SYSTEM_ACCESS_MOBILE_DEVICE_LOGIN)
533 {
534 TCHAR deviceId[MAX_OBJECT_NAME] = _T("");
535 pRequest->getFieldAsString(VID_DEVICE_ID, deviceId, MAX_OBJECT_NAME);
536 MobileDevice *md = FindMobileDeviceByDeviceID(deviceId);
537 if (md != NULL)
538 {
539 m_deviceObjectId = md->getId();
540 m_isAuthenticated = true;
541 _sntprintf(m_szUserName, MAX_SESSION_NAME, _T("%s@%s"), szLogin, m_szHostName);
542 msg.setField(VID_RCC, RCC_SUCCESS);
543 msg.setField(VID_USER_SYS_RIGHTS, userRights);
544 msg.setField(VID_USER_ID, m_dwUserId);
545 msg.setField(VID_CHANGE_PASSWD_FLAG, (WORD)changePasswd);
546 msg.setField(VID_DBCONN_STATUS, (WORD)((g_flags & AF_DB_CONNECTION_LOST) ? 0 : 1));
547 msg.setField(VID_ZONING_ENABLED, (WORD)((g_flags & AF_ENABLE_ZONING) ? 1 : 0));
548 debugPrintf(3, _T("User %s authenticated as mobile device"), m_szUserName);
549 WriteAuditLog(AUDIT_SECURITY, TRUE, m_dwUserId, m_szHostName, m_id, 0,
550 _T("Mobile device logged in as user \"%s\" (client info: %s)"), szLogin, m_szClientInfo);
551 }
552 else
553 {
554 debugPrintf(3, _T("Mobile device object with device ID \"%s\" not found"), deviceId);
555 msg.setField(VID_RCC, RCC_ACCESS_DENIED);
556 WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_szHostName, m_id, 0,
557 _T("Mobile device login as user \"%s\" failed - mobile device object not found (client info: %s)"),
558 szLogin, m_szClientInfo);
559 }
560 }
561 else
562 {
563 msg.setField(VID_RCC, RCC_ACCESS_DENIED);
564 WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_szHostName, m_id, 0,
565 _T("Mobile device login as user \"%s\" failed - user does not have mobile device login rights (client info: %s)"),
566 szLogin, m_szClientInfo);
567 }
568 }
569 else
570 {
571 msg.setField(VID_RCC, dwResult);
572 WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_szHostName, m_id, 0,
573 _T("Mobile device login as user \"%s\" failed with error code %d (client info: %s)"),
574 szLogin, dwResult, m_szClientInfo);
575 if (intruderLockout)
576 {
577 WriteAuditLog(AUDIT_SECURITY, FALSE, m_dwUserId, m_szHostName, m_id, 0,
578 _T("User account \"%s\" temporary disabled due to excess count of failed authentication attempts"), szLogin);
579 }
580 }
581 }
582 else
583 {
584 msg.setField(VID_RCC, RCC_OUT_OF_STATE_REQUEST);
585 }
586
587 // Send response
588 sendMessage(&msg);
589 }
590
591 /**
592 * Setup encryption with client
593 */
594 void MobileDeviceSession::setupEncryption(NXCPMessage *request)
595 {
596 NXCPMessage msg;
597
598 #ifdef _WITH_ENCRYPTION
599 m_dwEncryptionRqId = request->getId();
600 m_dwEncryptionResult = RCC_TIMEOUT;
601 if (m_condEncryptionSetup == INVALID_CONDITION_HANDLE)
602 m_condEncryptionSetup = ConditionCreate(FALSE);
603
604 // Send request for session key
605 PrepareKeyRequestMsg(&msg, g_pServerKey, request->getFieldAsUInt16(VID_USE_X509_KEY_FORMAT) != 0);
606 msg.setId(request->getId());
607 sendMessage(&msg);
608 msg.deleteAllFields();
609
610 // Wait for encryption setup
611 ConditionWait(m_condEncryptionSetup, 30000);
612
613 // Send response
614 msg.setCode(CMD_REQUEST_COMPLETED);
615 msg.setId(request->getId());
616 msg.setField(VID_RCC, m_dwEncryptionResult);
617 #else /* _WITH_ENCRYPTION not defined */
618 msg.setCode(CMD_REQUEST_COMPLETED);
619 msg.setId(request->getId());
620 msg.setField(VID_RCC, RCC_NO_ENCRYPTION_SUPPORT);
621 #endif
622
623 sendMessage(&msg);
624 }
625
626 /**
627 * Update device system information
628 */
629 void MobileDeviceSession::updateDeviceInfo(NXCPMessage *request)
630 {
631 NXCPMessage msg;
632
633 // Prepare response message
634 msg.setCode(CMD_REQUEST_COMPLETED);
635 msg.setId(request->getId());
636
637 MobileDevice *device = (MobileDevice *)FindObjectById(m_deviceObjectId, OBJECT_MOBILEDEVICE);
638 if (device != NULL)
639 {
640 device->updateSystemInfo(request);
641 msg.setField(VID_RCC, RCC_SUCCESS);
642 }
643 else
644 {
645 msg.setField(VID_RCC, RCC_INVALID_OBJECT_ID);
646 }
647
648 sendMessage(&msg);
649 }
650
651 /**
652 * Update device status
653 */
654 void MobileDeviceSession::updateDeviceStatus(NXCPMessage *request)
655 {
656 NXCPMessage msg;
657
658 // Prepare response message
659 msg.setCode(CMD_REQUEST_COMPLETED);
660 msg.setId(request->getId());
661
662 MobileDevice *device = (MobileDevice *)FindObjectById(m_deviceObjectId, OBJECT_MOBILEDEVICE);
663 if (device != NULL)
664 {
665 device->updateStatus(request);
666 msg.setField(VID_RCC, RCC_SUCCESS);
667 }
668 else
669 {
670 msg.setField(VID_RCC, RCC_INVALID_OBJECT_ID);
671 }
672
673 sendMessage(&msg);
674 }
675
676 /**
677 * Push DCI data
678 */
679 void MobileDeviceSession::pushData(NXCPMessage *request)
680 {
681 NXCPMessage msg;
682
683 msg.setCode(CMD_REQUEST_COMPLETED);
684 msg.setId(request->getId());
685
686 MobileDevice *device = (MobileDevice *)FindObjectById(m_deviceObjectId, OBJECT_MOBILEDEVICE);
687 if (device != NULL)
688 {
689 int count = (int)request->getFieldAsUInt32(VID_NUM_ITEMS);
690 if (count > 0)
691 {
692 DCItem **dciList = (DCItem **)malloc(sizeof(DCItem *) * count);
693 TCHAR **valueList = (TCHAR **)malloc(sizeof(TCHAR *) * count);
694 memset(valueList, 0, sizeof(TCHAR *) * count);
695
696 int i;
697 UINT32 varId = VID_PUSH_DCI_DATA_BASE;
698 bool ok = true;
699 for(i = 0; (i < count) && ok; i++)
700 {
701 ok = false;
702
703 // find DCI by ID or name (if ID==0)
704 UINT32 dciId = request->getFieldAsUInt32(varId++);
705 DCObject *pItem;
706 if (dciId != 0)
707 {
708 pItem = device->getDCObjectById(dciId);
709 }
710 else
711 {
712 TCHAR name[MAX_PARAM_NAME];
713 request->getFieldAsString(varId++, name, MAX_PARAM_NAME);
714 pItem = device->getDCObjectByName(name);
715 }
716
717 if ((pItem != NULL) && (pItem->getType() == DCO_TYPE_ITEM))
718 {
719 if (pItem->getDataSource() == DS_PUSH_AGENT)
720 {
721 dciList[i] = (DCItem *)pItem;
722 valueList[i] = request->getFieldAsString(varId++);
723 ok = true;
724 }
725 else
726 {
727 msg.setField(VID_RCC, RCC_NOT_PUSH_DCI);
728 }
729 }
730 else
731 {
732 msg.setField(VID_RCC, RCC_INVALID_DCI_ID);
733 }
734 }
735
736 // If all items was checked OK, push data
737 if (ok)
738 {
739 time_t t = 0;
740 int ft = request->getFieldType(VID_TIMESTAMP);
741 if (ft == NXCP_DT_INT32)
742 {
743 t = (time_t)request->getFieldAsUInt32(VID_TIMESTAMP);
744 }
745 else if (ft == NXCP_DT_STRING)
746 {
747 char ts[256];
748 request->getFieldAsMBString(VID_TIMESTAMP, ts, 256);
749
750 struct tm timeBuff;
751 if (strptime(ts, "%Y/%m/%d %H:%M:%S", &timeBuff) != NULL)
752 {
753 timeBuff.tm_isdst = -1;
754 t = timegm(&timeBuff);
755 }
756 }
757 if (t == 0)
758 {
759 time(&t);
760 }
761
762 for(i = 0; i < count; i++)
763 {
764 if (_tcslen(valueList[i]) >= MAX_DCI_STRING_VALUE)
765 valueList[i][MAX_DCI_STRING_VALUE - 1] = 0;
766 device->processNewDCValue(dciList[i], t, valueList[i]);
767 dciList[i]->setLastPollTime(t);
768 }
769 msg.setField(VID_RCC, RCC_SUCCESS);
770 }
771 else
772 {
773 msg.setField(VID_FAILED_DCI_INDEX, i - 1);
774 }
775
776 // Cleanup
777 for(i = 0; i < count; i++)
778 safe_free(valueList[i]);
779 safe_free(valueList);
780 free(dciList);
781 }
782 else
783 {
784 msg.setField(VID_RCC, RCC_INVALID_ARGUMENT);
785 }
786 }
787 else
788 {
789 msg.setField(VID_RCC, RCC_INVALID_OBJECT_ID);
790 }
791
792 sendMessage(&msg);
793 }