ua_server.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2014-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2014-2017 (c) Florian Palm
  7. * Copyright 2015-2016 (c) Sten Grüner
  8. * Copyright 2015-2016 (c) Chris Iatrou
  9. * Copyright 2015 (c) LEvertz
  10. * Copyright 2015-2016 (c) Oleksiy Vasylyev
  11. * Copyright 2016 (c) Julian Grothoff
  12. * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
  13. * Copyright 2016 (c) Lorenz Haas
  14. * Copyright 2017 (c) frax2222
  15. * Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
  16. * Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang)
  17. */
  18. #include "ua_server_internal.h"
  19. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
  20. #include "ua_pubsub_ns0.h"
  21. #endif
  22. #ifdef UA_ENABLE_SUBSCRIPTIONS
  23. #include "ua_subscription.h"
  24. #endif
  25. #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
  26. #include <valgrind/memcheck.h>
  27. #endif
  28. /**********************/
  29. /* Namespace Handling */
  30. /**********************/
  31. UA_UInt16 addNamespace(UA_Server *server, const UA_String name) {
  32. /* Check if the namespace already exists in the server's namespace array */
  33. for(UA_UInt16 i = 0; i < server->namespacesSize; ++i) {
  34. if(UA_String_equal(&name, &server->namespaces[i]))
  35. return i;
  36. }
  37. /* Make the array bigger */
  38. UA_String *newNS = (UA_String*)UA_realloc(server->namespaces,
  39. sizeof(UA_String) * (server->namespacesSize + 1));
  40. if(!newNS)
  41. return 0;
  42. server->namespaces = newNS;
  43. /* Copy the namespace string */
  44. UA_StatusCode retval = UA_String_copy(&name, &server->namespaces[server->namespacesSize]);
  45. if(retval != UA_STATUSCODE_GOOD)
  46. return 0;
  47. /* Announce the change (otherwise, the array appears unchanged) */
  48. ++server->namespacesSize;
  49. return (UA_UInt16)(server->namespacesSize - 1);
  50. }
  51. UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
  52. /* Override const attribute to get string (dirty hack) */
  53. UA_String nameString;
  54. nameString.length = strlen(name);
  55. nameString.data = (UA_Byte*)(uintptr_t)name;
  56. return addNamespace(server, nameString);
  57. }
  58. UA_ServerConfig*
  59. UA_Server_getConfig(UA_Server *server)
  60. {
  61. if(!server)
  62. return NULL;
  63. return &server->config;
  64. }
  65. UA_StatusCode
  66. UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
  67. size_t* foundIndex) {
  68. for(size_t idx = 0; idx < server->namespacesSize; idx++)
  69. {
  70. if(UA_String_equal(&server->namespaces[idx], &namespaceUri) == true)
  71. {
  72. (*foundIndex) = idx;
  73. return UA_STATUSCODE_GOOD;
  74. }
  75. }
  76. return UA_STATUSCODE_BADNOTFOUND;
  77. }
  78. UA_StatusCode
  79. UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
  80. UA_NodeIteratorCallback callback, void *handle) {
  81. const UA_Node *parent =
  82. server->config.nodestore.getNode(server->config.nodestore.context,
  83. &parentNodeId);
  84. if(!parent)
  85. return UA_STATUSCODE_BADNODEIDINVALID;
  86. /* TODO: We need to do an ugly copy of the references array since users may
  87. * delete references from within the callback. In single-threaded mode this
  88. * changes the same node we point at here. In multi-threaded mode, this
  89. * creates a new copy as nodes are truly immutable.
  90. * The callback could remove a node via the regular public API.
  91. * This can remove a member of the nodes-array we iterate over...
  92. * */
  93. UA_Node *parentCopy = UA_Node_copy_alloc(parent);
  94. if(!parentCopy) {
  95. server->config.nodestore.releaseNode(server->config.nodestore.context, parent);
  96. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  97. }
  98. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  99. for(size_t i = parentCopy->referencesSize; i > 0; --i) {
  100. UA_NodeReferenceKind *ref = &parentCopy->references[i - 1];
  101. for(size_t j = 0; j<ref->targetIdsSize; j++) {
  102. retval = callback(ref->targetIds[j].nodeId, ref->isInverse,
  103. ref->referenceTypeId, handle);
  104. if(retval != UA_STATUSCODE_GOOD)
  105. goto cleanup;
  106. }
  107. }
  108. cleanup:
  109. UA_Node_deleteMembers(parentCopy);
  110. UA_free(parentCopy);
  111. server->config.nodestore.releaseNode(server->config.nodestore.context, parent);
  112. return retval;
  113. }
  114. /********************/
  115. /* Server Lifecycle */
  116. /********************/
  117. /* The server needs to be stopped before it can be deleted */
  118. void UA_Server_delete(UA_Server *server) {
  119. /* Delete all internal data */
  120. UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
  121. UA_SessionManager_deleteMembers(&server->sessionManager);
  122. UA_Array_delete(server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]);
  123. #ifdef UA_ENABLE_SUBSCRIPTIONS
  124. UA_MonitoredItem *mon, *mon_tmp;
  125. LIST_FOREACH_SAFE(mon, &server->localMonitoredItems, listEntry, mon_tmp) {
  126. LIST_REMOVE(mon, listEntry);
  127. UA_MonitoredItem_delete(server, mon);
  128. }
  129. #endif
  130. #ifdef UA_ENABLE_PUBSUB
  131. UA_PubSubManager_delete(server, &server->pubSubManager);
  132. #endif
  133. #ifdef UA_ENABLE_DISCOVERY
  134. UA_DiscoveryManager_deleteMembers(&server->discoveryManager, server);
  135. #endif
  136. /* Clean up the Admin Session */
  137. UA_Session_deleteMembersCleanup(&server->adminSession, server);
  138. /* Clean up the work queue */
  139. UA_WorkQueue_cleanup(&server->workQueue);
  140. /* Delete the timed work */
  141. UA_Timer_deleteMembers(&server->timer);
  142. /* Delete the server itself */
  143. UA_free(server);
  144. }
  145. /* Recurring cleanup. Removing unused and timed-out channels and sessions */
  146. static void
  147. UA_Server_cleanup(UA_Server *server, void *_) {
  148. UA_DateTime nowMonotonic = UA_DateTime_nowMonotonic();
  149. UA_SessionManager_cleanupTimedOut(&server->sessionManager, nowMonotonic);
  150. UA_SecureChannelManager_cleanupTimedOut(&server->secureChannelManager, nowMonotonic);
  151. #ifdef UA_ENABLE_DISCOVERY
  152. UA_Discovery_cleanupTimedOut(server, nowMonotonic);
  153. #endif
  154. }
  155. /********************/
  156. /* Server Lifecycle */
  157. /********************/
  158. UA_Server *
  159. UA_Server_new(const UA_ServerConfig *config) {
  160. /* A config is required */
  161. if(!config)
  162. return NULL;
  163. /* Allocate the server */
  164. UA_Server *server = (UA_Server *)UA_calloc(1, sizeof(UA_Server));
  165. if(!server)
  166. return NULL;
  167. /* Set the config */
  168. server->config = *config;
  169. /* Init start time to zero, the actual start time will be sampled in
  170. * UA_Server_run_startup() */
  171. server->startTime = 0;
  172. /* Set a seed for non-cyptographic randomness */
  173. #ifndef UA_ENABLE_DETERMINISTIC_RNG
  174. UA_random_seed((UA_UInt64)UA_DateTime_now());
  175. #endif
  176. /* Initialize the handling of repeated callbacks */
  177. UA_Timer_init(&server->timer);
  178. UA_WorkQueue_init(&server->workQueue);
  179. /* Initialize the adminSession */
  180. UA_Session_init(&server->adminSession);
  181. server->adminSession.sessionId.identifierType = UA_NODEIDTYPE_GUID;
  182. server->adminSession.sessionId.identifier.guid.data1 = 1;
  183. server->adminSession.validTill = UA_INT64_MAX;
  184. /* Create Namespaces 0 and 1 */
  185. server->namespaces = (UA_String *)UA_Array_new(2, &UA_TYPES[UA_TYPES_STRING]);
  186. server->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
  187. UA_String_copy(&server->config.applicationDescription.applicationUri, &server->namespaces[1]);
  188. server->namespacesSize = 2;
  189. /* Initialized SecureChannel and Session managers */
  190. UA_SecureChannelManager_init(&server->secureChannelManager, server);
  191. UA_SessionManager_init(&server->sessionManager, server);
  192. /* Add a regular callback for cleanup and maintenance. With a 10s interval. */
  193. UA_Server_addRepeatedCallback(server, (UA_ServerCallback)UA_Server_cleanup, NULL,
  194. 10000.0, NULL);
  195. /* Initialized discovery */
  196. #ifdef UA_ENABLE_DISCOVERY
  197. UA_DiscoveryManager_init(&server->discoveryManager, server);
  198. #endif
  199. /* Initialize namespace 0*/
  200. UA_StatusCode retVal = UA_Server_initNS0(server);
  201. if(retVal != UA_STATUSCODE_GOOD) {
  202. UA_LOG_ERROR(&config->logger, UA_LOGCATEGORY_SERVER,
  203. "Namespace 0 could not be bootstrapped with error %s. "
  204. "Shutting down the server.",
  205. UA_StatusCode_name(retVal));
  206. UA_Server_delete(server);
  207. return NULL;
  208. }
  209. /* Build PubSub information model */
  210. #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
  211. UA_Server_initPubSubNS0(server);
  212. #endif
  213. return server;
  214. }
  215. /*******************/
  216. /* Timed Callbacks */
  217. /*******************/
  218. UA_StatusCode
  219. UA_Server_addTimedCallback(UA_Server *server, UA_ServerCallback callback,
  220. void *data, UA_DateTime date, UA_UInt64 *callbackId) {
  221. return UA_Timer_addTimedCallback(&server->timer,
  222. (UA_ApplicationCallback)callback,
  223. server, data, date, callbackId);
  224. }
  225. UA_StatusCode
  226. UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback,
  227. void *data, UA_Double interval_ms,
  228. UA_UInt64 *callbackId) {
  229. return UA_Timer_addRepeatedCallback(&server->timer,
  230. (UA_ApplicationCallback)callback,
  231. server, data, interval_ms, callbackId);
  232. }
  233. UA_StatusCode
  234. UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId,
  235. UA_Double interval_ms) {
  236. return UA_Timer_changeRepeatedCallbackInterval(&server->timer, callbackId,
  237. interval_ms);
  238. }
  239. void
  240. UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId) {
  241. UA_Timer_removeCallback(&server->timer, callbackId);
  242. }
  243. UA_StatusCode UA_EXPORT
  244. UA_Server_updateCertificate(UA_Server *server,
  245. const UA_ByteString *oldCertificate,
  246. const UA_ByteString *newCertificate,
  247. const UA_ByteString *newPrivateKey,
  248. UA_Boolean closeSessions,
  249. UA_Boolean closeSecureChannels) {
  250. if (server == NULL || oldCertificate == NULL
  251. || newCertificate == NULL || newPrivateKey == NULL) {
  252. return UA_STATUSCODE_BADINTERNALERROR;
  253. }
  254. if (closeSessions) {
  255. UA_SessionManager *sm = &server->sessionManager;
  256. session_list_entry *current;
  257. LIST_FOREACH(current, &sm->sessions, pointers) {
  258. if (UA_ByteString_equal(oldCertificate,
  259. &current->session.header.channel->securityPolicy->localCertificate)) {
  260. UA_SessionManager_removeSession(sm, &current->session.header.authenticationToken);
  261. }
  262. }
  263. }
  264. if (closeSecureChannels) {
  265. UA_SecureChannelManager *cm = &server->secureChannelManager;
  266. channel_entry *entry;
  267. TAILQ_FOREACH(entry, &cm->channels, pointers) {
  268. if(UA_ByteString_equal(&entry->channel.securityPolicy->localCertificate, oldCertificate)){
  269. UA_SecureChannelManager_close(cm, entry->channel.securityToken.channelId);
  270. }
  271. }
  272. }
  273. size_t i = 0;
  274. while (i < server->config.endpointsSize) {
  275. UA_EndpointDescription *ed = &server->config.endpoints[i];
  276. if (UA_ByteString_equal(&ed->serverCertificate, oldCertificate)) {
  277. UA_String_deleteMembers(&ed->serverCertificate);
  278. UA_String_copy(newCertificate, &ed->serverCertificate);
  279. UA_SecurityPolicy *sp = UA_SecurityPolicy_getSecurityPolicyByUri(server, &server->config.endpoints[i].securityPolicyUri);
  280. if(!sp)
  281. return UA_STATUSCODE_BADINTERNALERROR;
  282. sp->updateCertificateAndPrivateKey(sp, *newCertificate, *newPrivateKey);
  283. }
  284. i++;
  285. }
  286. return UA_STATUSCODE_GOOD;
  287. }
  288. /***************************/
  289. /* Server lookup functions */
  290. /***************************/
  291. UA_SecurityPolicy *
  292. UA_SecurityPolicy_getSecurityPolicyByUri(const UA_Server *server,
  293. UA_ByteString *securityPolicyUri)
  294. {
  295. for(size_t i = 0; i < server->config.securityPoliciesSize; i++) {
  296. UA_SecurityPolicy *securityPolicyCandidate = &server->config.securityPolicies[i];
  297. if(UA_ByteString_equal(securityPolicyUri,
  298. &securityPolicyCandidate->policyUri))
  299. return securityPolicyCandidate;
  300. }
  301. return NULL;
  302. }
  303. /********************/
  304. /* Main Server Loop */
  305. /********************/
  306. #define UA_MAXTIMEOUT 50 /* Max timeout in ms between main-loop iterations */
  307. /* Start: Spin up the workers and the network layer and sample the server's
  308. * start time.
  309. * Iterate: Process repeated callbacks and events in the network layer. This
  310. * part can be driven from an external main-loop in an event-driven
  311. * single-threaded architecture.
  312. * Stop: Stop workers, finish all callbacks, stop the network layer, clean up */
  313. UA_StatusCode
  314. UA_Server_run_startup(UA_Server *server) {
  315. UA_Variant var;
  316. UA_StatusCode result = UA_STATUSCODE_GOOD;
  317. /* At least one endpoint has to be configured */
  318. if(server->config.endpointsSize == 0) {
  319. UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
  320. "There has to be at least one endpoint.");
  321. }
  322. /* Sample the start time and set it to the Server object */
  323. server->startTime = UA_DateTime_now();
  324. UA_Variant_init(&var);
  325. UA_Variant_setScalar(&var, &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
  326. UA_Server_writeValue(server,
  327. UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
  328. var);
  329. /* Start the networklayers */
  330. for(size_t i = 0; i < server->config.networkLayersSize; ++i) {
  331. UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
  332. result |= nl->start(nl, &server->config.customHostname);
  333. }
  334. /* Spin up the worker threads */
  335. #ifdef UA_ENABLE_MULTITHREADING
  336. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  337. "Spinning up %u worker thread(s)", server->config.nThreads);
  338. UA_WorkQueue_start(&server->workQueue, server->config.nThreads);
  339. #endif
  340. /* Start the multicast discovery server */
  341. #ifdef UA_ENABLE_DISCOVERY_MULTICAST
  342. if(server->config.applicationDescription.applicationType ==
  343. UA_APPLICATIONTYPE_DISCOVERYSERVER)
  344. startMulticastDiscoveryServer(server);
  345. #endif
  346. return result;
  347. }
  348. static void
  349. serverExecuteRepeatedCallback(UA_Server *server, UA_ApplicationCallback cb,
  350. void *callbackApplication, void *data) {
  351. #ifndef UA_ENABLE_MULTITHREADING
  352. cb(callbackApplication, data);
  353. #else
  354. UA_WorkQueue_enqueue(&server->workQueue, cb, callbackApplication, data);
  355. #endif
  356. }
  357. UA_UInt16
  358. UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
  359. /* Process repeated work */
  360. UA_DateTime now = UA_DateTime_nowMonotonic();
  361. UA_DateTime nextRepeated = UA_Timer_process(&server->timer, now,
  362. (UA_TimerExecutionCallback)serverExecuteRepeatedCallback, server);
  363. UA_DateTime latest = now + (UA_MAXTIMEOUT * UA_DATETIME_MSEC);
  364. if(nextRepeated > latest)
  365. nextRepeated = latest;
  366. UA_UInt16 timeout = 0;
  367. /* round always to upper value to avoid timeout to be set to 0
  368. * if(nextRepeated - now) < (UA_DATETIME_MSEC/2) */
  369. if(waitInternal)
  370. timeout = (UA_UInt16)(((nextRepeated - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
  371. /* Listen on the networklayer */
  372. for(size_t i = 0; i < server->config.networkLayersSize; ++i) {
  373. UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
  374. nl->listen(nl, server, timeout);
  375. }
  376. #if defined(UA_ENABLE_DISCOVERY_MULTICAST) && !defined(UA_ENABLE_MULTITHREADING)
  377. if(server->config.applicationDescription.applicationType ==
  378. UA_APPLICATIONTYPE_DISCOVERYSERVER) {
  379. // TODO multicastNextRepeat does not consider new input data (requests)
  380. // on the socket. It will be handled on the next call. if needed, we
  381. // need to use select with timeout on the multicast socket
  382. // server->mdnsSocket (see example in mdnsd library) on higher level.
  383. UA_DateTime multicastNextRepeat = 0;
  384. UA_StatusCode hasNext =
  385. iterateMulticastDiscoveryServer(server, &multicastNextRepeat, true);
  386. if(hasNext == UA_STATUSCODE_GOOD && multicastNextRepeat < nextRepeated)
  387. nextRepeated = multicastNextRepeat;
  388. }
  389. #endif
  390. #ifndef UA_ENABLE_MULTITHREADING
  391. UA_WorkQueue_manuallyProcessDelayed(&server->workQueue);
  392. #endif
  393. now = UA_DateTime_nowMonotonic();
  394. timeout = 0;
  395. if(nextRepeated > now)
  396. timeout = (UA_UInt16)((nextRepeated - now) / UA_DATETIME_MSEC);
  397. return timeout;
  398. }
  399. UA_StatusCode
  400. UA_Server_run_shutdown(UA_Server *server) {
  401. /* Stop the netowrk layer */
  402. for(size_t i = 0; i < server->config.networkLayersSize; ++i) {
  403. UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
  404. nl->stop(nl, server);
  405. }
  406. #ifdef UA_ENABLE_MULTITHREADING
  407. /* Shut down the workers */
  408. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  409. "Shutting down %u worker thread(s)",
  410. (UA_UInt32)server->workQueue.workersSize);
  411. UA_WorkQueue_stop(&server->workQueue);
  412. #endif
  413. #ifdef UA_ENABLE_DISCOVERY_MULTICAST
  414. /* Stop multicast discovery */
  415. if(server->config.applicationDescription.applicationType ==
  416. UA_APPLICATIONTYPE_DISCOVERYSERVER)
  417. stopMulticastDiscoveryServer(server);
  418. #endif
  419. /* Execute all delayed callbacks */
  420. UA_WorkQueue_cleanup(&server->workQueue);
  421. return UA_STATUSCODE_GOOD;
  422. }
  423. UA_StatusCode
  424. UA_Server_run(UA_Server *server, const volatile UA_Boolean *running) {
  425. UA_StatusCode retval = UA_Server_run_startup(server);
  426. if(retval != UA_STATUSCODE_GOOD)
  427. return retval;
  428. #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
  429. size_t loopCount = 0;
  430. #endif
  431. while(*running) {
  432. #ifdef UA_ENABLE_VALGRIND_INTERACTIVE
  433. if(loopCount == 0) {
  434. VALGRIND_DO_LEAK_CHECK;
  435. }
  436. ++loopCount;
  437. loopCount %= UA_VALGRIND_INTERACTIVE_INTERVAL;
  438. #endif
  439. UA_Server_run_iterate(server, true);
  440. }
  441. return UA_Server_run_shutdown(server);
  442. }
  443. #ifdef UA_ENABLE_HISTORIZING
  444. /* Allow insert of historical data */
  445. UA_Boolean
  446. UA_Server_AccessControl_allowHistoryUpdateUpdateData(UA_Server *server,
  447. const UA_NodeId *sessionId, void *sessionContext,
  448. const UA_NodeId *nodeId,
  449. UA_PerformUpdateType performInsertReplace,
  450. const UA_DataValue *value) {
  451. if(server->config.accessControl.allowHistoryUpdateUpdateData &&
  452. !server->config.accessControl.allowHistoryUpdateUpdateData(server, &server->config.accessControl,
  453. sessionId, sessionContext, nodeId,
  454. performInsertReplace, value)) {
  455. return false;
  456. }
  457. return true;
  458. }
  459. /* Allow delete of historical data */
  460. UA_Boolean
  461. UA_Server_AccessControl_allowHistoryUpdateDeleteRawModified(UA_Server *server,
  462. const UA_NodeId *sessionId, void *sessionContext,
  463. const UA_NodeId *nodeId,
  464. UA_DateTime startTimestamp,
  465. UA_DateTime endTimestamp,
  466. bool isDeleteModified) {
  467. if(server->config.accessControl.allowHistoryUpdateDeleteRawModified &&
  468. !server->config.accessControl.allowHistoryUpdateDeleteRawModified(server, &server->config.accessControl,
  469. sessionId, sessionContext, nodeId,
  470. startTimestamp, endTimestamp,
  471. isDeleteModified)) {
  472. return false;
  473. }
  474. return true;
  475. }
  476. #endif // UA_ENABLE_HISTORIZING