- Added support for object modification from client
[public/netxms.git] / src / server / core / session.cpp
CommitLineData
21e4b6f0
VK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004 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** $module: session.cpp
20**
21**/
22
23#include "nms_core.h"
24
25
26//
27// Client session class constructor
28//
29
b54b2b11 30ClientSession::ClientSession(SOCKET hSocket, DWORD dwHostAddr)
21e4b6f0
VK
31{
32 m_pSendQueue = new Queue;
33 m_pMessageQueue = new Queue;
62f5857f 34 m_pUpdateQueue = new Queue;
21e4b6f0
VK
35 m_hSocket = hSocket;
36 m_dwIndex = INVALID_INDEX;
37 m_iState = STATE_CONNECTED;
38 m_pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
d16cf8a5
AK
39 m_hCondWriteThreadStopped = ConditionCreate(FALSE);
40 m_hCondProcessingThreadStopped = ConditionCreate(FALSE);
41 m_hCondUpdateThreadStopped = ConditionCreate(FALSE);
62f5857f 42 m_hMutexSendEvents = MutexCreate();
53512272 43 m_hMutexSendObjects = MutexCreate();
b54b2b11
VK
44 m_dwFlags = 0;
45 m_dwHostAddr = dwHostAddr;
46 strcpy(m_szUserName, "<not logged in>");
21e4b6f0
VK
47}
48
49
50//
51// Destructor
52//
53
54ClientSession::~ClientSession()
55{
56 shutdown(m_hSocket, 2);
57 closesocket(m_hSocket);
58 delete m_pSendQueue;
59 delete m_pMessageQueue;
62f5857f 60 delete m_pUpdateQueue;
21e4b6f0
VK
61 if (m_pMsgBuffer != NULL)
62 free(m_pMsgBuffer);
ecb7e1ee
VK
63 ConditionDestroy(m_hCondWriteThreadStopped);
64 ConditionDestroy(m_hCondProcessingThreadStopped);
62f5857f
VK
65 ConditionDestroy(m_hCondUpdateThreadStopped);
66 MutexDestroy(m_hMutexSendEvents);
53512272 67 MutexDestroy(m_hMutexSendObjects);
b54b2b11
VK
68
69 // Unlock locked components
70 if (m_dwFlags & CSF_EVENT_DB_LOCKED)
71 UnlockComponent(CID_EVENT_DB);
21e4b6f0
VK
72}
73
74
75//
76// Print debug information
77//
78
79void ClientSession::DebugPrintf(char *szFormat, ...)
80{
81 if ((g_dwFlags & AF_STANDALONE) && (g_dwFlags & AF_DEBUG_CSCP))
82 {
83 va_list args;
84
85 printf("*CSCP(%d)* ", m_dwIndex);
86 va_start(args, szFormat);
87 vprintf(szFormat, args);
88 va_end(args);
89 }
90}
91
92
93//
94// Post message to send queue
95//
96
97void ClientSession::SendMessage(CSCPMessage *pMsg)
98{
99 m_pSendQueue->Put(pMsg->CreateMessage());
100}
101
102
103//
104// ReadThread()
105//
106
107void ClientSession::ReadThread(void)
108{
109 CSCP_MESSAGE *pRawMsg;
110 CSCPMessage *pMsg;
111 int iErr;
112
113 // Initialize raw message receiving function
114 RecvCSCPMessage(0, NULL, m_pMsgBuffer);
115
116 pRawMsg = (CSCP_MESSAGE *)malloc(65536);
117 while(1)
118 {
119 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, m_pMsgBuffer)) <= 0)
120 break;
121
122 // Check that actual received packet size is equal to encoded in packet
123 if (ntohs(pRawMsg->wSize) != iErr)
124 {
125 DebugPrintf("Actual message size doesn't match wSize value (%d,%d)\n", iErr, ntohs(pRawMsg->wSize));
126 continue; // Bad packet, wait for next
127 }
128
129 // Create message object from raw message
130 pMsg = new CSCPMessage(pRawMsg);
131 m_pMessageQueue->Put(pMsg);
132 }
133 if (iErr < 0)
134 WriteLog(MSG_SESSION_CLOSED, EVENTLOG_WARNING_TYPE, "e", WSAGetLastError());
135 free(pRawMsg);
136
137 // Notify other threads to exit
ecb7e1ee
VK
138 m_pSendQueue->Put(INVALID_POINTER_VALUE);
139 m_pMessageQueue->Put(INVALID_POINTER_VALUE);
140
141 // Wait for other threads to finish
142 ConditionWait(m_hCondWriteThreadStopped, INFINITE);
143 ConditionWait(m_hCondProcessingThreadStopped, INFINITE);
21e4b6f0
VK
144}
145
146
147//
148// WriteThread()
149//
150
151void ClientSession::WriteThread(void)
152{
153 CSCP_MESSAGE *pMsg;
154
155 while(1)
156 {
157 pMsg = (CSCP_MESSAGE *)m_pSendQueue->GetOrBlock();
ecb7e1ee 158 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
159 break;
160
161 if (send(m_hSocket, (const char *)pMsg, ntohs(pMsg->wSize), 0) <= 0)
162 {
7968a52d 163 MemFree(pMsg);
21e4b6f0
VK
164 break;
165 }
7968a52d 166 MemFree(pMsg);
21e4b6f0 167 }
ecb7e1ee 168 ConditionSet(m_hCondWriteThreadStopped);
21e4b6f0
VK
169}
170
171
172//
173// Message processing thread
174//
175
176void ClientSession::ProcessingThread(void)
177{
178 CSCPMessage *pMsg, *pReply;
179
180 while(1)
181 {
182 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
ecb7e1ee 183 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
21e4b6f0
VK
184 break;
185
186 DebugPrintf("Received message with code %d\n", pMsg->GetCode());
187 if ((m_iState != STATE_AUTHENTICATED) && (pMsg->GetCode() != CMD_LOGIN))
188 {
189 delete pMsg;
190 continue;
191 }
192
193 switch(pMsg->GetCode())
194 {
195 case CMD_LOGIN:
196 if (m_iState != STATE_AUTHENTICATED)
197 {
0fdc2761 198 BYTE szPassword[SHA_DIGEST_LENGTH];
b54b2b11 199 char *pszLogin, szBuffer[16];
0fdc2761
VK
200
201 pszLogin = pMsg->GetVariableStr(VID_LOGIN_NAME);
202 pMsg->GetVariableBinary(VID_PASSWORD, szPassword, SHA_DIGEST_LENGTH);
21e4b6f0 203
0fdc2761 204 if (AuthenticateUser(pszLogin, szPassword, &m_dwUserId, &m_dwSystemAccess))
21e4b6f0
VK
205 m_iState = STATE_AUTHENTICATED;
206
b54b2b11
VK
207 if (m_iState == STATE_AUTHENTICATED)
208 {
209 sprintf(m_szUserName, "%s@%s", pszLogin, IpToStr(m_dwHostAddr, szBuffer));
210 }
211
7968a52d 212 MemFree(pszLogin);
21e4b6f0
VK
213
214 // Send reply
215 pReply = new CSCPMessage;
216 pReply->SetCode(CMD_LOGIN_RESP);
217 pReply->SetId(pMsg->GetId());
a5f8dbb8 218 pReply->SetVariable(VID_LOGIN_RESULT, (DWORD)(m_iState == STATE_AUTHENTICATED));
21e4b6f0
VK
219 SendMessage(pReply);
220 delete pReply;
221 }
222 else
223 {
224 }
225 break;
226 case CMD_GET_OBJECTS:
227 SendAllObjects();
228 break;
229 case CMD_GET_EVENTS:
230 SendAllEvents();
231 break;
232 case CMD_GET_CONFIG_VARLIST:
233 SendAllConfigVars();
234 break;
b54b2b11
VK
235 case CMD_OPEN_EVENT_DB:
236 SendEventDB(pMsg->GetId());
237 break;
238 case CMD_CLOSE_EVENT_DB:
239 if (m_dwFlags & CSF_EVENT_DB_LOCKED)
240 {
b8bad201
VK
241 // Check if event configuration DB has been modified
242 if (m_dwFlags & CSF_EVENT_DB_MODIFIED)
4d5a05a0 243 ReloadEvents();
b54b2b11
VK
244 UnlockComponent(CID_EVENT_DB);
245 m_dwFlags &= ~CSF_EVENT_DB_LOCKED;
246 }
3a5042fd
VK
247 // Send reply
248 pReply = new CSCPMessage;
249 pReply->SetCode(CMD_REQUEST_COMPLETED);
250 pReply->SetId(pMsg->GetId());
251 pReply->SetVariable(VID_RCC, RCC_SUCCESS);
252 SendMessage(pReply);
253 delete pReply;
b54b2b11 254 break;
605d2931
VK
255 case CMD_SET_EVENT_INFO:
256 SetEventInfo(pMsg);
257 break;
24156e90
VK
258 case CMD_MODIFY_OBJECT:
259 ModifyObject(pMsg);
260 break;
21e4b6f0
VK
261 default:
262 break;
263 }
264 delete pMsg;
265 }
ecb7e1ee 266 ConditionSet(m_hCondProcessingThreadStopped);
21e4b6f0
VK
267}
268
269
b54b2b11
VK
270//
271// Send event configuration to client
272//
273
274void ClientSession::SendEventDB(DWORD dwRqId)
275{
276 DB_ASYNC_RESULT hResult;
277 CSCPMessage msg;
278 char szBuffer[1024];
279
280 // Prepare responce message
3a5042fd 281 msg.SetCode(CMD_REQUEST_COMPLETED);
b54b2b11
VK
282 msg.SetId(dwRqId);
283
b8bad201
VK
284 if (!CheckSysAccessRights(SYSTEM_ACCESS_VIEW_EVENT_DB))
285 {
286 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
287 SendMessage(&msg);
288 }
289 else if (!LockComponent(CID_EVENT_DB, m_dwIndex, m_szUserName, NULL, szBuffer))
b54b2b11
VK
290 {
291 msg.SetVariable(VID_RCC, RCC_COMPONENT_LOCKED);
292 msg.SetVariable(VID_LOCKED_BY, szBuffer);
293 SendMessage(&msg);
294 }
295 else
296 {
297 m_dwFlags |= CSF_EVENT_DB_LOCKED;
b8bad201 298 m_dwFlags &= ~CSF_EVENT_DB_MODIFIED;
b54b2b11
VK
299
300 msg.SetVariable(VID_RCC, RCC_SUCCESS);
301 SendMessage(&msg);
302 msg.DeleteAllVariables();
303
304 // Prepare data message
305 msg.SetCode(CMD_EVENT_DB_RECORD);
306 msg.SetId(dwRqId);
307
eafa21c7 308 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,name,severity,flags,message,description FROM events");
b54b2b11
VK
309 while(DBFetch(hResult))
310 {
311 msg.SetVariable(VID_EVENT_ID, DBGetFieldAsyncULong(hResult, 0));
312 msg.SetVariable(VID_NAME, DBGetFieldAsync(hResult, 1, szBuffer, 1024));
313 msg.SetVariable(VID_SEVERITY, DBGetFieldAsyncULong(hResult, 2));
314 msg.SetVariable(VID_FLAGS, DBGetFieldAsyncULong(hResult, 3));
315 msg.SetVariable(VID_MESSAGE, DBGetFieldAsync(hResult, 4, szBuffer, 1024));
316 msg.SetVariable(VID_DESCRIPTION, DBGetFieldAsync(hResult, 5, szBuffer, 1024));
317 SendMessage(&msg);
318 msg.DeleteAllVariables();
319 }
320 DBFreeAsyncResult(hResult);
3a5042fd
VK
321
322 // Send end-of-list indicator
323 msg.SetCode(CMD_EVENT_DB_EOF);
324 SendMessage(&msg);
b54b2b11
VK
325 }
326}
327
328
21e4b6f0
VK
329//
330// Send all objects to client
331//
332
333void ClientSession::SendAllObjects(void)
334{
335 DWORD i;
336 CSCPMessage msg;
337
53512272
VK
338 MutexLock(m_hMutexSendObjects, INFINITE);
339
21e4b6f0
VK
340 // Prepare message
341 msg.SetCode(CMD_OBJECT);
342
343 // Send objects, one per message
344 ObjectsGlobalLock();
345 for(i = 0; i < g_dwIdIndexSize; i++)
24156e90
VK
346 if (g_pIndexById[i].pObject->CheckAccessRights(m_dwUserId, OBJECT_ACCESS_READ))
347 {
348 g_pIndexById[i].pObject->CreateMessage(&msg);
349 SendMessage(&msg);
350 msg.DeleteAllVariables();
351 }
21e4b6f0
VK
352 ObjectsGlobalUnlock();
353
354 // Send end of list notification
355 msg.SetCode(CMD_OBJECT_LIST_END);
356 SendMessage(&msg);
53512272
VK
357
358 MutexUnlock(m_hMutexSendObjects);
21e4b6f0
VK
359}
360
361
362//
363// Send all events to client
364//
365
366void ClientSession::SendAllEvents(void)
367{
21e4b6f0 368 CSCPMessage msg;
20177e8e 369 DB_ASYNC_RESULT hResult;
9c36ef66 370 NXC_EVENT event;
21e4b6f0 371
62f5857f
VK
372 MutexLock(m_hMutexSendEvents, INFINITE);
373
21e4b6f0 374 // Retrieve events from database
fa1d3757 375 hResult = DBAsyncSelect(g_hCoreDB, "SELECT event_id,timestamp,source,severity,message FROM event_log ORDER BY timestamp");
21e4b6f0
VK
376 if (hResult != NULL)
377 {
378 // Send events, one per message
20177e8e 379 while(DBFetch(hResult))
21e4b6f0 380 {
20177e8e
VK
381 event.dwEventId = htonl(DBGetFieldAsyncULong(hResult, 0));
382 event.dwTimeStamp = htonl(DBGetFieldAsyncULong(hResult, 1));
383 event.dwSourceId = htonl(DBGetFieldAsyncULong(hResult, 2));
384 event.dwSeverity = htonl(DBGetFieldAsyncULong(hResult, 3));
385 DBGetFieldAsync(hResult, 4, event.szMessage, MAX_EVENT_MSG_LENGTH);
9c36ef66
VK
386 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), &event, NULL));
387 }
20177e8e 388 DBFreeAsyncResult(hResult);
21e4b6f0
VK
389 }
390
391 // Send end of list notification
392 msg.SetCode(CMD_EVENT_LIST_END);
393 SendMessage(&msg);
62f5857f
VK
394
395 MutexUnlock(m_hMutexSendEvents);
21e4b6f0
VK
396}
397
398
399//
400// Send all configuration variables to client
401//
402
403void ClientSession::SendAllConfigVars(void)
404{
405 DWORD i, dwNumRecords;
406 CSCPMessage msg;
407 DB_RESULT hResult;
408
409 // Check user rights
410 if ((m_dwUserId != 0) && ((m_dwSystemAccess & SYSTEM_ACCESS_VIEW_CONFIG) == 0))
411 {
412 // Access denied
413 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 414 msg.SetVariable(VID_ERROR, (DWORD)1);
21e4b6f0
VK
415 SendMessage(&msg);
416 }
417 else
418 {
419 // Prepare message
420 msg.SetCode(CMD_CONFIG_VARIABLE);
421
422 // Retrieve configuration variables from database
423 hResult = DBSelect(g_hCoreDB, "SELECT name,value FROM config");
424 if (hResult != NULL)
425 {
426 // Send events, one per message
427 dwNumRecords = DBGetNumRows(hResult);
428 for(i = 0; i < dwNumRecords; i++)
429 {
a5f8dbb8
VK
430 msg.SetVariable(VID_NAME, DBGetField(hResult, i, 0));
431 msg.SetVariable(VID_VALUE, DBGetField(hResult, i, 1));
21e4b6f0
VK
432 SendMessage(&msg);
433 msg.DeleteAllVariables();
434 }
20177e8e 435 DBFreeResult(hResult);
21e4b6f0
VK
436 }
437
438 // Send end of list notification
439 msg.SetCode(CMD_CONFIG_VARLIST_END);
a5f8dbb8 440 msg.SetVariable(VID_ERROR, (DWORD)0);
21e4b6f0
VK
441 SendMessage(&msg);
442 }
443}
20177e8e
VK
444
445
446//
447// Close session forcibly
448//
449
450void ClientSession::Kill(void)
451{
452 // We shutdown socket connection, which will cause
453 // read thread to stop, and other threads will follow
454 shutdown(m_hSocket, 2);
455}
62f5857f
VK
456
457
458//
459// Handler for new events
460//
461
462void ClientSession::OnNewEvent(Event *pEvent)
463{
464 UPDATE_INFO *pUpdate;
465
466 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
467 pUpdate->dwCategory = INFO_CAT_EVENT;
468 pUpdate->pData = malloc(sizeof(NXC_EVENT));
469 pEvent->PrepareMessage((NXC_EVENT *)pUpdate->pData);
470 m_pUpdateQueue->Put(pUpdate);
471}
472
473
474//
475// Handler for object changes
476//
477
53512272 478void ClientSession::OnObjectChange(NetObj *pObject)
62f5857f 479{
53512272
VK
480 UPDATE_INFO *pUpdate;
481
24156e90
VK
482 if (pObject->CheckAccessRights(m_dwUserId, OBJECT_ACCESS_READ))
483 {
484 pUpdate = (UPDATE_INFO *)malloc(sizeof(UPDATE_INFO));
485 pUpdate->dwCategory = INFO_CAT_OBJECT_CHANGE;
486 pUpdate->pData = pObject;
487 m_pUpdateQueue->Put(pUpdate);
488 pObject->IncRefCount();
489 }
62f5857f
VK
490}
491
492
493//
494// Update processing thread
495//
496
497void ClientSession::UpdateThread(void)
498{
499 UPDATE_INFO *pUpdate;
53512272 500 CSCPMessage msg;
62f5857f
VK
501
502 while(1)
503 {
504 pUpdate = (UPDATE_INFO *)m_pUpdateQueue->GetOrBlock();
505 if (pUpdate == INVALID_POINTER_VALUE) // Session termination indicator
506 break;
507
508 switch(pUpdate->dwCategory)
509 {
510 case INFO_CAT_EVENT:
511 MutexLock(m_hMutexSendEvents, INFINITE);
512 m_pSendQueue->Put(CreateRawCSCPMessage(CMD_EVENT, 0, sizeof(NXC_EVENT), pUpdate->pData, NULL));
513 MutexUnlock(m_hMutexSendEvents);
514 free(pUpdate->pData);
515 break;
53512272
VK
516 case INFO_CAT_OBJECT_CHANGE:
517 MutexLock(m_hMutexSendObjects, INFINITE);
518 msg.SetId(0);
519 msg.SetCode(CMD_OBJECT_UPDATE);
520 ((NetObj *)pUpdate->pData)->CreateMessage(&msg);
521 SendMessage(&msg);
522 MutexUnlock(m_hMutexSendObjects);
523 msg.DeleteAllVariables();
524 ((NetObj *)pUpdate->pData)->DecRefCount();
525 break;
62f5857f
VK
526 default:
527 break;
528 }
529
530 free(pUpdate);
531 }
532 ConditionSet(m_hCondUpdateThreadStopped);
533}
83f0529c
VK
534
535
536//
537// Send notification message to server
538//
539
540void ClientSession::Notify(DWORD dwCode)
541{
542 CSCPMessage msg;
543
544 msg.SetCode(CMD_NOTIFY);
545 msg.SetVariable(VID_NOTIFICATION_CODE, dwCode);
546 SendMessage(&msg);
547}
605d2931
VK
548
549
550//
551// Update event template
552//
553
554void ClientSession::SetEventInfo(CSCPMessage *pRequest)
555{
556 CSCPMessage msg;
557
558 // Prepare reply message
559 msg.SetCode(CMD_REQUEST_COMPLETED);
560 msg.SetId(pRequest->GetId());
561
562 // Check if we have event configuration database opened
563 if (!(m_dwFlags & CSF_EVENT_DB_LOCKED))
564 {
565 msg.SetVariable(VID_RCC, RCC_OUT_OF_STATE_REQUEST);
566 }
567 else
568 {
569 // Check access rights
570 if (CheckSysAccessRights(SYSTEM_ACCESS_EDIT_EVENT_DB))
571 {
572 char szQuery[4096], *pszName, *pszMessage, *pszDescription;
573 DWORD dwEventId;
574 BOOL bEventExist = FALSE;
575 DB_RESULT hResult;
576
577 // Check if event with specific id exists
578 dwEventId = pRequest->GetVariableLong(VID_EVENT_ID);
579 sprintf(szQuery, "SELECT event_id FROM events WHERE event_id=%ld", dwEventId);
580 hResult = DBSelect(g_hCoreDB, szQuery);
581 if (hResult != NULL)
582 {
583 if (DBGetNumRows(hResult) > 0)
584 bEventExist = TRUE;
585 DBFreeResult(hResult);
586 }
587
b8bad201 588 // Prepare and execute SQL query
eafa21c7
VK
589 pszName = pRequest->GetVariableStr(VID_NAME);
590 pszMessage = pRequest->GetVariableStr(VID_MESSAGE);
591 pszDescription = pRequest->GetVariableStr(VID_DESCRIPTION);
605d2931
VK
592 if (bEventExist)
593 sprintf(szQuery, "UPDATE events SET name='%s',severity=%ld,flags=%ld,message='%s',description='%s' WHERE event_id=%ld",
eafa21c7 594 pszName, pRequest->GetVariableLong(VID_SEVERITY), pRequest->GetVariableLong(VID_FLAGS),
b8bad201 595 pszMessage, pszDescription, dwEventId);
605d2931 596 else
b8bad201 597 sprintf(szQuery, "INSERT INTO events SET event_id,name,severity,flags,message,description VALUES (%ld,'%s',%ld,%ld,'%s','%s')",
eafa21c7
VK
598 dwEventId, pszName, pRequest->GetVariableLong(VID_SEVERITY),
599 pRequest->GetVariableLong(VID_FLAGS), pszMessage, pszDescription);
605d2931 600 if (DBQuery(g_hCoreDB, szQuery))
b8bad201 601 {
605d2931 602 msg.SetVariable(VID_RCC, RCC_SUCCESS);
b8bad201
VK
603 m_dwFlags |= CSF_EVENT_DB_MODIFIED;
604 }
605d2931 605 else
b8bad201 606 {
605d2931 607 msg.SetVariable(VID_RCC, RCC_DB_FAILURE);
b8bad201 608 }
605d2931
VK
609
610 MemFree(pszName);
611 MemFree(pszMessage);
612 MemFree(pszDescription);
613 }
614 else
615 {
616 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
617 }
618 }
619
620 // Send responce
621 SendMessage(&msg);
622}
24156e90
VK
623
624
625//
626// Modify object
627//
628
629void ClientSession::ModifyObject(CSCPMessage *pRequest)
630{
631 DWORD dwObjectId, dwResult;
632 NetObj *pObject;
633 CSCPMessage msg;
634
635 // Prepare reply message
636 msg.SetCode(CMD_REQUEST_COMPLETED);
637 msg.SetId(pRequest->GetId());
638
639 dwObjectId = pRequest->GetVariableLong(VID_OBJECT_ID);
640 pObject = FindObjectById(dwObjectId);
641 if (pObject != NULL)
642 {
643 if (pObject->CheckAccessRights(m_dwUserId, OBJECT_ACCESS_MODIFY))
644 {
645 dwResult = pObject->ModifyFromMessage(pRequest);
646 msg.SetVariable(VID_RCC, dwResult);
647 }
648 else
649 {
650 msg.SetVariable(VID_RCC, RCC_ACCESS_DENIED);
651 }
652 }
653 else
654 {
655 msg.SetVariable(VID_RCC, RCC_INVALID_OBJECT_ID);
656 }
657
658 // Send responce
659 SendMessage(&msg);
660}