added uuid class; code rerfactoring
[public/netxms.git] / src / server / core / netobj.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2015 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: netobj.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Default constructor
27 */
28 NetObj::NetObj()
29 {
30 int i;
31
32 m_dwRefCount = 0;
33 m_mutexProperties = MutexCreate();
34 m_mutexRefCount = MutexCreate();
35 m_mutexACL = MutexCreate();
36 m_rwlockParentList = RWLockCreate();
37 m_rwlockChildList = RWLockCreate();
38 m_iStatus = STATUS_UNKNOWN;
39 m_name[0] = 0;
40 m_pszComments = NULL;
41 m_isModified = false;
42 m_isDeleted = false;
43 m_isHidden = false;
44 m_isSystem = false;
45 m_dwChildCount = 0;
46 m_pChildList = NULL;
47 m_dwParentCount = 0;
48 m_pParentList = NULL;
49 m_pAccessList = new AccessList;
50 m_bInheritAccessRights = TRUE;
51 m_dwNumTrustedNodes = 0;
52 m_pdwTrustedNodes = NULL;
53 m_pollRequestor = NULL;
54 m_iStatusCalcAlg = SA_CALCULATE_DEFAULT;
55 m_iStatusPropAlg = SA_PROPAGATE_DEFAULT;
56 m_iFixedStatus = STATUS_WARNING;
57 m_iStatusShift = 0;
58 m_iStatusSingleThreshold = 75;
59 m_dwTimeStamp = 0;
60 for(i = 0; i < 4; i++)
61 {
62 m_iStatusTranslation[i] = i + 1;
63 m_iStatusThresholds[i] = 80 - i * 20;
64 }
65 m_submapId = 0;
66 m_moduleData = NULL;
67 m_postalAddress = new PostalAddress();
68 }
69
70 /**
71 * Destructor
72 */
73 NetObj::~NetObj()
74 {
75 MutexDestroy(m_mutexProperties);
76 MutexDestroy(m_mutexRefCount);
77 MutexDestroy(m_mutexACL);
78 RWLockDestroy(m_rwlockParentList);
79 RWLockDestroy(m_rwlockChildList);
80 safe_free(m_pChildList);
81 safe_free(m_pParentList);
82 delete m_pAccessList;
83 safe_free(m_pdwTrustedNodes);
84 safe_free(m_pszComments);
85 delete m_moduleData;
86 delete m_postalAddress;
87 }
88
89 /**
90 * Create object from database data
91 */
92 BOOL NetObj::loadFromDatabase(UINT32 dwId)
93 {
94 return FALSE; // Abstract objects cannot be loaded from database
95 }
96
97 /**
98 * Save object to database
99 */
100 BOOL NetObj::saveToDatabase(DB_HANDLE hdb)
101 {
102 return FALSE; // Abstract objects cannot be saved to database
103 }
104
105 /**
106 * Parameters for DeleteModuleDataCallback and SaveModuleDataCallback
107 */
108 struct ModuleDataDatabaseCallbackParams
109 {
110 UINT32 id;
111 DB_HANDLE hdb;
112 };
113
114 /**
115 * Callback for deleting module data from database
116 */
117 static EnumerationCallbackResult DeleteModuleDataCallback(const TCHAR *key, const void *value, void *data)
118 {
119 return ((ModuleData *)value)->deleteFromDatabase(((ModuleDataDatabaseCallbackParams *)data)->hdb, ((ModuleDataDatabaseCallbackParams *)data)->id) ? _CONTINUE : _STOP;
120 }
121
122 /**
123 * Delete object from database
124 */
125 bool NetObj::deleteFromDatabase(DB_HANDLE hdb)
126 {
127 // Delete ACL
128 bool success = executeQueryOnObject(hdb, _T("DELETE FROM acl WHERE object_id=?"));
129 if (success)
130 success = executeQueryOnObject(hdb, _T("DELETE FROM object_properties WHERE object_id=?"));
131 if (success)
132 success = executeQueryOnObject(hdb, _T("DELETE FROM object_custom_attributes WHERE object_id=?"));
133
134 // Delete events
135 if (success && ConfigReadInt(_T("DeleteEventsOfDeletedObject"), 1))
136 {
137 success = executeQueryOnObject(hdb, _T("DELETE FROM event_log WHERE event_source=?"));
138 }
139
140 // Delete alarms
141 if (success && ConfigReadInt(_T("DeleteAlarmsOfDeletedObject"), 1))
142 {
143 success = DeleteObjectAlarms(m_id, hdb);
144 }
145
146 // Delete module data
147 if (success && (m_moduleData != NULL))
148 {
149 ModuleDataDatabaseCallbackParams data;
150 data.id = m_id;
151 data.hdb = hdb;
152 success = (m_moduleData->forEach(DeleteModuleDataCallback, &data) == _CONTINUE);
153 }
154
155 return success;
156 }
157
158 /**
159 * Load common object properties from database
160 */
161 bool NetObj::loadCommonProperties()
162 {
163 bool success = false;
164
165 // Load access options
166 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB,
167 _T("SELECT name,status,is_deleted,")
168 _T("inherit_access_rights,last_modified,status_calc_alg,")
169 _T("status_prop_alg,status_fixed_val,status_shift,")
170 _T("status_translation,status_single_threshold,")
171 _T("status_thresholds,comments,is_system,")
172 _T("location_type,latitude,longitude,location_accuracy,")
173 _T("location_timestamp,guid,image,submap_id,country,city,")
174 _T("street_address,postcode FROM object_properties ")
175 _T("WHERE object_id=?"));
176 if (hStmt != NULL)
177 {
178 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
179 DB_RESULT hResult = DBSelectPrepared(hStmt);
180 if (hResult != NULL)
181 {
182 if (DBGetNumRows(hResult) > 0)
183 {
184 DBGetField(hResult, 0, 0, m_name, MAX_OBJECT_NAME);
185 m_iStatus = DBGetFieldLong(hResult, 0, 1);
186 m_isDeleted = DBGetFieldLong(hResult, 0, 2) ? true : false;
187 m_bInheritAccessRights = DBGetFieldLong(hResult, 0, 3) ? TRUE : FALSE;
188 m_dwTimeStamp = DBGetFieldULong(hResult, 0, 4);
189 m_iStatusCalcAlg = DBGetFieldLong(hResult, 0, 5);
190 m_iStatusPropAlg = DBGetFieldLong(hResult, 0, 6);
191 m_iFixedStatus = DBGetFieldLong(hResult, 0, 7);
192 m_iStatusShift = DBGetFieldLong(hResult, 0, 8);
193 DBGetFieldByteArray(hResult, 0, 9, m_iStatusTranslation, 4, STATUS_WARNING);
194 m_iStatusSingleThreshold = DBGetFieldLong(hResult, 0, 10);
195 DBGetFieldByteArray(hResult, 0, 11, m_iStatusThresholds, 4, 50);
196 safe_free(m_pszComments);
197 m_pszComments = DBGetField(hResult, 0, 12, NULL, 0);
198 m_isSystem = DBGetFieldLong(hResult, 0, 13) ? true : false;
199
200 int locType = DBGetFieldLong(hResult, 0, 14);
201 if (locType != GL_UNSET)
202 {
203 TCHAR lat[32], lon[32];
204
205 DBGetField(hResult, 0, 15, lat, 32);
206 DBGetField(hResult, 0, 16, lon, 32);
207 m_geoLocation = GeoLocation(locType, lat, lon, DBGetFieldLong(hResult, 0, 17), DBGetFieldULong(hResult, 0, 18));
208 }
209 else
210 {
211 m_geoLocation = GeoLocation();
212 }
213
214 m_guid = DBGetFieldGUID(hResult, 0, 19);
215 m_image = DBGetFieldGUID(hResult, 0, 20);
216 m_submapId = DBGetFieldULong(hResult, 0, 21);
217
218 TCHAR country[64], city[64], streetAddress[256], postcode[32];
219 DBGetField(hResult, 0, 22, country, 64);
220 DBGetField(hResult, 0, 23, city, 64);
221 DBGetField(hResult, 0, 24, streetAddress, 256);
222 DBGetField(hResult, 0, 25, postcode, 32);
223 delete m_postalAddress;
224 m_postalAddress = new PostalAddress(country, city, streetAddress, postcode);
225
226 success = true;
227 }
228 DBFreeResult(hResult);
229 }
230 DBFreeStatement(hStmt);
231 }
232
233 // Load custom attributes
234 if (success)
235 {
236 hStmt = DBPrepare(g_hCoreDB, _T("SELECT attr_name,attr_value FROM object_custom_attributes WHERE object_id=?"));
237 if (hStmt != NULL)
238 {
239 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
240 DB_RESULT hResult = DBSelectPrepared(hStmt);
241 if (hResult != NULL)
242 {
243 int i, count;
244 TCHAR *name, *value;
245
246 count = DBGetNumRows(hResult);
247 for(i = 0; i < count; i++)
248 {
249 name = DBGetField(hResult, i, 0, NULL, 0);
250 if (name != NULL)
251 {
252 value = DBGetField(hResult, i, 1, NULL, 0);
253 if (value != NULL)
254 {
255 m_customAttributes.setPreallocated(name, value);
256 }
257 }
258 }
259 DBFreeResult(hResult);
260 }
261 else
262 {
263 success = false;
264 }
265 DBFreeStatement(hStmt);
266 }
267 else
268 {
269 success = false;
270 }
271 }
272
273 if (success)
274 success = loadTrustedNodes();
275
276 if (!success)
277 DbgPrintf(4, _T("NetObj::loadCommonProperties() failed for object %s [%ld] class=%d"), m_name, (long)m_id, getObjectClass());
278
279 return success;
280 }
281
282 /**
283 * Callback for saving custom attribute in database
284 */
285 static EnumerationCallbackResult SaveAttributeCallback(const TCHAR *key, const void *value, void *data)
286 {
287 DB_STATEMENT hStmt = (DB_STATEMENT)data;
288 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, key, DB_BIND_STATIC);
289 DBBind(hStmt, 3, DB_SQLTYPE_VARCHAR, (const TCHAR *)value, DB_BIND_STATIC);
290 return DBExecute(hStmt) ? _CONTINUE : _STOP;
291 }
292
293 /**
294 * Callback for saving module data in database
295 */
296 static EnumerationCallbackResult SaveModuleDataCallback(const TCHAR *key, const void *value, void *data)
297 {
298 return ((ModuleData *)value)->saveToDatabase(((ModuleDataDatabaseCallbackParams *)data)->hdb, ((ModuleDataDatabaseCallbackParams *)data)->id) ? _CONTINUE : _STOP;
299 }
300
301 /**
302 * Save common object properties to database
303 */
304 bool NetObj::saveCommonProperties(DB_HANDLE hdb)
305 {
306 DB_STATEMENT hStmt;
307 if (IsDatabaseRecordExist(hdb, _T("object_properties"), _T("object_id"), m_id))
308 {
309 hStmt = DBPrepare(hdb,
310 _T("UPDATE object_properties SET name=?,status=?,")
311 _T("is_deleted=?,inherit_access_rights=?,")
312 _T("last_modified=?,status_calc_alg=?,status_prop_alg=?,")
313 _T("status_fixed_val=?,status_shift=?,status_translation=?,")
314 _T("status_single_threshold=?,status_thresholds=?,")
315 _T("comments=?,is_system=?,location_type=?,latitude=?,")
316 _T("longitude=?,location_accuracy=?,location_timestamp=?,")
317 _T("guid=?,image=?,submap_id=?,country=?,city=?,")
318 _T("street_address=?,postcode=? WHERE object_id=?"));
319 }
320 else
321 {
322 hStmt = DBPrepare(hdb,
323 _T("INSERT INTO object_properties (name,status,is_deleted,")
324 _T("inherit_access_rights,last_modified,status_calc_alg,")
325 _T("status_prop_alg,status_fixed_val,status_shift,status_translation,")
326 _T("status_single_threshold,status_thresholds,comments,is_system,")
327 _T("location_type,latitude,longitude,location_accuracy,location_timestamp,")
328 _T("guid,image,submap_id,country,city,street_address,postcode,object_id) ")
329 _T("VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
330 }
331 if (hStmt == NULL)
332 return FALSE;
333
334 TCHAR szTranslation[16], szThresholds[16], lat[32], lon[32];
335 for(int i = 0, j = 0; i < 4; i++, j += 2)
336 {
337 _sntprintf(&szTranslation[j], 16 - j, _T("%02X"), (BYTE)m_iStatusTranslation[i]);
338 _sntprintf(&szThresholds[j], 16 - j, _T("%02X"), (BYTE)m_iStatusThresholds[i]);
339 }
340 _sntprintf(lat, 32, _T("%f"), m_geoLocation.getLatitude());
341 _sntprintf(lon, 32, _T("%f"), m_geoLocation.getLongitude());
342
343 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, m_name, DB_BIND_STATIC);
344 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, (LONG)m_iStatus);
345 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)(m_isDeleted ? 1 : 0));
346 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (LONG)m_bInheritAccessRights);
347 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (LONG)m_dwTimeStamp);
348 DBBind(hStmt, 6, DB_SQLTYPE_INTEGER, (LONG)m_iStatusCalcAlg);
349 DBBind(hStmt, 7, DB_SQLTYPE_INTEGER, (LONG)m_iStatusPropAlg);
350 DBBind(hStmt, 8, DB_SQLTYPE_INTEGER, (LONG)m_iFixedStatus);
351 DBBind(hStmt, 9, DB_SQLTYPE_INTEGER, (LONG)m_iStatusShift);
352 DBBind(hStmt, 10, DB_SQLTYPE_VARCHAR, szTranslation, DB_BIND_STATIC);
353 DBBind(hStmt, 11, DB_SQLTYPE_INTEGER, (LONG)m_iStatusSingleThreshold);
354 DBBind(hStmt, 12, DB_SQLTYPE_VARCHAR, szThresholds, DB_BIND_STATIC);
355 DBBind(hStmt, 13, DB_SQLTYPE_VARCHAR, m_pszComments, DB_BIND_STATIC);
356 DBBind(hStmt, 14, DB_SQLTYPE_INTEGER, (LONG)(m_isSystem ? 1 : 0));
357 DBBind(hStmt, 15, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getType());
358 DBBind(hStmt, 16, DB_SQLTYPE_VARCHAR, lat, DB_BIND_STATIC);
359 DBBind(hStmt, 17, DB_SQLTYPE_VARCHAR, lon, DB_BIND_STATIC);
360 DBBind(hStmt, 18, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getAccuracy());
361 DBBind(hStmt, 19, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
362 DBBind(hStmt, 20, DB_SQLTYPE_VARCHAR, m_guid);
363 DBBind(hStmt, 21, DB_SQLTYPE_VARCHAR, m_image);
364 DBBind(hStmt, 22, DB_SQLTYPE_INTEGER, m_submapId);
365 DBBind(hStmt, 23, DB_SQLTYPE_VARCHAR, m_postalAddress->getCountry(), DB_BIND_STATIC);
366 DBBind(hStmt, 24, DB_SQLTYPE_VARCHAR, m_postalAddress->getCity(), DB_BIND_STATIC);
367 DBBind(hStmt, 25, DB_SQLTYPE_VARCHAR, m_postalAddress->getStreetAddress(), DB_BIND_STATIC);
368 DBBind(hStmt, 26, DB_SQLTYPE_VARCHAR, m_postalAddress->getPostCode(), DB_BIND_STATIC);
369 DBBind(hStmt, 27, DB_SQLTYPE_INTEGER, m_id);
370
371 bool success = DBExecute(hStmt) ? true : false;
372 DBFreeStatement(hStmt);
373
374 // Save custom attributes
375 if (success)
376 {
377 TCHAR szQuery[512];
378 _sntprintf(szQuery, 512, _T("DELETE FROM object_custom_attributes WHERE object_id=%d"), m_id);
379 success = DBQuery(hdb, szQuery) ? true : false;
380 if (success)
381 {
382 hStmt = DBPrepare(hdb, _T("INSERT INTO object_custom_attributes (object_id,attr_name,attr_value) VALUES (?,?,?)"));
383 if (hStmt != NULL)
384 {
385 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
386 success = (m_customAttributes.forEach(SaveAttributeCallback, hStmt) == _CONTINUE);
387 DBFreeStatement(hStmt);
388 }
389 else
390 {
391 success = false;
392 }
393 }
394 }
395
396 // Save module data
397 if (success && (m_moduleData != NULL))
398 {
399 ModuleDataDatabaseCallbackParams data;
400 data.id = m_id;
401 data.hdb = hdb;
402 success = (m_moduleData->forEach(SaveModuleDataCallback, &data) == _CONTINUE);
403 }
404
405 if (success)
406 success = saveTrustedNodes(hdb);
407
408 return success;
409 }
410
411 /**
412 * Add reference to the new child object
413 */
414 void NetObj::AddChild(NetObj *pObject)
415 {
416 UINT32 i;
417
418 LockChildList(TRUE);
419 for(i = 0; i < m_dwChildCount; i++)
420 if (m_pChildList[i] == pObject)
421 {
422 UnlockChildList();
423 return; // Already in the child list
424 }
425 m_pChildList = (NetObj **)realloc(m_pChildList, sizeof(NetObj *) * (m_dwChildCount + 1));
426 m_pChildList[m_dwChildCount++] = pObject;
427 UnlockChildList();
428 incRefCount();
429 setModified();
430 }
431
432 /**
433 * Add reference to parent object
434 */
435 void NetObj::AddParent(NetObj *pObject)
436 {
437 UINT32 i;
438
439 LockParentList(TRUE);
440 for(i = 0; i < m_dwParentCount; i++)
441 if (m_pParentList[i] == pObject)
442 {
443 UnlockParentList();
444 return; // Already in the parents list
445 }
446 m_pParentList = (NetObj **)realloc(m_pParentList, sizeof(NetObj *) * (m_dwParentCount + 1));
447 m_pParentList[m_dwParentCount++] = pObject;
448 UnlockParentList();
449 incRefCount();
450 setModified();
451 }
452
453 /**
454 * Delete reference to child object
455 */
456 void NetObj::DeleteChild(NetObj *pObject)
457 {
458 UINT32 i;
459
460 LockChildList(TRUE);
461 for(i = 0; i < m_dwChildCount; i++)
462 if (m_pChildList[i] == pObject)
463 break;
464
465 if (i == m_dwChildCount) // No such object
466 {
467 UnlockChildList();
468 return;
469 }
470 m_dwChildCount--;
471 if (m_dwChildCount > 0)
472 {
473 memmove(&m_pChildList[i], &m_pChildList[i + 1], sizeof(NetObj *) * (m_dwChildCount - i));
474 m_pChildList = (NetObj **)realloc(m_pChildList, sizeof(NetObj *) * m_dwChildCount);
475 }
476 else
477 {
478 free(m_pChildList);
479 m_pChildList = NULL;
480 }
481 UnlockChildList();
482 decRefCount();
483 setModified();
484 }
485
486 /**
487 * Delete reference to parent object
488 */
489 void NetObj::DeleteParent(NetObj *pObject)
490 {
491 UINT32 i;
492
493 LockParentList(TRUE);
494 for(i = 0; i < m_dwParentCount; i++)
495 if (m_pParentList[i] == pObject)
496 break;
497 if (i == m_dwParentCount) // No such object
498 {
499 UnlockParentList();
500 return;
501 }
502 m_dwParentCount--;
503 if (m_dwParentCount > 0)
504 {
505 memmove(&m_pParentList[i], &m_pParentList[i + 1], sizeof(NetObj *) * (m_dwParentCount - i));
506 m_pParentList = (NetObj **)realloc(m_pParentList, sizeof(NetObj *) * m_dwParentCount);
507 }
508 else
509 {
510 free(m_pParentList);
511 m_pParentList = NULL;
512 }
513 UnlockParentList();
514 decRefCount();
515 setModified();
516 }
517
518 /**
519 * Walker callback to call OnObjectDelete for each active object
520 */
521 void NetObj::onObjectDeleteCallback(NetObj *object, void *data)
522 {
523 UINT32 currId = ((NetObj *)data)->getId();
524 if ((object->getId() != currId) && !object->isDeleted())
525 object->onObjectDelete(currId);
526 }
527
528 /**
529 * Prepare object for deletion - remove all references, etc.
530 *
531 * @param initiator pointer to parent object which causes recursive deletion or NULL
532 */
533 void NetObj::deleteObject(NetObj *initiator)
534 {
535 DbgPrintf(4, _T("Deleting object %d [%s]"), m_id, m_name);
536
537 // Prevent object change propagation until it's marked as deleted
538 // (to prevent the object's incorrect appearance in GUI
539 lockProperties();
540 m_isHidden = true;
541 unlockProperties();
542
543 // Notify modules about object deletion
544 CALL_ALL_MODULES(pfPreObjectDelete, (this));
545
546 prepareForDeletion();
547
548 DbgPrintf(5, _T("NetObj::deleteObject(): deleting object %d from indexes"), m_id);
549 NetObjDeleteFromIndexes(this);
550
551 // Delete references to this object from child objects
552 DbgPrintf(5, _T("NetObj::deleteObject(): clearing child list for object %d"), m_id);
553 ObjectArray<NetObj> *deleteList = NULL;
554 LockChildList(TRUE);
555 for(UINT32 i = 0; i < m_dwChildCount; i++)
556 {
557 if (m_pChildList[i]->getParentCount() == 1)
558 {
559 // last parent, delete object
560 if (deleteList == NULL)
561 deleteList = new ObjectArray<NetObj>(16, 16, false);
562 deleteList->add(m_pChildList[i]);
563 }
564 else
565 {
566 m_pChildList[i]->DeleteParent(this);
567 }
568 decRefCount();
569 }
570 free(m_pChildList);
571 m_pChildList = NULL;
572 m_dwChildCount = 0;
573 UnlockChildList();
574
575 // Delete orphaned child objects
576 if (deleteList != NULL)
577 {
578 for(int i = 0; i < deleteList->size(); i++)
579 {
580 NetObj *o = deleteList->get(i);
581 DbgPrintf(5, _T("NetObj::deleteObject(): calling deleteObject() on %s [%d]"), o->getName(), o->getId());
582 o->deleteObject(this);
583 }
584 delete deleteList;
585 }
586
587 // Remove references to this object from parent objects
588 DbgPrintf(5, _T("NetObj::Delete(): clearing parent list for object %d"), m_id);
589 LockParentList(TRUE);
590 for(UINT32 i = 0; i < m_dwParentCount; i++)
591 {
592 // If parent is deletion initiator then this object already
593 // removed from parent's list
594 if (m_pParentList[i] != initiator)
595 {
596 m_pParentList[i]->DeleteChild(this);
597 m_pParentList[i]->calculateCompoundStatus();
598 }
599 decRefCount();
600 }
601 free(m_pParentList);
602 m_pParentList = NULL;
603 m_dwParentCount = 0;
604 UnlockParentList();
605
606 lockProperties();
607 m_isHidden = false;
608 m_isDeleted = true;
609 setModified();
610 unlockProperties();
611
612 // Notify all other objects about object deletion
613 DbgPrintf(5, _T("NetObj::deleteObject(): calling onObjectDelete(%d)"), m_id);
614 g_idxObjectById.forEach(onObjectDeleteCallback, this);
615
616 DbgPrintf(4, _T("Object %d successfully deleted"), m_id);
617 }
618
619 /**
620 * Default handler for object deletion notification
621 */
622 void NetObj::onObjectDelete(UINT32 dwObjectId)
623 {
624 }
625
626 /**
627 * Get childs IDs in printable form
628 */
629 const TCHAR *NetObj::dbgGetChildList(TCHAR *szBuffer)
630 {
631 UINT32 i;
632 TCHAR *pBuf = szBuffer;
633
634 *pBuf = 0;
635 LockChildList(FALSE);
636 for(i = 0, pBuf = szBuffer; i < m_dwChildCount; i++)
637 {
638 _sntprintf(pBuf, 10, _T("%d "), m_pChildList[i]->getId());
639 while(*pBuf)
640 pBuf++;
641 }
642 UnlockChildList();
643 if (pBuf != szBuffer)
644 *(pBuf - 1) = 0;
645 return szBuffer;
646 }
647
648 /**
649 * Get parents IDs in printable form
650 */
651 const TCHAR *NetObj::dbgGetParentList(TCHAR *szBuffer)
652 {
653 UINT32 i;
654 TCHAR *pBuf = szBuffer;
655
656 *pBuf = 0;
657 LockParentList(FALSE);
658 for(i = 0; i < m_dwParentCount; i++)
659 {
660 _sntprintf(pBuf, 10, _T("%d "), m_pParentList[i]->getId());
661 while(*pBuf)
662 pBuf++;
663 }
664 UnlockParentList();
665 if (pBuf != szBuffer)
666 *(pBuf - 1) = 0;
667 return szBuffer;
668 }
669
670 /**
671 * Calculate status for compound object based on childs' status
672 */
673 void NetObj::calculateCompoundStatus(BOOL bForcedRecalc)
674 {
675 if (m_iStatus == STATUS_UNMANAGED)
676 return;
677
678 int mostCriticalAlarm = GetMostCriticalStatusForObject(m_id);
679 int mostCriticalDCI =
680 (getObjectClass() == OBJECT_NODE || getObjectClass() == OBJECT_MOBILEDEVICE || getObjectClass() == OBJECT_CLUSTER || getObjectClass() == OBJECT_ACCESSPOINT) ?
681 ((DataCollectionTarget *)this)->getMostCriticalDCIStatus() : STATUS_UNKNOWN;
682
683 UINT32 i;
684 int oldStatus = m_iStatus;
685 int mostCriticalStatus, count, iStatusAlg;
686 int nSingleThreshold, *pnThresholds;
687 int nRating[5], iChildStatus, nThresholds[4];
688
689 lockProperties();
690 if (m_iStatusCalcAlg == SA_CALCULATE_DEFAULT)
691 {
692 iStatusAlg = GetDefaultStatusCalculation(&nSingleThreshold, &pnThresholds);
693 }
694 else
695 {
696 iStatusAlg = m_iStatusCalcAlg;
697 nSingleThreshold = m_iStatusSingleThreshold;
698 pnThresholds = m_iStatusThresholds;
699 }
700 if (iStatusAlg == SA_CALCULATE_SINGLE_THRESHOLD)
701 {
702 for(i = 0; i < 4; i++)
703 nThresholds[i] = nSingleThreshold;
704 pnThresholds = nThresholds;
705 }
706
707 switch(iStatusAlg)
708 {
709 case SA_CALCULATE_MOST_CRITICAL:
710 LockChildList(FALSE);
711 for(i = 0, count = 0, mostCriticalStatus = -1; i < m_dwChildCount; i++)
712 {
713 iChildStatus = m_pChildList[i]->getPropagatedStatus();
714 if ((iChildStatus < STATUS_UNKNOWN) &&
715 (iChildStatus > mostCriticalStatus))
716 {
717 mostCriticalStatus = iChildStatus;
718 count++;
719 }
720 }
721 m_iStatus = (count > 0) ? mostCriticalStatus : STATUS_UNKNOWN;
722 UnlockChildList();
723 break;
724 case SA_CALCULATE_SINGLE_THRESHOLD:
725 case SA_CALCULATE_MULTIPLE_THRESHOLDS:
726 // Step 1: calculate severity raitings
727 memset(nRating, 0, sizeof(int) * 5);
728 LockChildList(FALSE);
729 for(i = 0, count = 0; i < m_dwChildCount; i++)
730 {
731 iChildStatus = m_pChildList[i]->getPropagatedStatus();
732 if (iChildStatus < STATUS_UNKNOWN)
733 {
734 while(iChildStatus >= 0)
735 nRating[iChildStatus--]++;
736 count++;
737 }
738 }
739 UnlockChildList();
740
741 // Step 2: check what severity rating is above threshold
742 if (count > 0)
743 {
744 for(i = 4; i > 0; i--)
745 if (nRating[i] * 100 / count >= pnThresholds[i - 1])
746 break;
747 m_iStatus = i;
748 }
749 else
750 {
751 m_iStatus = STATUS_UNKNOWN;
752 }
753 break;
754 default:
755 m_iStatus = STATUS_UNKNOWN;
756 break;
757 }
758
759 // If alarms exist for object, apply alarm severity to object's status
760 if (mostCriticalAlarm != STATUS_UNKNOWN)
761 {
762 if (m_iStatus == STATUS_UNKNOWN)
763 {
764 m_iStatus = mostCriticalAlarm;
765 }
766 else
767 {
768 m_iStatus = max(m_iStatus, mostCriticalAlarm);
769 }
770 }
771
772 // If DCI status is calculated for object apply DCI object's statud
773 if (mostCriticalDCI != STATUS_UNKNOWN)
774 {
775 if (m_iStatus == STATUS_UNKNOWN)
776 {
777 m_iStatus = mostCriticalDCI;
778 }
779 else
780 {
781 m_iStatus = max(m_iStatus, mostCriticalDCI);
782 }
783 }
784
785 // Query loaded modules for object status
786 ENUMERATE_MODULES(pfCalculateObjectStatus)
787 {
788 int moduleStatus = g_pModuleList[__i].pfCalculateObjectStatus(this);
789 if (moduleStatus != STATUS_UNKNOWN)
790 {
791 if (m_iStatus == STATUS_UNKNOWN)
792 {
793 m_iStatus = moduleStatus;
794 }
795 else
796 {
797 m_iStatus = max(m_iStatus, moduleStatus);
798 }
799 }
800 }
801
802 unlockProperties();
803
804 // Cause parent object(s) to recalculate it's status
805 if ((oldStatus != m_iStatus) || bForcedRecalc)
806 {
807 LockParentList(FALSE);
808 for(i = 0; i < m_dwParentCount; i++)
809 m_pParentList[i]->calculateCompoundStatus();
810 UnlockParentList();
811 lockProperties();
812 setModified();
813 unlockProperties();
814 }
815 }
816
817 /**
818 * Load ACL from database
819 */
820 bool NetObj::loadACLFromDB()
821 {
822 bool success = false;
823
824 DB_STATEMENT hStmt = DBPrepare(g_hCoreDB, _T("SELECT user_id,access_rights FROM acl WHERE object_id=?"));
825 if (hStmt != NULL)
826 {
827 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, m_id);
828 DB_RESULT hResult = DBSelectPrepared(hStmt);
829 if (hResult != NULL)
830 {
831 int i, iNumRows;
832
833 iNumRows = DBGetNumRows(hResult);
834 for(i = 0; i < iNumRows; i++)
835 m_pAccessList->addElement(DBGetFieldULong(hResult, i, 0),
836 DBGetFieldULong(hResult, i, 1));
837 DBFreeResult(hResult);
838 success = true;
839 }
840 DBFreeStatement(hStmt);
841 }
842 return success;
843 }
844
845 /**
846 * ACL enumeration parameters structure
847 */
848 struct SAVE_PARAM
849 {
850 DB_HANDLE hdb;
851 UINT32 dwObjectId;
852 };
853
854 /**
855 * Handler for ACL elements enumeration
856 */
857 static void EnumerationHandler(UINT32 dwUserId, UINT32 dwAccessRights, void *pArg)
858 {
859 TCHAR szQuery[256];
860
861 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("INSERT INTO acl (object_id,user_id,access_rights) VALUES (%d,%d,%d)"),
862 ((SAVE_PARAM *)pArg)->dwObjectId, dwUserId, dwAccessRights);
863 DBQuery(((SAVE_PARAM *)pArg)->hdb, szQuery);
864 }
865
866 /**
867 * Save ACL to database
868 */
869 bool NetObj::saveACLToDB(DB_HANDLE hdb)
870 {
871 TCHAR szQuery[256];
872 bool success = false;
873 SAVE_PARAM sp;
874
875 // Save access list
876 lockACL();
877 _sntprintf(szQuery, sizeof(szQuery) / sizeof(TCHAR), _T("DELETE FROM acl WHERE object_id=%d"), m_id);
878 if (DBQuery(hdb, szQuery))
879 {
880 sp.dwObjectId = m_id;
881 sp.hdb = hdb;
882 m_pAccessList->enumerateElements(EnumerationHandler, &sp);
883 success = true;
884 }
885 unlockACL();
886 return success;
887 }
888
889 /**
890 * Data for SendModuleDataCallback
891 */
892 struct SendModuleDataCallbackData
893 {
894 NXCPMessage *msg;
895 UINT32 id;
896 };
897
898 /**
899 * Callback for sending module data in NXCP message
900 */
901 static EnumerationCallbackResult SendModuleDataCallback(const TCHAR *key, const void *value, void *data)
902 {
903 ((SendModuleDataCallbackData *)data)->msg->setField(((SendModuleDataCallbackData *)data)->id, key);
904 ((ModuleData *)value)->fillMessage(((SendModuleDataCallbackData *)data)->msg, ((SendModuleDataCallbackData *)data)->id + 1);
905 ((SendModuleDataCallbackData *)data)->id += 0x100000;
906 return _CONTINUE;
907 }
908
909 /**
910 * Fill NXCP message with object's data
911 * Object's properties are locked when this method is called. Method should not do any other locks.
912 * Data required other locks should be filled in fillMessageInternalStage2().
913 */
914 void NetObj::fillMessageInternal(NXCPMessage *pMsg)
915 {
916 pMsg->setField(VID_OBJECT_CLASS, (WORD)getObjectClass());
917 pMsg->setField(VID_OBJECT_ID, m_id);
918 pMsg->setField(VID_GUID, m_guid);
919 pMsg->setField(VID_OBJECT_NAME, m_name);
920 pMsg->setField(VID_OBJECT_STATUS, (WORD)m_iStatus);
921 pMsg->setField(VID_IS_DELETED, (WORD)(m_isDeleted ? 1 : 0));
922 pMsg->setField(VID_IS_SYSTEM, (WORD)(m_isSystem ? 1 : 0));
923
924 pMsg->setField(VID_INHERIT_RIGHTS, (WORD)m_bInheritAccessRights);
925 pMsg->setField(VID_STATUS_CALCULATION_ALG, (WORD)m_iStatusCalcAlg);
926 pMsg->setField(VID_STATUS_PROPAGATION_ALG, (WORD)m_iStatusPropAlg);
927 pMsg->setField(VID_FIXED_STATUS, (WORD)m_iFixedStatus);
928 pMsg->setField(VID_STATUS_SHIFT, (WORD)m_iStatusShift);
929 pMsg->setField(VID_STATUS_TRANSLATION_1, (WORD)m_iStatusTranslation[0]);
930 pMsg->setField(VID_STATUS_TRANSLATION_2, (WORD)m_iStatusTranslation[1]);
931 pMsg->setField(VID_STATUS_TRANSLATION_3, (WORD)m_iStatusTranslation[2]);
932 pMsg->setField(VID_STATUS_TRANSLATION_4, (WORD)m_iStatusTranslation[3]);
933 pMsg->setField(VID_STATUS_SINGLE_THRESHOLD, (WORD)m_iStatusSingleThreshold);
934 pMsg->setField(VID_STATUS_THRESHOLD_1, (WORD)m_iStatusThresholds[0]);
935 pMsg->setField(VID_STATUS_THRESHOLD_2, (WORD)m_iStatusThresholds[1]);
936 pMsg->setField(VID_STATUS_THRESHOLD_3, (WORD)m_iStatusThresholds[2]);
937 pMsg->setField(VID_STATUS_THRESHOLD_4, (WORD)m_iStatusThresholds[3]);
938 pMsg->setField(VID_COMMENTS, CHECK_NULL_EX(m_pszComments));
939 pMsg->setField(VID_IMAGE, m_image);
940 pMsg->setField(VID_SUBMAP_ID, m_submapId);
941 pMsg->setField(VID_NUM_TRUSTED_NODES, m_dwNumTrustedNodes);
942 if (m_dwNumTrustedNodes > 0)
943 pMsg->setFieldFromInt32Array(VID_TRUSTED_NODES, m_dwNumTrustedNodes, m_pdwTrustedNodes);
944
945 m_customAttributes.fillMessage(pMsg, VID_NUM_CUSTOM_ATTRIBUTES, VID_CUSTOM_ATTRIBUTES_BASE);
946
947 m_pAccessList->fillMessage(pMsg);
948 m_geoLocation.fillMessage(*pMsg);
949
950 pMsg->setField(VID_COUNTRY, m_postalAddress->getCountry());
951 pMsg->setField(VID_CITY, m_postalAddress->getCity());
952 pMsg->setField(VID_STREET_ADDRESS, m_postalAddress->getStreetAddress());
953 pMsg->setField(VID_POSTCODE, m_postalAddress->getPostCode());
954
955 if (m_moduleData != NULL)
956 {
957 pMsg->setField(VID_MODULE_DATA_COUNT, (UINT16)m_moduleData->size());
958 SendModuleDataCallbackData data;
959 data.msg = pMsg;
960 data.id = VID_MODULE_DATA_BASE;
961 m_moduleData->forEach(SendModuleDataCallback, &data);
962 }
963 else
964 {
965 pMsg->setField(VID_MODULE_DATA_COUNT, (UINT16)0);
966 }
967 }
968
969 /**
970 * Fill NXCP message with object's data - stage 2
971 * Object's properties are not locked when this method is called. Should be
972 * used only to fill data where properties lock is not enough (like data
973 * collection configuration).
974 */
975 void NetObj::fillMessageInternalStage2(NXCPMessage *pMsg)
976 {
977 }
978
979 /**
980 * Fill NXCP message with object's data
981 */
982 void NetObj::fillMessage(NXCPMessage *msg)
983 {
984 lockProperties();
985 fillMessageInternal(msg);
986 unlockProperties();
987 fillMessageInternalStage2(msg);
988
989 UINT32 i, dwId;
990
991 LockParentList(FALSE);
992 msg->setField(VID_PARENT_CNT, m_dwParentCount);
993 for(i = 0, dwId = VID_PARENT_ID_BASE; i < m_dwParentCount; i++, dwId++)
994 msg->setField(dwId, m_pParentList[i]->getId());
995 UnlockParentList();
996
997 LockChildList(FALSE);
998 msg->setField(VID_CHILD_CNT, m_dwChildCount);
999 for(i = 0, dwId = VID_CHILD_ID_BASE; i < m_dwChildCount; i++, dwId++)
1000 msg->setField(dwId, m_pChildList[i]->getId());
1001 UnlockChildList();
1002 }
1003
1004 /**
1005 * Handler for EnumerateSessions()
1006 */
1007 static void BroadcastObjectChange(ClientSession *pSession, void *pArg)
1008 {
1009 if (pSession->isAuthenticated())
1010 pSession->onObjectChange((NetObj *)pArg);
1011 }
1012
1013 /**
1014 * Mark object as modified and put on client's notification queue
1015 * We assume that object is locked at the time of function call
1016 */
1017 void NetObj::setModified()
1018 {
1019 if (g_bModificationsLocked)
1020 return;
1021
1022 m_isModified = true;
1023 m_dwTimeStamp = (UINT32)time(NULL);
1024
1025 // Send event to all connected clients
1026 if (!m_isHidden && !m_isSystem)
1027 EnumerateClientSessions(BroadcastObjectChange, this);
1028 }
1029
1030 /**
1031 * Modify object from NXCP message - common wrapper
1032 */
1033 UINT32 NetObj::modifyFromMessage(NXCPMessage *msg)
1034 {
1035 lockProperties();
1036 UINT32 rcc = modifyFromMessageInternal(msg);
1037 setModified();
1038 unlockProperties();
1039 return rcc;
1040 }
1041
1042 /**
1043 * Modify object from NXCP message
1044 */
1045 UINT32 NetObj::modifyFromMessageInternal(NXCPMessage *pRequest)
1046 {
1047 // Change object's name
1048 if (pRequest->isFieldExist(VID_OBJECT_NAME))
1049 pRequest->getFieldAsString(VID_OBJECT_NAME, m_name, MAX_OBJECT_NAME);
1050
1051 // Change object's status calculation/propagation algorithms
1052 if (pRequest->isFieldExist(VID_STATUS_CALCULATION_ALG))
1053 {
1054 m_iStatusCalcAlg = pRequest->getFieldAsInt16(VID_STATUS_CALCULATION_ALG);
1055 m_iStatusPropAlg = pRequest->getFieldAsInt16(VID_STATUS_PROPAGATION_ALG);
1056 m_iFixedStatus = pRequest->getFieldAsInt16(VID_FIXED_STATUS);
1057 m_iStatusShift = pRequest->getFieldAsInt16(VID_STATUS_SHIFT);
1058 m_iStatusTranslation[0] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_1);
1059 m_iStatusTranslation[1] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_2);
1060 m_iStatusTranslation[2] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_3);
1061 m_iStatusTranslation[3] = pRequest->getFieldAsInt16(VID_STATUS_TRANSLATION_4);
1062 m_iStatusSingleThreshold = pRequest->getFieldAsInt16(VID_STATUS_SINGLE_THRESHOLD);
1063 m_iStatusThresholds[0] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_1);
1064 m_iStatusThresholds[1] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_2);
1065 m_iStatusThresholds[2] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_3);
1066 m_iStatusThresholds[3] = pRequest->getFieldAsInt16(VID_STATUS_THRESHOLD_4);
1067 }
1068
1069 // Change image
1070 if (pRequest->isFieldExist(VID_IMAGE))
1071 m_image = pRequest->getFieldAsGUID(VID_IMAGE);
1072
1073 // Change object's ACL
1074 if (pRequest->isFieldExist(VID_ACL_SIZE))
1075 {
1076 UINT32 i, dwNumElements;
1077
1078 lockACL();
1079 dwNumElements = pRequest->getFieldAsUInt32(VID_ACL_SIZE);
1080 m_bInheritAccessRights = pRequest->getFieldAsUInt16(VID_INHERIT_RIGHTS);
1081 m_pAccessList->deleteAll();
1082 for(i = 0; i < dwNumElements; i++)
1083 m_pAccessList->addElement(pRequest->getFieldAsUInt32(VID_ACL_USER_BASE + i),
1084 pRequest->getFieldAsUInt32(VID_ACL_RIGHTS_BASE +i));
1085 unlockACL();
1086 }
1087
1088 // Change trusted nodes list
1089 if (pRequest->isFieldExist(VID_NUM_TRUSTED_NODES))
1090 {
1091 m_dwNumTrustedNodes = pRequest->getFieldAsUInt32(VID_NUM_TRUSTED_NODES);
1092 m_pdwTrustedNodes = (UINT32 *)realloc(m_pdwTrustedNodes, sizeof(UINT32) * m_dwNumTrustedNodes);
1093 pRequest->getFieldAsInt32Array(VID_TRUSTED_NODES, m_dwNumTrustedNodes, m_pdwTrustedNodes);
1094 }
1095
1096 // Change custom attributes
1097 if (pRequest->isFieldExist(VID_NUM_CUSTOM_ATTRIBUTES))
1098 {
1099 UINT32 i, dwId, dwNumElements;
1100 TCHAR *name, *value;
1101
1102 dwNumElements = pRequest->getFieldAsUInt32(VID_NUM_CUSTOM_ATTRIBUTES);
1103 m_customAttributes.clear();
1104 for(i = 0, dwId = VID_CUSTOM_ATTRIBUTES_BASE; i < dwNumElements; i++)
1105 {
1106 name = pRequest->getFieldAsString(dwId++);
1107 value = pRequest->getFieldAsString(dwId++);
1108 if ((name != NULL) && (value != NULL))
1109 m_customAttributes.setPreallocated(name, value);
1110 }
1111 }
1112
1113 // Change geolocation
1114 if (pRequest->isFieldExist(VID_GEOLOCATION_TYPE))
1115 {
1116 m_geoLocation = GeoLocation(*pRequest);
1117 addLocationToHistory();
1118 }
1119
1120 if (pRequest->isFieldExist(VID_SUBMAP_ID))
1121 {
1122 m_submapId = pRequest->getFieldAsUInt32(VID_SUBMAP_ID);
1123 }
1124
1125 if (pRequest->isFieldExist(VID_COUNTRY))
1126 {
1127 TCHAR buffer[64];
1128 pRequest->getFieldAsString(VID_COUNTRY, buffer, 64);
1129 m_postalAddress->setCountry(buffer);
1130 }
1131
1132 if (pRequest->isFieldExist(VID_CITY))
1133 {
1134 TCHAR buffer[64];
1135 pRequest->getFieldAsString(VID_CITY, buffer, 64);
1136 m_postalAddress->setCity(buffer);
1137 }
1138
1139 if (pRequest->isFieldExist(VID_STREET_ADDRESS))
1140 {
1141 TCHAR buffer[256];
1142 pRequest->getFieldAsString(VID_STREET_ADDRESS, buffer, 256);
1143 m_postalAddress->setStreetAddress(buffer);
1144 }
1145
1146 if (pRequest->isFieldExist(VID_POSTCODE))
1147 {
1148 TCHAR buffer[32];
1149 pRequest->getFieldAsString(VID_POSTCODE, buffer, 32);
1150 m_postalAddress->setPostCode(buffer);
1151 }
1152
1153 return RCC_SUCCESS;
1154 }
1155
1156 /**
1157 * Post-modify hook
1158 */
1159 void NetObj::postModify()
1160 {
1161 calculateCompoundStatus(TRUE);
1162 }
1163
1164 /**
1165 * Get rights to object for specific user
1166 *
1167 * @param userId user object ID
1168 */
1169 UINT32 NetObj::getUserRights(UINT32 userId)
1170 {
1171 UINT32 dwRights;
1172
1173 // Admin always has all rights to any object
1174 if (userId == 0)
1175 return 0xFFFFFFFF;
1176
1177 // Non-admin users have no rights to system objects
1178 if (m_isSystem)
1179 return 0;
1180
1181 // Check if have direct right assignment
1182 lockACL();
1183 bool hasDirectRights = m_pAccessList->getUserRights(userId, &dwRights);
1184 unlockACL();
1185
1186 if (!hasDirectRights)
1187 {
1188 // We don't. If this object inherit rights from parents, get them
1189 if (m_bInheritAccessRights)
1190 {
1191 UINT32 i;
1192
1193 LockParentList(FALSE);
1194 for(i = 0, dwRights = 0; i < m_dwParentCount; i++)
1195 dwRights |= m_pParentList[i]->getUserRights(userId);
1196 UnlockParentList();
1197 }
1198 }
1199
1200 return dwRights;
1201 }
1202
1203 /**
1204 * Check if given user has specific rights on this object
1205 *
1206 * @param userId user object ID
1207 * @param requiredRights bit mask of requested right
1208 * @return true if user has all rights specified in requested rights bit mask
1209 */
1210 BOOL NetObj::checkAccessRights(UINT32 userId, UINT32 requiredRights)
1211 {
1212 UINT32 effectiveRights = getUserRights(userId);
1213 return (effectiveRights & requiredRights) == requiredRights;
1214 }
1215
1216 /**
1217 * Drop all user privileges on current object
1218 */
1219 void NetObj::dropUserAccess(UINT32 dwUserId)
1220 {
1221 lockACL();
1222 bool modified = m_pAccessList->deleteElement(dwUserId);
1223 unlockACL();
1224 if (modified)
1225 {
1226 lockProperties();
1227 setModified();
1228 unlockProperties();
1229 }
1230 }
1231
1232 /**
1233 * Set object's management status
1234 */
1235 void NetObj::setMgmtStatus(BOOL bIsManaged)
1236 {
1237 UINT32 i;
1238 int oldStatus;
1239
1240 lockProperties();
1241
1242 if ((bIsManaged && (m_iStatus != STATUS_UNMANAGED)) ||
1243 ((!bIsManaged) && (m_iStatus == STATUS_UNMANAGED)))
1244 {
1245 unlockProperties();
1246 return; // Status is already correct
1247 }
1248
1249 oldStatus = m_iStatus;
1250 m_iStatus = (bIsManaged ? STATUS_UNKNOWN : STATUS_UNMANAGED);
1251
1252 // Generate event if current object is a node
1253 if (getObjectClass() == OBJECT_NODE)
1254 PostEvent(bIsManaged ? EVENT_NODE_UNKNOWN : EVENT_NODE_UNMANAGED, m_id, "d", oldStatus);
1255
1256 setModified();
1257 unlockProperties();
1258
1259 // Change status for child objects also
1260 LockChildList(FALSE);
1261 for(i = 0; i < m_dwChildCount; i++)
1262 m_pChildList[i]->setMgmtStatus(bIsManaged);
1263 UnlockChildList();
1264
1265 // Cause parent object(s) to recalculate it's status
1266 LockParentList(FALSE);
1267 for(i = 0; i < m_dwParentCount; i++)
1268 m_pParentList[i]->calculateCompoundStatus();
1269 UnlockParentList();
1270 }
1271
1272 /**
1273 * Check if given object is an our child (possibly indirect, i.e child of child)
1274 *
1275 * @param id object ID to test
1276 */
1277 bool NetObj::isChild(UINT32 id)
1278 {
1279 UINT32 i;
1280 bool bResult = false;
1281
1282 // Check for our own ID (object ID should never change, so we may not lock object's data)
1283 if (m_id == id)
1284 bResult = true;
1285
1286 // First, walk through our own child list
1287 if (!bResult)
1288 {
1289 LockChildList(FALSE);
1290 for(i = 0; i < m_dwChildCount; i++)
1291 if (m_pChildList[i]->getId() == id)
1292 {
1293 bResult = true;
1294 break;
1295 }
1296 UnlockChildList();
1297 }
1298
1299 // If given object is not in child list, check if it is indirect child
1300 if (!bResult)
1301 {
1302 LockChildList(FALSE);
1303 for(i = 0; i < m_dwChildCount; i++)
1304 if (m_pChildList[i]->isChild(id))
1305 {
1306 bResult = true;
1307 break;
1308 }
1309 UnlockChildList();
1310 }
1311
1312 return bResult;
1313 }
1314
1315 /**
1316 * Send message to client, who requests poll, if any
1317 * This method is used by Node and Interface class objects
1318 */
1319 void NetObj::sendPollerMsg(UINT32 dwRqId, const TCHAR *pszFormat, ...)
1320 {
1321 if (m_pollRequestor != NULL)
1322 {
1323 va_list args;
1324 TCHAR szBuffer[1024];
1325
1326 va_start(args, pszFormat);
1327 _vsntprintf(szBuffer, 1024, pszFormat, args);
1328 va_end(args);
1329 m_pollRequestor->sendPollerMsg(dwRqId, szBuffer);
1330 }
1331 }
1332
1333 /**
1334 * Add child node objects (direct and indirect childs) to list
1335 */
1336 void NetObj::addChildNodesToList(ObjectArray<Node> *nodeList, UINT32 dwUserId)
1337 {
1338 UINT32 i;
1339
1340 LockChildList(FALSE);
1341
1342 // Walk through our own child list
1343 for(i = 0; i < m_dwChildCount; i++)
1344 {
1345 if (m_pChildList[i]->getObjectClass() == OBJECT_NODE)
1346 {
1347 // Check if this node already in the list
1348 int j;
1349 for(j = 0; j < nodeList->size(); j++)
1350 if (nodeList->get(j)->getId() == m_pChildList[i]->getId())
1351 break;
1352 if (j == nodeList->size())
1353 {
1354 m_pChildList[i]->incRefCount();
1355 nodeList->add((Node *)m_pChildList[i]);
1356 }
1357 }
1358 else
1359 {
1360 if (m_pChildList[i]->checkAccessRights(dwUserId, OBJECT_ACCESS_READ))
1361 m_pChildList[i]->addChildNodesToList(nodeList, dwUserId);
1362 }
1363 }
1364
1365 UnlockChildList();
1366 }
1367
1368 /**
1369 * Add child data collection targets (direct and indirect childs) to list
1370 */
1371 void NetObj::addChildDCTargetsToList(ObjectArray<DataCollectionTarget> *dctList, UINT32 dwUserId)
1372 {
1373 UINT32 i;
1374
1375 LockChildList(FALSE);
1376
1377 // Walk through our own child list
1378 for(i = 0; i < m_dwChildCount; i++)
1379 {
1380 if ((m_pChildList[i]->getObjectClass() == OBJECT_NODE) || (m_pChildList[i]->getObjectClass() == OBJECT_MOBILEDEVICE))
1381 {
1382 // Check if this objects already in the list
1383 int j;
1384 for(j = 0; j < dctList->size(); j++)
1385 if (dctList->get(j)->getId() == m_pChildList[i]->getId())
1386 break;
1387 if (j == dctList->size())
1388 {
1389 m_pChildList[i]->incRefCount();
1390 dctList->add((DataCollectionTarget *)m_pChildList[i]);
1391 }
1392 }
1393 else
1394 {
1395 if (m_pChildList[i]->checkAccessRights(dwUserId, OBJECT_ACCESS_READ))
1396 m_pChildList[i]->addChildDCTargetsToList(dctList, dwUserId);
1397 }
1398 }
1399
1400 UnlockChildList();
1401 }
1402
1403 /**
1404 * Hide object and all its childs
1405 */
1406 void NetObj::hide()
1407 {
1408 UINT32 i;
1409
1410 LockChildList(FALSE);
1411 for(i = 0; i < m_dwChildCount; i++)
1412 m_pChildList[i]->hide();
1413 UnlockChildList();
1414
1415 lockProperties();
1416 m_isHidden = true;
1417 unlockProperties();
1418 }
1419
1420 /**
1421 * Unhide object and all its childs
1422 */
1423 void NetObj::unhide()
1424 {
1425 UINT32 i;
1426
1427 lockProperties();
1428 m_isHidden = false;
1429 if (!m_isSystem)
1430 EnumerateClientSessions(BroadcastObjectChange, this);
1431 unlockProperties();
1432
1433 LockChildList(FALSE);
1434 for(i = 0; i < m_dwChildCount; i++)
1435 m_pChildList[i]->unhide();
1436 UnlockChildList();
1437 }
1438
1439 /**
1440 * Return status propagated to parent
1441 */
1442 int NetObj::getPropagatedStatus()
1443 {
1444 int iStatus;
1445
1446 if (m_iStatusPropAlg == SA_PROPAGATE_DEFAULT)
1447 {
1448 iStatus = DefaultPropagatedStatus(m_iStatus);
1449 }
1450 else
1451 {
1452 switch(m_iStatusPropAlg)
1453 {
1454 case SA_PROPAGATE_UNCHANGED:
1455 iStatus = m_iStatus;
1456 break;
1457 case SA_PROPAGATE_FIXED:
1458 iStatus = ((m_iStatus > STATUS_NORMAL) && (m_iStatus < STATUS_UNKNOWN)) ? m_iFixedStatus : m_iStatus;
1459 break;
1460 case SA_PROPAGATE_RELATIVE:
1461 if ((m_iStatus > STATUS_NORMAL) && (m_iStatus < STATUS_UNKNOWN))
1462 {
1463 iStatus = m_iStatus + m_iStatusShift;
1464 if (iStatus < 0)
1465 iStatus = 0;
1466 if (iStatus > STATUS_CRITICAL)
1467 iStatus = STATUS_CRITICAL;
1468 }
1469 else
1470 {
1471 iStatus = m_iStatus;
1472 }
1473 break;
1474 case SA_PROPAGATE_TRANSLATED:
1475 if ((m_iStatus > STATUS_NORMAL) && (m_iStatus < STATUS_UNKNOWN))
1476 {
1477 iStatus = m_iStatusTranslation[m_iStatus - 1];
1478 }
1479 else
1480 {
1481 iStatus = m_iStatus;
1482 }
1483 break;
1484 default:
1485 iStatus = STATUS_UNKNOWN;
1486 break;
1487 }
1488 }
1489 return iStatus;
1490 }
1491
1492 /**
1493 * Prepare object for deletion. Method should return only
1494 * when object deletion is safe
1495 */
1496 void NetObj::prepareForDeletion()
1497 {
1498 }
1499
1500 /**
1501 * Set object's comments.
1502 * NOTE: pszText should be dynamically allocated or NULL
1503 */
1504 void NetObj::setComments(TCHAR *text)
1505 {
1506 lockProperties();
1507 safe_free(m_pszComments);
1508 m_pszComments = text;
1509 setModified();
1510 unlockProperties();
1511 }
1512
1513 /**
1514 * Copy object's comments to NXCP message
1515 */
1516 void NetObj::commentsToMessage(NXCPMessage *pMsg)
1517 {
1518 lockProperties();
1519 pMsg->setField(VID_COMMENTS, CHECK_NULL_EX(m_pszComments));
1520 unlockProperties();
1521 }
1522
1523 /**
1524 * Load trusted nodes list from database
1525 */
1526 bool NetObj::loadTrustedNodes()
1527 {
1528 DB_RESULT hResult;
1529 TCHAR query[256];
1530 int i, count;
1531
1532 _sntprintf(query, 256, _T("SELECT target_node_id FROM trusted_nodes WHERE source_object_id=%d"), m_id);
1533 hResult = DBSelect(g_hCoreDB, query);
1534 if (hResult != NULL)
1535 {
1536 count = DBGetNumRows(hResult);
1537 if (count > 0)
1538 {
1539 m_dwNumTrustedNodes = count;
1540 m_pdwTrustedNodes = (UINT32 *)malloc(sizeof(UINT32) * count);
1541 for(i = 0; i < count; i++)
1542 {
1543 m_pdwTrustedNodes[i] = DBGetFieldULong(hResult, i, 0);
1544 }
1545 }
1546 DBFreeResult(hResult);
1547 }
1548 return (hResult != NULL);
1549 }
1550
1551 /**
1552 * Save list of trusted nodes to database
1553 */
1554 bool NetObj::saveTrustedNodes(DB_HANDLE hdb)
1555 {
1556 TCHAR query[256];
1557 UINT32 i;
1558 bool rc = false;
1559
1560 _sntprintf(query, 256, _T("DELETE FROM trusted_nodes WHERE source_object_id=%d"), m_id);
1561 if (DBQuery(hdb, query))
1562 {
1563 for(i = 0; i < m_dwNumTrustedNodes; i++)
1564 {
1565 _sntprintf(query, 256, _T("INSERT INTO trusted_nodes (source_object_id,target_node_id) VALUES (%d,%d)"),
1566 m_id, m_pdwTrustedNodes[i]);
1567 if (!DBQuery(hdb, query))
1568 break;
1569 }
1570 if (i == m_dwNumTrustedNodes)
1571 rc = true;
1572 }
1573 return rc;
1574 }
1575
1576 /**
1577 * Check if given node is in trust list
1578 * Will always return TRUE if system parameter CheckTrustedNodes set to 0
1579 */
1580 bool NetObj::isTrustedNode(UINT32 id)
1581 {
1582 bool rc;
1583
1584 if (g_flags & AF_CHECK_TRUSTED_NODES)
1585 {
1586 UINT32 i;
1587
1588 lockProperties();
1589 for(i = 0, rc = false; i < m_dwNumTrustedNodes; i++)
1590 {
1591 if (m_pdwTrustedNodes[i] == id)
1592 {
1593 rc = true;
1594 break;
1595 }
1596 }
1597 unlockProperties();
1598 }
1599 else
1600 {
1601 rc = true;
1602 }
1603 return rc;
1604 }
1605
1606 /**
1607 * Get list of parent objects for NXSL script
1608 */
1609 NXSL_Array *NetObj::getParentsForNXSL()
1610 {
1611 NXSL_Array *parents = new NXSL_Array;
1612 int index = 0;
1613
1614 LockParentList(FALSE);
1615 for(UINT32 i = 0; i < m_dwParentCount; i++)
1616 {
1617 if ((m_pParentList[i]->getObjectClass() == OBJECT_CONTAINER) ||
1618 (m_pParentList[i]->getObjectClass() == OBJECT_SERVICEROOT) ||
1619 (m_pParentList[i]->getObjectClass() == OBJECT_NETWORK))
1620 {
1621 parents->set(index++, new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, m_pParentList[i])));
1622 }
1623 }
1624 UnlockParentList();
1625
1626 return parents;
1627 }
1628
1629 /**
1630 * Get list of child objects for NXSL script
1631 */
1632 NXSL_Array *NetObj::getChildrenForNXSL()
1633 {
1634 NXSL_Array *children = new NXSL_Array;
1635 int index = 0;
1636
1637 LockChildList(FALSE);
1638 for(UINT32 i = 0; i < m_dwChildCount; i++)
1639 {
1640 if (m_pChildList[i]->getObjectClass() == OBJECT_NODE)
1641 {
1642 children->set(index++, new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, m_pChildList[i])));
1643 }
1644 else if (m_pChildList[i]->getObjectClass() == OBJECT_INTERFACE)
1645 {
1646 children->set(index++, new NXSL_Value(new NXSL_Object(&g_nxslInterfaceClass, m_pChildList[i])));
1647 }
1648 else
1649 {
1650 children->set(index++, new NXSL_Value(new NXSL_Object(&g_nxslNetObjClass, m_pChildList[i])));
1651 }
1652 }
1653 UnlockChildList();
1654
1655 return children;
1656 }
1657
1658 /**
1659 * Get full list of child objects (including both direct and indirect childs)
1660 */
1661 void NetObj::getFullChildListInternal(ObjectIndex *list, bool eventSourceOnly)
1662 {
1663 LockChildList(FALSE);
1664 for(UINT32 i = 0; i < m_dwChildCount; i++)
1665 {
1666 if (!eventSourceOnly || IsEventSource(m_pChildList[i]->getObjectClass()))
1667 list->put(m_pChildList[i]->getId(), m_pChildList[i]);
1668 m_pChildList[i]->getFullChildListInternal(list, eventSourceOnly);
1669 }
1670 UnlockChildList();
1671 }
1672
1673 /**
1674 * Get full list of child objects (including both direct and indirect childs).
1675 * Returned array is dynamically allocated and must be deleted by the caller.
1676 *
1677 * @param eventSourceOnly if true, only objects that can be event source will be included
1678 */
1679 ObjectArray<NetObj> *NetObj::getFullChildList(bool eventSourceOnly, bool updateRefCount)
1680 {
1681 ObjectIndex list;
1682 getFullChildListInternal(&list, eventSourceOnly);
1683 return list.getObjects(updateRefCount);
1684 }
1685
1686 /**
1687 * Get list of child objects (direct only). Returned array is
1688 * dynamically allocated and must be deleted by the caller.
1689 *
1690 * @param typeFilter Only return objects with class ID equals given value.
1691 * Set to -1 to disable filtering.
1692 */
1693 ObjectArray<NetObj> *NetObj::getChildList(int typeFilter)
1694 {
1695 LockChildList(FALSE);
1696 ObjectArray<NetObj> *list = new ObjectArray<NetObj>((int)m_dwChildCount, 16, false);
1697 for(UINT32 i = 0; i < m_dwChildCount; i++)
1698 {
1699 if ((typeFilter == -1) || (typeFilter == m_pChildList[i]->getObjectClass()))
1700 list->add(m_pChildList[i]);
1701 }
1702 UnlockChildList();
1703 return list;
1704 }
1705
1706 /**
1707 * Get list of parent objects (direct only). Returned array is
1708 * dynamically allocated and must be deleted by the caller.
1709 *
1710 * @param typeFilter Only return objects with class ID equals given value.
1711 * Set to -1 to disable filtering.
1712 */
1713 ObjectArray<NetObj> *NetObj::getParentList(int typeFilter)
1714 {
1715 LockParentList(FALSE);
1716 ObjectArray<NetObj> *list = new ObjectArray<NetObj>((int)m_dwParentCount, 16, false);
1717 for(UINT32 i = 0; i < m_dwParentCount; i++)
1718 {
1719 if ((typeFilter == -1) || (typeFilter == m_pParentList[i]->getObjectClass()))
1720 list->add(m_pParentList[i]);
1721 }
1722 UnlockParentList();
1723 return list;
1724 }
1725
1726 /**
1727 * FInd child object by name (with optional class filter)
1728 */
1729 NetObj *NetObj::findChildObject(const TCHAR *name, int typeFilter)
1730 {
1731 NetObj *object = NULL;
1732 LockChildList(FALSE);
1733 for(UINT32 i = 0; i < m_dwChildCount; i++)
1734 {
1735 if (((typeFilter == -1) || (typeFilter == m_pChildList[i]->getObjectClass())) && !_tcsicmp(name, m_pChildList[i]->getName()))
1736 {
1737 object = m_pChildList[i];
1738 break;
1739 }
1740 }
1741 UnlockChildList();
1742 return object;
1743 }
1744
1745 /**
1746 * Called by client session handler to check if threshold summary should
1747 * be shown for this object. Default implementation always returns false.
1748 */
1749 bool NetObj::showThresholdSummary()
1750 {
1751 return false;
1752 }
1753
1754 /**
1755 * Must return true if object is a possible event source
1756 */
1757 bool NetObj::isEventSource()
1758 {
1759 return false;
1760 }
1761
1762 /**
1763 * Get module data
1764 */
1765 ModuleData *NetObj::getModuleData(const TCHAR *module)
1766 {
1767 lockProperties();
1768 ModuleData *data = (m_moduleData != NULL) ? m_moduleData->get(module) : NULL;
1769 unlockProperties();
1770 return data;
1771 }
1772
1773 /**
1774 * Set module data
1775 */
1776 void NetObj::setModuleData(const TCHAR *module, ModuleData *data)
1777 {
1778 lockProperties();
1779 if (m_moduleData == NULL)
1780 m_moduleData = new StringObjectMap<ModuleData>(true);
1781 m_moduleData->set(module, data);
1782 unlockProperties();
1783 }
1784
1785 /**
1786 * Add new location entry
1787 */
1788 void NetObj::addLocationToHistory()
1789 {
1790 DB_HANDLE hdb = DBConnectionPoolAcquireConnection();
1791 UINT32 startTimestamp;
1792 bool isSamePlace;
1793 DB_RESULT hResult;
1794 if (!isLocationTableExists())
1795 {
1796 DbgPrintf(4, _T("NetObj::addLocationToHistory: Geolocation history table will be created for object %s [%d]"), m_name, m_id);
1797 if (!createLocationHistoryTable(hdb))
1798 {
1799 DbgPrintf(4, _T("NetObj::addLocationToHistory: Error creating geolocation history table for object %s [%d]"), m_name, m_id);
1800 return;
1801 }
1802 }
1803 const TCHAR *query;
1804 switch(g_dbSyntax)
1805 {
1806 case DB_SYNTAX_ORACLE:
1807 query = _T("SELECT * FROM (latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC) WHERE ROWNUM<=1");
1808 break;
1809 case DB_SYNTAX_MSSQL:
1810 query = _T("SELECT TOP 1 latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC");
1811 break;
1812 case DB_SYNTAX_DB2:
1813 query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC FETCH FIRST 200 ROWS ONLY");
1814 break;
1815 default:
1816 query = _T("SELECT latitude,longitude,accuracy,start_timestamp FROM gps_history_%d ORDER BY start_timestamp DESC LIMIT 1");
1817 break;
1818 }
1819 TCHAR preparedQuery[256];
1820 _sntprintf(preparedQuery, 256, query, m_id);
1821 DB_STATEMENT hStmt = DBPrepare(hdb, preparedQuery);
1822
1823 if (hStmt == NULL)
1824 goto onFail;
1825
1826 hResult = DBSelectPrepared(hStmt);
1827 if (hResult == NULL)
1828 goto onFail;
1829 if (DBGetNumRows(hResult) > 0)
1830 {
1831 startTimestamp = DBGetFieldULong(hResult, 0, 3);
1832 isSamePlace = m_geoLocation.sameLocation(DBGetFieldDouble(hResult, 0, 0), DBGetFieldDouble(hResult, 0, 1), DBGetFieldLong(hResult, 0, 2));
1833 DBFreeStatement(hStmt);
1834 DBFreeResult(hResult);
1835 }
1836 else
1837 {
1838 isSamePlace = false;
1839 }
1840
1841 if (isSamePlace)
1842 {
1843 TCHAR query[256];
1844 _sntprintf(query, 255, _T("UPDATE gps_history_%d SET end_timestamp = ? WHERE start_timestamp =? "), m_id);
1845 hStmt = DBPrepare(hdb, query);
1846 DBBind(hStmt, 1, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
1847 DBBind(hStmt, 2, DB_SQLTYPE_INTEGER, startTimestamp);
1848 }
1849 else
1850 {
1851 TCHAR query[256];
1852 _sntprintf(query, 255, _T("INSERT INTO gps_history_%d (latitude,longitude,")
1853 _T("accuracy,start_timestamp,end_timestamp) VALUES (?,?,?,?,?)"), m_id);
1854 hStmt = DBPrepare(hdb, query);
1855
1856 TCHAR lat[32], lon[32];
1857 _sntprintf(lat, 32, _T("%f"), m_geoLocation.getLatitude());
1858 _sntprintf(lon, 32, _T("%f"), m_geoLocation.getLongitude());
1859
1860 DBBind(hStmt, 1, DB_SQLTYPE_VARCHAR, lat, DB_BIND_STATIC);
1861 DBBind(hStmt, 2, DB_SQLTYPE_VARCHAR, lon, DB_BIND_STATIC);
1862 DBBind(hStmt, 3, DB_SQLTYPE_INTEGER, (LONG)m_geoLocation.getAccuracy());
1863 DBBind(hStmt, 4, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
1864 DBBind(hStmt, 5, DB_SQLTYPE_INTEGER, (UINT32)m_geoLocation.getTimestamp());
1865 }
1866
1867 if (hStmt == NULL)
1868 goto onFail;
1869
1870 DBExecute(hStmt);
1871 DBFreeStatement(hStmt);
1872 DBConnectionPoolReleaseConnection(hdb);
1873 return;
1874
1875 onFail:
1876 DBFreeStatement(hStmt);
1877 DbgPrintf(4, _T("NetObj::addLocationToHistory(%s [%d]): Failed to add location to history"), m_name, m_id);
1878 DBConnectionPoolReleaseConnection(hdb);
1879 return;
1880 }
1881
1882 /**
1883 * Check if given data table exist
1884 */
1885 bool NetObj::isLocationTableExists()
1886 {
1887 TCHAR table[256];
1888 _sntprintf(table, 256, _T("gps_history_%d"), m_id);
1889 int rc = DBIsTableExist(g_hCoreDB, table);
1890 if (rc == DBIsTableExist_Failure)
1891 {
1892 _tprintf(_T("WARNING: call to DBIsTableExist(\"%s\") failed\n"), table);
1893 }
1894 return rc != DBIsTableExist_NotFound;
1895 }
1896
1897 /**
1898 * Create table for storing geolocation history for this object
1899 */
1900 bool NetObj::createLocationHistoryTable(DB_HANDLE hdb)
1901 {
1902 TCHAR szQuery[256], szQueryTemplate[256];
1903 MetaDataReadStr(_T("LocationHistory"), szQueryTemplate, 255, _T(""));
1904 _sntprintf(szQuery, 256, szQueryTemplate, m_id);
1905 if (!DBQuery(hdb, szQuery))
1906 return false;
1907
1908 return true;
1909 }
1910
1911 /**
1912 * Set status calculation method
1913 */
1914 void NetObj::setStatusCalculation(int method, int arg1, int arg2, int arg3, int arg4)
1915 {
1916 lockProperties();
1917 m_iStatusCalcAlg = method;
1918 switch(method)
1919 {
1920 case SA_CALCULATE_SINGLE_THRESHOLD:
1921 m_iStatusSingleThreshold = arg1;
1922 break;
1923 case SA_CALCULATE_MULTIPLE_THRESHOLDS:
1924 m_iStatusThresholds[0] = arg1;
1925 m_iStatusThresholds[1] = arg2;
1926 m_iStatusThresholds[2] = arg3;
1927 m_iStatusThresholds[3] = arg4;
1928 break;
1929 default:
1930 break;
1931 }
1932 setModified();
1933 unlockProperties();
1934 }
1935
1936 /**
1937 * Set status propagation method
1938 */
1939 void NetObj::setStatusPropagation(int method, int arg1, int arg2, int arg3, int arg4)
1940 {
1941 lockProperties();
1942 m_iStatusPropAlg = method;
1943 switch(method)
1944 {
1945 case SA_PROPAGATE_FIXED:
1946 m_iFixedStatus = arg1;
1947 break;
1948 case SA_PROPAGATE_RELATIVE:
1949 m_iStatusShift = arg1;
1950 break;
1951 case SA_PROPAGATE_TRANSLATED:
1952 m_iStatusTranslation[0] = arg1;
1953 m_iStatusTranslation[1] = arg2;
1954 m_iStatusTranslation[2] = arg3;
1955 m_iStatusTranslation[3] = arg4;
1956 break;
1957 default:
1958 break;
1959 }
1960 setModified();
1961 unlockProperties();
1962 }