ua_services_nodemanagement.c 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  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. #include "ua_server_internal.h"
  5. #include "ua_services.h"
  6. /*********************/
  7. /* Edit Node Context */
  8. /*********************/
  9. UA_StatusCode
  10. UA_Server_getNodeContext(UA_Server *server, UA_NodeId nodeId,
  11. void **nodeContext) {
  12. const UA_Node *node = UA_Nodestore_get(server, &nodeId);
  13. if(!node)
  14. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  15. *nodeContext = node->context;
  16. UA_Nodestore_release(server, node);
  17. return UA_STATUSCODE_GOOD;
  18. }
  19. static UA_StatusCode
  20. editNodeContext(UA_Server *server, UA_Session* session,
  21. UA_Node* node, void *context) {
  22. node->context = context;
  23. return UA_STATUSCODE_GOOD;
  24. }
  25. UA_StatusCode
  26. UA_Server_setNodeContext(UA_Server *server, UA_NodeId nodeId,
  27. void *nodeContext) {
  28. UA_StatusCode retval =
  29. UA_Server_editNode(server, &adminSession, &nodeId,
  30. (UA_EditNodeCallback)editNodeContext, nodeContext);
  31. return retval;
  32. }
  33. /**********************/
  34. /* Consistency Checks */
  35. /**********************/
  36. /* Check if the requested parent node exists, has the right node class and is
  37. * referenced with an allowed (hierarchical) reference type. For "type" nodes,
  38. * only hasSubType references are allowed. */
  39. static UA_StatusCode
  40. checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
  41. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
  42. /* Objects do not need a parent (e.g. mandatory/optional modellingrules) */
  43. if(nodeClass == UA_NODECLASS_OBJECT && UA_NodeId_isNull(parentNodeId) &&
  44. UA_NodeId_isNull(referenceTypeId))
  45. return UA_STATUSCODE_GOOD;
  46. /* Omit checks during bootstrap */
  47. if(server->bootstrapNS0)
  48. return UA_STATUSCODE_GOOD;
  49. /* See if the parent exists */
  50. const UA_Node *parent = UA_Nodestore_get(server, parentNodeId);
  51. if(!parent) {
  52. UA_LOG_INFO_SESSION(server->config.logger, session,
  53. "AddNodes: Parent node not found");
  54. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  55. }
  56. UA_NodeClass parentNodeClass = parent->nodeClass;
  57. UA_Nodestore_release(server, parent);
  58. /* Check the referencetype exists */
  59. const UA_ReferenceTypeNode *referenceType = (const UA_ReferenceTypeNode*)
  60. UA_Nodestore_get(server, referenceTypeId);
  61. if(!referenceType) {
  62. UA_LOG_INFO_SESSION(server->config.logger, session,
  63. "AddNodes: Reference type to the parent not found");
  64. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  65. }
  66. /* Check if the referencetype is a reference type node */
  67. if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
  68. UA_LOG_INFO_SESSION(server->config.logger, session,
  69. "AddNodes: Reference type to the parent invalid");
  70. UA_Nodestore_release(server, (const UA_Node*)referenceType);
  71. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  72. }
  73. UA_Boolean referenceTypeIsAbstract = referenceType->isAbstract;
  74. UA_Nodestore_release(server, (const UA_Node*)referenceType);
  75. /* Check that the reference type is not abstract */
  76. if(referenceTypeIsAbstract == true) {
  77. UA_LOG_INFO_SESSION(server->config.logger, session,
  78. "AddNodes: Abstract reference type to the parent not allowed");
  79. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  80. }
  81. /* Check hassubtype relation for type nodes */
  82. if(nodeClass == UA_NODECLASS_DATATYPE ||
  83. nodeClass == UA_NODECLASS_VARIABLETYPE ||
  84. nodeClass == UA_NODECLASS_OBJECTTYPE ||
  85. nodeClass == UA_NODECLASS_REFERENCETYPE) {
  86. /* type needs hassubtype reference to the supertype */
  87. if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
  88. UA_LOG_INFO_SESSION(server->config.logger, session,
  89. "AddNodes: New type node need to have a "
  90. "HasSubType reference");
  91. return UA_STATUSCODE_BADREFERENCENOTALLOWED;
  92. }
  93. /* supertype needs to be of the same node type */
  94. if(parentNodeClass != nodeClass) {
  95. UA_LOG_INFO_SESSION(server->config.logger, session,
  96. "AddNodes: New type node needs to be of the same "
  97. "node type as the parent");
  98. return UA_STATUSCODE_BADPARENTNODEIDINVALID;
  99. }
  100. return UA_STATUSCODE_GOOD;
  101. }
  102. /* Test if the referencetype is hierarchical */
  103. const UA_NodeId hierarchicalReference =
  104. UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
  105. if(!isNodeInTree(&server->config.nodestore, referenceTypeId,
  106. &hierarchicalReference, &subtypeId, 1)) {
  107. UA_LOG_INFO_SESSION(server->config.logger, session,
  108. "AddNodes: Reference type is not hierarchical");
  109. return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
  110. }
  111. return UA_STATUSCODE_GOOD;
  112. }
  113. static UA_StatusCode
  114. typeCheckVariableNode(UA_Server *server, UA_Session *session,
  115. const UA_VariableNode *node,
  116. const UA_VariableTypeNode *vt,
  117. const UA_NodeId *parentNodeId) {
  118. /* The value might come from a datasource, so we perform a
  119. * regular read. */
  120. UA_DataValue value;
  121. UA_DataValue_init(&value);
  122. UA_StatusCode retval = readValueAttribute(server, session, node, &value);
  123. if(retval != UA_STATUSCODE_GOOD)
  124. return retval;
  125. /* Check the datatype against the vt */
  126. if(!compatibleDataType(server, &node->dataType, &vt->dataType))
  127. return UA_STATUSCODE_BADTYPEMISMATCH;
  128. /* Get the array dimensions */
  129. size_t arrayDims = node->arrayDimensionsSize;
  130. if(arrayDims == 0 && value.hasValue && value.value.type &&
  131. !UA_Variant_isScalar(&value.value)) {
  132. arrayDims = 1; /* No array dimensions on an array implies one dimension */
  133. }
  134. /* Check valueRank against array dimensions */
  135. if(!(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
  136. ((const UA_VariableTypeNode*)node)->isAbstract && node->valueRank == 0) &&
  137. !compatibleValueRankArrayDimensions(node->valueRank, arrayDims))
  138. return UA_STATUSCODE_BADTYPEMISMATCH;
  139. /* If variable node is created below BaseObjectType and has its default valueRank of -2,
  140. * skip the test */
  141. const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
  142. const UA_NodeId refs[2] = {
  143. UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
  144. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
  145. };
  146. if(node->valueRank != vt->valueRank &&
  147. node->valueRank != UA_VariableAttributes_default.valueRank &&
  148. !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
  149. /* Check valueRank against the vt */
  150. if(!compatibleValueRanks(node->valueRank, vt->valueRank))
  151. return UA_STATUSCODE_BADTYPEMISMATCH;
  152. }
  153. /* Check array dimensions against the vt */
  154. if(!compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
  155. node->arrayDimensionsSize, node->arrayDimensions))
  156. return UA_STATUSCODE_BADTYPEMISMATCH;
  157. /* Typecheck the value */
  158. if(!server->bootstrapNS0 && value.hasValue) {
  159. /* If the type-check failed write the same value again. The
  160. * write-service tries to convert to the correct type... */
  161. if(!compatibleValue(server, &node->dataType, node->valueRank,
  162. node->arrayDimensionsSize, node->arrayDimensions,
  163. &value.value, NULL))
  164. retval = UA_Server_writeValue(server, node->nodeId, value.value);
  165. UA_DataValue_deleteMembers(&value);
  166. }
  167. return retval;
  168. }
  169. /********************/
  170. /* Instantiate Node */
  171. /********************/
  172. static const UA_NodeId baseDataVariableType =
  173. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEDATAVARIABLETYPE}};
  174. static const UA_NodeId baseObjectType =
  175. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_BASEOBJECTTYPE}};
  176. /* Use attributes from the variable type wherever required */
  177. static UA_StatusCode
  178. useVariableTypeAttributes(UA_Server *server, UA_Session *session,
  179. UA_VariableNode *node, const UA_AddNodesItem *item) {
  180. const UA_VariableAttributes *attributes = (const UA_VariableAttributes*)
  181. item->nodeAttributes.content.decoded.data;
  182. /* Select the type definition */
  183. const UA_NodeId *typeDefinition;
  184. if(node->nodeClass == UA_NODECLASS_VARIABLE)
  185. typeDefinition = &item->typeDefinition.nodeId;
  186. else /* UA_NODECLASS_VARIABLETYPE */
  187. typeDefinition = &item->parentNodeId.nodeId;
  188. /* Replace an empty typeDefinition with the most permissive default */
  189. if(UA_NodeId_isNull(typeDefinition))
  190. typeDefinition = &baseDataVariableType;
  191. const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)
  192. UA_Nodestore_get(server, typeDefinition);
  193. if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE) {
  194. UA_Nodestore_release(server, (const UA_Node*)vt);
  195. return UA_STATUSCODE_BADTYPEMISMATCH;
  196. }
  197. /* If no value is set, see if the vt provides one and copy it. This needs to
  198. * be done before copying the datatype from the vt, as setting the datatype
  199. * triggers a typecheck. */
  200. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  201. if(!attributes->value.type) {
  202. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  203. "AddNodes: No value given; Copy the value"
  204. "from the TypeDefinition");
  205. UA_DataValue vt_value;
  206. UA_DataValue_init(&vt_value);
  207. retval = readValueAttribute(server, session,
  208. (const UA_VariableNode*)vt, &vt_value);
  209. if(retval == UA_STATUSCODE_GOOD && vt_value.hasValue) {
  210. retval = UA_Variant_copy(&vt_value.value, &node->value.data.value.value);
  211. node->value.data.value.hasValue = true;
  212. }
  213. UA_DataValue_deleteMembers(&vt_value);
  214. }
  215. /* If no datatype is given, use the datatype of the vt */
  216. if(retval == UA_STATUSCODE_GOOD && UA_NodeId_isNull(&node->dataType)) {
  217. UA_LOG_INFO_SESSION(server->config.logger, session, "AddNodes: "
  218. "No datatype given; Copy the datatype attribute "
  219. "from the TypeDefinition");
  220. retval = UA_NodeId_copy(&vt->dataType, &node->dataType);
  221. }
  222. /* TODO: If the vt has arraydimensions but this variable does not, copy */
  223. UA_Nodestore_release(server, (const UA_Node*)vt);
  224. return retval;
  225. }
  226. /* Search for an instance of "browseName" in node searchInstance. Used during
  227. * copyChildNodes to find overwritable/mergable nodes. Does not touch
  228. * outInstanceNodeId if no child is found. */
  229. static UA_StatusCode
  230. findChildByBrowsename(UA_Server *server, UA_Session *session,
  231. const UA_NodeId *searchInstance,
  232. const UA_QualifiedName *browseName,
  233. UA_NodeId *outInstanceNodeId) {
  234. UA_BrowseDescription bd;
  235. UA_BrowseDescription_init(&bd);
  236. bd.nodeId = *searchInstance;
  237. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  238. bd.includeSubtypes = true;
  239. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  240. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  241. bd.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
  242. UA_BrowseResult br;
  243. UA_BrowseResult_init(&br);
  244. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  245. if(br.statusCode != UA_STATUSCODE_GOOD)
  246. return br.statusCode;
  247. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  248. for(size_t i = 0; i < br.referencesSize; ++i) {
  249. UA_ReferenceDescription *rd = &br.references[i];
  250. if(rd->browseName.namespaceIndex == browseName->namespaceIndex &&
  251. UA_String_equal(&rd->browseName.name, &browseName->name)) {
  252. retval = UA_NodeId_copy(&rd->nodeId.nodeId, outInstanceNodeId);
  253. break;
  254. }
  255. }
  256. UA_BrowseResult_deleteMembers(&br);
  257. return retval;
  258. }
  259. static const UA_NodeId mandatoryId =
  260. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_MODELLINGRULE_MANDATORY}};
  261. static const UA_NodeId hasModellingRuleId =
  262. {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASMODELLINGRULE}};
  263. static UA_Boolean
  264. isMandatoryChild(UA_Server *server, UA_Session *session,
  265. const UA_NodeId *childNodeId) {
  266. /* Get the child */
  267. const UA_Node *child = UA_Nodestore_get(server, childNodeId);
  268. if(!child)
  269. return false;
  270. /* Look for the reference making the child mandatory */
  271. for(size_t i = 0; i < child->referencesSize; ++i) {
  272. UA_NodeReferenceKind *refs = &child->references[i];
  273. if(!UA_NodeId_equal(&hasModellingRuleId, &refs->referenceTypeId))
  274. continue;
  275. if(refs->isInverse)
  276. continue;
  277. for(size_t j = 0; j < refs->targetIdsSize; ++j) {
  278. if(UA_NodeId_equal(&mandatoryId, &refs->targetIds[j].nodeId)) {
  279. UA_Nodestore_release(server, child);
  280. return true;
  281. }
  282. }
  283. }
  284. UA_Nodestore_release(server, child);
  285. return false;
  286. }
  287. static UA_StatusCode
  288. copyChildNodes(UA_Server *server, UA_Session *session,
  289. const UA_NodeId *sourceNodeId,
  290. const UA_NodeId *destinationNodeId);
  291. static void
  292. Operation_addReference(UA_Server *server, UA_Session *session, void *context,
  293. const UA_AddReferencesItem *item, UA_StatusCode *retval);
  294. /*
  295. * This method only deletes references from the node which are not matching any type in the given array.
  296. * Could be used to e.g. delete all the references, except 'HASMODELINGRULE'
  297. */
  298. static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_NodeId* referencesSkip) {
  299. if(referencesSkipSize == 0) {
  300. UA_Node_deleteReferences(node);
  301. return;
  302. }
  303. /* Let's count if there are references left. If not just delete all the references.
  304. * It's faster */
  305. size_t newSize = 0;
  306. for(size_t i = 0; i < node->referencesSize; ++i) {
  307. for(size_t j = 0; j < referencesSkipSize; j++) {
  308. if(UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j])) {
  309. newSize++;
  310. }
  311. }
  312. }
  313. if(newSize == 0) {
  314. UA_Node_deleteReferences(node);
  315. return;
  316. }
  317. /* Now copy the remaining references to a new array */
  318. UA_NodeReferenceKind *newReferences = (UA_NodeReferenceKind *)UA_malloc(sizeof(UA_NodeReferenceKind) * (newSize));
  319. size_t curr = 0;
  320. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  321. for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; ++i) {
  322. for(size_t j = 0; j < referencesSkipSize; j++) {
  323. if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j]))
  324. continue;
  325. // copy the reference
  326. UA_NodeReferenceKind *srefs = &node->references[i];
  327. UA_NodeReferenceKind *drefs = &newReferences[curr++];
  328. drefs->isInverse = srefs->isInverse;
  329. retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId);
  330. if(retval != UA_STATUSCODE_GOOD)
  331. break;
  332. retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize,
  333. (void**)&drefs->targetIds,
  334. &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
  335. if(retval != UA_STATUSCODE_GOOD)
  336. break;
  337. drefs->targetIdsSize = srefs->targetIdsSize;
  338. break;
  339. }
  340. if(retval != UA_STATUSCODE_GOOD) {
  341. for(size_t k=0; k<i; k++) {
  342. UA_NodeReferenceKind *refs = &newReferences[i];
  343. for(size_t j = 0; j < refs->targetIdsSize; ++j)
  344. UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
  345. UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
  346. UA_NodeId_deleteMembers(&refs->referenceTypeId);
  347. }
  348. }
  349. }
  350. UA_Node_deleteReferences(node);
  351. if(retval == UA_STATUSCODE_GOOD) {
  352. node->references = newReferences;
  353. node->referencesSize = newSize;
  354. } else {
  355. UA_free(newReferences);
  356. }
  357. }
  358. static UA_StatusCode
  359. copyChildNode(UA_Server *server, UA_Session *session,
  360. const UA_NodeId *destinationNodeId,
  361. const UA_ReferenceDescription *rd) {
  362. UA_NodeId existingChild = UA_NODEID_NULL;
  363. UA_StatusCode retval =
  364. findChildByBrowsename(server, session, destinationNodeId,
  365. &rd->browseName, &existingChild);
  366. if(retval != UA_STATUSCODE_GOOD)
  367. return retval;
  368. /* Have a child with that browseName. Try to deep-copy missing members. */
  369. if(!UA_NodeId_isNull(&existingChild)) {
  370. if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
  371. rd->nodeClass == UA_NODECLASS_OBJECT)
  372. retval = copyChildNodes(server, session, &rd->nodeId.nodeId, &existingChild);
  373. UA_NodeId_deleteMembers(&existingChild);
  374. return retval;
  375. }
  376. /* Is the child mandatory? If not, skip */
  377. if(!isMandatoryChild(server, session, &rd->nodeId.nodeId))
  378. return UA_STATUSCODE_GOOD;
  379. /* No existing child with that browsename. Create it. */
  380. if(rd->nodeClass == UA_NODECLASS_METHOD) {
  381. /* Add a reference to the method in the objecttype */
  382. UA_AddReferencesItem newItem;
  383. UA_AddReferencesItem_init(&newItem);
  384. newItem.sourceNodeId = *destinationNodeId;
  385. newItem.referenceTypeId = rd->referenceTypeId;
  386. newItem.isForward = true;
  387. newItem.targetNodeId = rd->nodeId;
  388. newItem.targetNodeClass = UA_NODECLASS_METHOD;
  389. Operation_addReference(server, session, NULL, &newItem, &retval);
  390. return retval;
  391. }
  392. /* Node exists and is a variable or object. Instantiate missing mandatory
  393. * children */
  394. if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
  395. rd->nodeClass == UA_NODECLASS_OBJECT) {
  396. /* Get the node */
  397. UA_Node *node;
  398. retval = UA_Nodestore_getCopy(server, &rd->nodeId.nodeId, &node);
  399. if(retval != UA_STATUSCODE_GOOD)
  400. return retval;
  401. /* Get the type */
  402. const UA_Node *type = getNodeType(server, node);
  403. const UA_NodeId *typeId;
  404. if(type)
  405. typeId = &type->nodeId;
  406. else
  407. typeId = &UA_NODEID_NULL;
  408. /* Reset the NodeId (random numeric id will be assigned in the nodestore) */
  409. UA_NodeId_deleteMembers(&node->nodeId);
  410. node->nodeId.namespaceIndex = destinationNodeId->namespaceIndex;
  411. /* Remove references, they are re-created from scratch in addnode_finish */
  412. /* TODO: Be more clever in removing references that are re-added during
  413. * addnode_finish. That way, we can call addnode_finish also on children that were
  414. * manually added by the user during addnode_begin and addnode_finish. */
  415. /* For now we keep all the modelling rule references and delete all others */
  416. UA_NodeId modellingRuleReferenceId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE);
  417. deleteReferencesSubset(node, 1, &modellingRuleReferenceId);
  418. /* Add the node to the nodestore */
  419. UA_NodeId newNodeId;
  420. retval = UA_Nodestore_insert(server, node, &newNodeId);
  421. if(retval != UA_STATUSCODE_GOOD) {
  422. UA_Nodestore_release(server, type);
  423. return retval;
  424. }
  425. /* Add all the children of this child to the new child node to make sure we take
  426. * the values from the nearest inherited object first.
  427. */
  428. copyChildNodes(server, session, &rd->nodeId.nodeId, &newNodeId);
  429. /* Call addnode_finish, this recursively adds additional members, the type
  430. * definition and so on of the base type of this child, if they are not yet
  431. * in the destination */
  432. retval = Operation_addNode_finish(server, session, &newNodeId, destinationNodeId,
  433. &rd->referenceTypeId, typeId);
  434. UA_NodeId_deleteMembers(&newNodeId);
  435. UA_Nodestore_release(server, type);
  436. }
  437. return retval;
  438. }
  439. /* Copy any children of Node sourceNodeId to another node destinationNodeId. */
  440. static UA_StatusCode
  441. copyChildNodes(UA_Server *server, UA_Session *session,
  442. const UA_NodeId *sourceNodeId, const UA_NodeId *destinationNodeId) {
  443. /* Browse to get all children of the source */
  444. UA_BrowseDescription bd;
  445. UA_BrowseDescription_init(&bd);
  446. bd.nodeId = *sourceNodeId;
  447. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  448. bd.includeSubtypes = true;
  449. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  450. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  451. bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
  452. UA_BROWSERESULTMASK_BROWSENAME;
  453. UA_BrowseResult br;
  454. UA_BrowseResult_init(&br);
  455. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  456. if(br.statusCode != UA_STATUSCODE_GOOD)
  457. return br.statusCode;
  458. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  459. for(size_t i = 0; i < br.referencesSize; ++i) {
  460. UA_ReferenceDescription *rd = &br.references[i];
  461. retval |= copyChildNode(server, session, destinationNodeId, rd);
  462. }
  463. UA_BrowseResult_deleteMembers(&br);
  464. return retval;
  465. }
  466. static UA_StatusCode
  467. addChildren(UA_Server *server, UA_Session *session,
  468. const UA_Node *node, const UA_Node *type) {
  469. /* Get the hierarchy of the type and all its supertypes */
  470. UA_NodeId *hierarchy = NULL;
  471. size_t hierarchySize = 0;
  472. UA_StatusCode retval = getTypeHierarchy(&server->config.nodestore, &type->nodeId,
  473. &hierarchy, &hierarchySize);
  474. if(retval != UA_STATUSCODE_GOOD)
  475. return retval;
  476. /* Copy members of the type and supertypes (and instantiate them) */
  477. for(size_t i = 0; i < hierarchySize; ++i)
  478. retval |= copyChildNodes(server, session, &hierarchy[i], &node->nodeId);
  479. UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
  480. return retval;
  481. }
  482. /* Calls the global destructor internally of the global constructor succeeds and
  483. * the type-level constructor fails. */
  484. static UA_StatusCode callConstructors(UA_Server *server, UA_Session *session,
  485. const UA_Node *node, const UA_Node *type) {
  486. /* Get the node type constructor */
  487. const UA_NodeTypeLifecycle *lifecycle = NULL;
  488. if(node->nodeClass == UA_NODECLASS_OBJECT) {
  489. const UA_ObjectTypeNode *ot = (const UA_ObjectTypeNode*)type;
  490. lifecycle = &ot->lifecycle;
  491. } else if(node->nodeClass == UA_NODECLASS_VARIABLE) {
  492. const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)type;
  493. lifecycle = &vt->lifecycle;
  494. }
  495. /* Call the global constructor */
  496. void *context = node->context;
  497. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  498. if(server->config.nodeLifecycle.constructor)
  499. retval = server->config.nodeLifecycle.constructor(server, &session->sessionId,
  500. session->sessionHandle,
  501. &node->nodeId, &context);
  502. /* Call the type constructor */
  503. if(retval == UA_STATUSCODE_GOOD && lifecycle && lifecycle->constructor)
  504. retval = lifecycle->constructor(server, &session->sessionId,
  505. session->sessionHandle, &type->nodeId,
  506. type->context, &node->nodeId, &context);
  507. /* Set the context *and* mark the node as constructed */
  508. if(retval == UA_STATUSCODE_GOOD)
  509. retval = UA_Server_editNode(server, &adminSession, &node->nodeId,
  510. (UA_EditNodeCallback)editNodeContext,
  511. context);
  512. /* Fail. Call the global destructor. */
  513. if(retval != UA_STATUSCODE_GOOD && server->config.nodeLifecycle.destructor)
  514. server->config.nodeLifecycle.destructor(server, &session->sessionId,
  515. session->sessionHandle,
  516. &node->nodeId, context);
  517. return retval;
  518. }
  519. static UA_StatusCode
  520. addTypeDefRef(UA_Server *server, UA_Session *session,
  521. const UA_Node *node, const UA_Node *type) {
  522. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  523. UA_AddReferencesItem addref;
  524. UA_AddReferencesItem_init(&addref);
  525. addref.sourceNodeId = node->nodeId;
  526. addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
  527. addref.isForward = true;
  528. addref.targetNodeId.nodeId = type->nodeId;
  529. Operation_addReference(server, session, NULL, &addref, &retval);
  530. return retval;
  531. }
  532. static UA_StatusCode
  533. addParentRef(UA_Server *server, UA_Session *session,
  534. const UA_NodeId *nodeId,
  535. const UA_NodeId *referenceTypeId,
  536. const UA_NodeId *parentNodeId) {
  537. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  538. UA_AddReferencesItem ref_item;
  539. UA_AddReferencesItem_init(&ref_item);
  540. ref_item.sourceNodeId = *nodeId;
  541. ref_item.referenceTypeId = *referenceTypeId;
  542. ref_item.isForward = false;
  543. ref_item.targetNodeId.nodeId = *parentNodeId;
  544. Operation_addReference(server, session, NULL, &ref_item, &retval);
  545. return retval;
  546. }
  547. /************/
  548. /* Add Node */
  549. /************/
  550. /* Prepare the node, then add it to the nodestore */
  551. UA_StatusCode
  552. Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
  553. const UA_AddNodesItem *item, UA_NodeId *outNewNodeId) {
  554. /* Check the namespaceindex */
  555. if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) {
  556. UA_LOG_INFO_SESSION(server->config.logger, session,
  557. "AddNodes: Namespace invalid");
  558. return UA_STATUSCODE_BADNODEIDINVALID;
  559. }
  560. if(item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED &&
  561. item->nodeAttributes.encoding != UA_EXTENSIONOBJECT_DECODED_NODELETE) {
  562. UA_LOG_INFO_SESSION(server->config.logger, session,
  563. "AddNodes: Node attributes invalid");
  564. return UA_STATUSCODE_BADINTERNALERROR;
  565. }
  566. /* Create a node */
  567. UA_Node *node = UA_Nodestore_new(server, item->nodeClass);
  568. if(!node) {
  569. UA_LOG_INFO_SESSION(server->config.logger, session,
  570. "AddNodes: Node could not create a node "
  571. "in the nodestore");
  572. return UA_STATUSCODE_BADOUTOFMEMORY;
  573. }
  574. /* Fill the node */
  575. node->context = nodeContext;
  576. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  577. retval |= UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
  578. retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName);
  579. retval |= UA_Node_setAttributes(node, item->nodeAttributes.content.decoded.data,
  580. item->nodeAttributes.content.decoded.type);
  581. if(retval != UA_STATUSCODE_GOOD) {
  582. UA_LOG_INFO_SESSION(server->config.logger, session,
  583. "AddNodes: Node could not create a node "
  584. "with error code %s",
  585. UA_StatusCode_name(retval));
  586. UA_Nodestore_delete(server, node);
  587. return retval;
  588. }
  589. if(server->bootstrapNS0)
  590. goto finished_checks;
  591. /* Use attributes from the typedefinition */
  592. if(node->nodeClass == UA_NODECLASS_VARIABLE ||
  593. node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
  594. /* Use attributes from the type. The value and value constraints are the
  595. * same for the variable and variabletype attribute structs. */
  596. retval = useVariableTypeAttributes(server, session,
  597. (UA_VariableNode*)node, item);
  598. if(retval != UA_STATUSCODE_GOOD) {
  599. UA_LOG_INFO_SESSION(server->config.logger, session,
  600. "AddNodes: Using attributes from the variable type "
  601. "failed with error code %s",
  602. UA_StatusCode_name(retval));
  603. UA_Nodestore_delete(server, node);
  604. return retval;
  605. }
  606. }
  607. finished_checks:
  608. /* Add the node to the nodestore */
  609. retval = UA_Nodestore_insert(server, node, outNewNodeId);
  610. if(retval != UA_STATUSCODE_GOOD) {
  611. UA_LOG_INFO_SESSION(server->config.logger, session,
  612. "AddNodes: Node could not add the new node "
  613. "to the nodestore with error code %s",
  614. UA_StatusCode_name(retval));
  615. }
  616. return retval;
  617. }
  618. static void
  619. removeDeconstructedNode(UA_Server *server, UA_Session *session,
  620. const UA_Node *node, UA_Boolean removeTargetRefs);
  621. static const UA_NodeId hasSubtype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
  622. /* Children, references, type-checking, constructors. */
  623. UA_StatusCode
  624. Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
  625. const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
  626. const UA_NodeId *typeDefinitionId) {
  627. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  628. const UA_Node *type = NULL;
  629. /* Get the node */
  630. const UA_Node *node = UA_Nodestore_get(server, nodeId);
  631. if(!node)
  632. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  633. /* Use the typeDefinition as parent for type-nodes */
  634. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE ||
  635. node->nodeClass == UA_NODECLASS_OBJECTTYPE ||
  636. node->nodeClass == UA_NODECLASS_REFERENCETYPE ||
  637. node->nodeClass == UA_NODECLASS_DATATYPE) {
  638. if(UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL))
  639. referenceTypeId = &hasSubtype;
  640. const UA_Node *parentNode = UA_Nodestore_get(server, parentNodeId);
  641. if(parentNode) {
  642. if(parentNode->nodeClass == node->nodeClass)
  643. typeDefinitionId = parentNodeId;
  644. UA_Nodestore_release(server, parentNode);
  645. }
  646. }
  647. if(server->bootstrapNS0)
  648. goto get_type;
  649. /* Check parent reference. Objects may have no parent. */
  650. retval = checkParentReference(server, session, node->nodeClass,
  651. parentNodeId, referenceTypeId);
  652. if(retval != UA_STATUSCODE_GOOD) {
  653. UA_LOG_INFO_SESSION(server->config.logger, session,
  654. "AddNodes: The parent reference is invalid");
  655. UA_Nodestore_release(server, node);
  656. UA_Server_deleteNode(server, *nodeId, true);
  657. return retval;
  658. }
  659. /* Replace empty typeDefinition with the most permissive default */
  660. if((node->nodeClass == UA_NODECLASS_VARIABLE ||
  661. node->nodeClass == UA_NODECLASS_OBJECT) &&
  662. UA_NodeId_isNull(typeDefinitionId)) {
  663. UA_LOG_INFO_SESSION(server->config.logger, session,
  664. "AddNodes: No TypeDefinition; Use the default "
  665. "TypeDefinition for the Variable/Object");
  666. if(node->nodeClass == UA_NODECLASS_VARIABLE)
  667. typeDefinitionId = &baseDataVariableType;
  668. else
  669. typeDefinitionId = &baseObjectType;
  670. }
  671. get_type:
  672. /* Get the node type. There must be a typedefinition for variables, objects
  673. * and type-nodes. See the above checks. */
  674. if(!UA_NodeId_isNull(typeDefinitionId)) {
  675. /* Get the type node */
  676. type = UA_Nodestore_get(server, typeDefinitionId);
  677. if(!type) {
  678. UA_LOG_INFO_SESSION(server->config.logger, session,
  679. "AddNodes: Node type not found in nodestore");
  680. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  681. goto cleanup;
  682. }
  683. UA_Boolean typeOk = UA_FALSE;
  684. switch(node->nodeClass) {
  685. case UA_NODECLASS_DATATYPE:
  686. typeOk = type->nodeClass == UA_NODECLASS_DATATYPE;
  687. break;
  688. case UA_NODECLASS_METHOD:
  689. typeOk = type->nodeClass == UA_NODECLASS_METHOD;
  690. break;
  691. case UA_NODECLASS_OBJECT:
  692. typeOk = type->nodeClass == UA_NODECLASS_OBJECTTYPE;
  693. break;
  694. case UA_NODECLASS_OBJECTTYPE:
  695. typeOk = type->nodeClass == UA_NODECLASS_OBJECTTYPE;
  696. break;
  697. case UA_NODECLASS_REFERENCETYPE:
  698. typeOk = type->nodeClass == UA_NODECLASS_REFERENCETYPE;
  699. break;
  700. case UA_NODECLASS_VARIABLE:
  701. typeOk = type->nodeClass == UA_NODECLASS_VARIABLETYPE;
  702. break;
  703. case UA_NODECLASS_VARIABLETYPE:
  704. typeOk = type->nodeClass == UA_NODECLASS_VARIABLETYPE;
  705. break;
  706. case UA_NODECLASS_VIEW:
  707. typeOk = type->nodeClass == UA_NODECLASS_VIEW;
  708. break;
  709. default:
  710. typeOk = UA_FALSE;
  711. }
  712. if(!typeOk) {
  713. UA_LOG_INFO_SESSION(server->config.logger, session,
  714. "AddNodes: Type does not match node class");
  715. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  716. goto cleanup;
  717. }
  718. /* See if the type has the correct node class. For type-nodes, we know
  719. * that type has the same nodeClass from checkParentReference. */
  720. if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_VARIABLE) {
  721. if(((const UA_VariableTypeNode*)type)->isAbstract) {
  722. /* Abstract variable is allowed if parent is a children of a base data variable */
  723. const UA_NodeId variableTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  724. /* A variable may be of an object type which again is below BaseObjectType */
  725. const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
  726. const UA_NodeId refs[2] = {
  727. UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
  728. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
  729. };
  730. if(!isNodeInTree(&server->config.nodestore, parentNodeId, &variableTypes, refs , 2) &&
  731. !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
  732. UA_LOG_INFO_SESSION(server->config.logger, session,
  733. "AddNodes: Type of variable node must "
  734. "be VariableType and not cannot be abstract");
  735. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  736. goto cleanup;
  737. }
  738. }
  739. }
  740. if(!server->bootstrapNS0 && node->nodeClass == UA_NODECLASS_OBJECT) {
  741. if(((const UA_ObjectTypeNode*)type)->isAbstract) {
  742. /* Object node created of an abstract ObjectType. Only allowed if within BaseObjectType folder */
  743. const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
  744. const UA_NodeId refs[2] = {
  745. UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
  746. UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
  747. };
  748. if(!isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
  749. UA_LOG_INFO_SESSION(server->config.logger, session,
  750. "AddNodes: Type of object node must "
  751. "be ObjectType and not be abstract");
  752. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  753. goto cleanup;
  754. }
  755. }
  756. }
  757. }
  758. /* Check if all attributes hold the constraints of the type now. The initial
  759. * attributes must type-check. The constructor might change the attributes
  760. * again. Then, the changes are type-checked by the normal write service. */
  761. if(type && (node->nodeClass == UA_NODECLASS_VARIABLE ||
  762. node->nodeClass == UA_NODECLASS_VARIABLETYPE)) {
  763. retval = typeCheckVariableNode(server, session,
  764. (const UA_VariableNode*)node,
  765. (const UA_VariableTypeNode*)type, parentNodeId);
  766. if(retval != UA_STATUSCODE_GOOD) {
  767. UA_LOG_INFO_SESSION(server->config.logger, session,
  768. "AddNodes: Type-checking the variable node "
  769. "failed with error code %s", UA_StatusCode_name(retval));
  770. goto cleanup;
  771. }
  772. }
  773. /* Instantiate variables and objects */
  774. if(node->nodeClass == UA_NODECLASS_VARIABLE ||
  775. node->nodeClass == UA_NODECLASS_OBJECT) {
  776. UA_assert(type != NULL); /* see above */
  777. /* Add (mandatory) child nodes from the type definition */
  778. if(!server->bootstrapNS0) {
  779. retval = addChildren(server, session, node, type);
  780. if(retval != UA_STATUSCODE_GOOD) {
  781. UA_LOG_INFO_SESSION(server->config.logger, session,
  782. "AddNodes: Adding child nodes failed with error code %s",
  783. UA_StatusCode_name(retval));
  784. goto cleanup;
  785. }
  786. }
  787. /* Add a hasTypeDefinition reference */
  788. retval = addTypeDefRef(server, session, node, type);
  789. if(retval != UA_STATUSCODE_GOOD) {
  790. UA_LOG_INFO_SESSION(server->config.logger, session,
  791. "AddNodes: Adding a reference to the type "
  792. "definition failed with error code %s",
  793. UA_StatusCode_name(retval));
  794. goto cleanup;
  795. }
  796. }
  797. /* Add reference to the parent */
  798. if(!UA_NodeId_isNull(parentNodeId)) {
  799. if(UA_NodeId_isNull(referenceTypeId)) {
  800. UA_LOG_INFO_SESSION(server->config.logger, session,
  801. "AddNodes: Reference to parent cannot be null");
  802. retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
  803. goto cleanup;
  804. }
  805. retval = addParentRef(server, session, nodeId, referenceTypeId, parentNodeId);
  806. if(retval != UA_STATUSCODE_GOOD) {
  807. UA_LOG_INFO_SESSION(server->config.logger, session,
  808. "AddNodes: Adding reference to parent failed");
  809. goto cleanup;
  810. }
  811. }
  812. /* Call the constructor(s) */
  813. retval = callConstructors(server, session, node, type);
  814. if(retval != UA_STATUSCODE_GOOD) {
  815. UA_LOG_INFO_SESSION(server->config.logger, session,
  816. "AddNodes: Calling the node constructor(s) failed "
  817. "with status code %s", UA_StatusCode_name(retval));
  818. }
  819. cleanup:
  820. if(type)
  821. UA_Nodestore_release(server, type);
  822. if(retval != UA_STATUSCODE_GOOD)
  823. removeDeconstructedNode(server, session, node, true);
  824. UA_Nodestore_release(server, node);
  825. return retval;
  826. }
  827. static void
  828. Operation_addNode(UA_Server *server, UA_Session *session, void *nodeContext,
  829. const UA_AddNodesItem *item, UA_AddNodesResult *result) {
  830. /* Do not check access for server */
  831. if(session != &adminSession && server->config.accessControl.allowAddNode &&
  832. !server->config.accessControl.allowAddNode(&session->sessionId, session->sessionHandle, item)) {
  833. result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  834. return;
  835. }
  836. result->statusCode = Operation_addNode_begin(server, session, nodeContext,
  837. item, &result->addedNodeId);
  838. if(result->statusCode != UA_STATUSCODE_GOOD)
  839. return;
  840. /* AddNodes_finish */
  841. result->statusCode =
  842. Operation_addNode_finish(server, session, &result->addedNodeId,
  843. &item->parentNodeId.nodeId, &item->referenceTypeId,
  844. &item->typeDefinition.nodeId);
  845. /* If finishing failed, the node was deleted */
  846. if(result->statusCode != UA_STATUSCODE_GOOD)
  847. UA_NodeId_deleteMembers(&result->addedNodeId);
  848. }
  849. void
  850. Service_AddNodes(UA_Server *server, UA_Session *session,
  851. const UA_AddNodesRequest *request,
  852. UA_AddNodesResponse *response) {
  853. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  854. "Processing AddNodesRequest");
  855. if(server->config.maxNodesPerNodeManagement != 0 &&
  856. request->nodesToAddSize > server->config.maxNodesPerNodeManagement) {
  857. response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
  858. return;
  859. }
  860. response->responseHeader.serviceResult =
  861. UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addNode, NULL,
  862. &request->nodesToAddSize, &UA_TYPES[UA_TYPES_ADDNODESITEM],
  863. &response->resultsSize, &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
  864. }
  865. UA_StatusCode
  866. __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
  867. const UA_NodeId *requestedNewNodeId,
  868. const UA_NodeId *parentNodeId,
  869. const UA_NodeId *referenceTypeId,
  870. const UA_QualifiedName browseName,
  871. const UA_NodeId *typeDefinition,
  872. const UA_NodeAttributes *attr,
  873. const UA_DataType *attributeType,
  874. void *nodeContext, UA_NodeId *outNewNodeId) {
  875. /* Create the AddNodesItem */
  876. UA_AddNodesItem item;
  877. UA_AddNodesItem_init(&item);
  878. item.nodeClass = nodeClass;
  879. item.requestedNewNodeId.nodeId = *requestedNewNodeId;
  880. item.browseName = browseName;
  881. item.parentNodeId.nodeId = *parentNodeId;
  882. item.referenceTypeId = *referenceTypeId;
  883. item.typeDefinition.nodeId = *typeDefinition;
  884. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  885. item.nodeAttributes.content.decoded.type = attributeType;
  886. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
  887. /* Call the normal addnodes service */
  888. UA_AddNodesResult result;
  889. UA_AddNodesResult_init(&result);
  890. Operation_addNode(server, &adminSession, nodeContext, &item, &result);
  891. if(outNewNodeId)
  892. *outNewNodeId = result.addedNodeId;
  893. else
  894. UA_NodeId_deleteMembers(&result.addedNodeId);
  895. return result.statusCode;
  896. }
  897. UA_StatusCode
  898. UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
  899. const UA_NodeId requestedNewNodeId,
  900. const UA_QualifiedName browseName,
  901. const UA_NodeId typeDefinition,
  902. const void *attr, const UA_DataType *attributeType,
  903. void *nodeContext, UA_NodeId *outNewNodeId) {
  904. UA_AddNodesItem item;
  905. UA_AddNodesItem_init(&item);
  906. item.nodeClass = nodeClass;
  907. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  908. item.browseName = browseName;
  909. item.typeDefinition.nodeId = typeDefinition;
  910. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  911. item.nodeAttributes.content.decoded.type = attributeType;
  912. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
  913. return Operation_addNode_begin(server, &adminSession, nodeContext,
  914. &item, outNewNodeId);
  915. }
  916. UA_StatusCode
  917. UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
  918. const UA_NodeId parentNodeId,
  919. const UA_NodeId referenceTypeId,
  920. const UA_NodeId typeDefinitionId) {
  921. return Operation_addNode_finish(server, &adminSession, &nodeId, &parentNodeId,
  922. &referenceTypeId, &typeDefinitionId);
  923. }
  924. /****************/
  925. /* Delete Nodes */
  926. /****************/
  927. static void
  928. Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
  929. const UA_DeleteReferencesItem *item, UA_StatusCode *retval);
  930. /* Remove references to this node (in the other nodes) */
  931. static void
  932. removeIncomingReferences(UA_Server *server, UA_Session *session,
  933. const UA_Node *node) {
  934. UA_DeleteReferencesItem item;
  935. UA_DeleteReferencesItem_init(&item);
  936. item.targetNodeId.nodeId = node->nodeId;
  937. item.deleteBidirectional = false;
  938. UA_StatusCode dummy;
  939. for(size_t i = 0; i < node->referencesSize; ++i) {
  940. UA_NodeReferenceKind *refs = &node->references[i];
  941. item.isForward = refs->isInverse;
  942. item.referenceTypeId = refs->referenceTypeId;
  943. for(size_t j = 0; j < refs->targetIdsSize; ++j) {
  944. item.sourceNodeId = refs->targetIds[j].nodeId;
  945. Operation_deleteReference(server, session, NULL, &item, &dummy);
  946. }
  947. }
  948. }
  949. static void
  950. deconstructNode(UA_Server *server, UA_Session *session,
  951. const UA_Node *node) {
  952. /* Call the type-level destructor */
  953. void *context = node->context; /* No longer needed after this function */
  954. if(node->nodeClass == UA_NODECLASS_OBJECT ||
  955. node->nodeClass == UA_NODECLASS_VARIABLE) {
  956. const UA_Node *type = getNodeType(server, node);
  957. if(type) {
  958. const UA_NodeTypeLifecycle *lifecycle;
  959. if(node->nodeClass == UA_NODECLASS_OBJECT)
  960. lifecycle = &((const UA_ObjectTypeNode*)type)->lifecycle;
  961. else
  962. lifecycle = &((const UA_VariableTypeNode*)type)->lifecycle;
  963. if(lifecycle->destructor)
  964. lifecycle->destructor(server,
  965. &session->sessionId, session->sessionHandle,
  966. &type->nodeId, type->context,
  967. &node->nodeId, &context);
  968. UA_Nodestore_release(server, type);
  969. }
  970. }
  971. /* Call the global destructor */
  972. if(server->config.nodeLifecycle.destructor)
  973. server->config.nodeLifecycle.destructor(server, &session->sessionId,
  974. session->sessionHandle,
  975. &node->nodeId, context);
  976. }
  977. static void
  978. deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
  979. const UA_DeleteNodesItem *item, UA_StatusCode *result);
  980. static void
  981. removeChildren(UA_Server *server, UA_Session *session,
  982. const UA_Node *node) {
  983. /* Browse to get all children of the node */
  984. UA_BrowseDescription bd;
  985. UA_BrowseDescription_init(&bd);
  986. bd.nodeId = node->nodeId;
  987. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
  988. bd.includeSubtypes = true;
  989. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  990. bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
  991. bd.resultMask = UA_BROWSERESULTMASK_NONE;
  992. UA_BrowseResult br;
  993. UA_BrowseResult_init(&br);
  994. Service_Browse_single(server, session, NULL, &bd, 0, &br);
  995. if(br.statusCode != UA_STATUSCODE_GOOD)
  996. return;
  997. UA_DeleteNodesItem item;
  998. item.deleteTargetReferences = true;
  999. /* Remove every child */
  1000. for(size_t i = 0; i < br.referencesSize; ++i) {
  1001. UA_ReferenceDescription *rd = &br.references[i];
  1002. // check for self-reference to avoid endless loop
  1003. if(UA_NodeId_equal(&node->nodeId, &rd->nodeId.nodeId))
  1004. continue;
  1005. item.nodeId = rd->nodeId.nodeId;
  1006. UA_StatusCode retval;
  1007. deleteNodeOperation(server, session, NULL, &item, &retval);
  1008. }
  1009. UA_BrowseResult_deleteMembers(&br);
  1010. }
  1011. static void
  1012. removeDeconstructedNode(UA_Server *server, UA_Session *session,
  1013. const UA_Node *node, UA_Boolean removeTargetRefs) {
  1014. /* Remove all children of the node */
  1015. removeChildren(server, session, node);
  1016. /* Remove references to the node (not the references going out, as the node
  1017. * will be deleted anyway) */
  1018. if(removeTargetRefs)
  1019. removeIncomingReferences(server, session, node);
  1020. /* Remove the node in the nodestore */
  1021. UA_Nodestore_remove(server, &node->nodeId);
  1022. }
  1023. static void
  1024. deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
  1025. const UA_DeleteNodesItem *item, UA_StatusCode *result) {
  1026. /* Do not check access for server */
  1027. if(session != &adminSession && server->config.accessControl.allowDeleteNode &&
  1028. !server->config.accessControl.allowDeleteNode(&session->sessionId, session->sessionHandle, item)) {
  1029. *result = UA_STATUSCODE_BADUSERACCESSDENIED;
  1030. return;
  1031. }
  1032. const UA_Node *node = UA_Nodestore_get(server, &item->nodeId);
  1033. if(!node) {
  1034. *result = UA_STATUSCODE_BADNODEIDUNKNOWN;
  1035. return;
  1036. }
  1037. if(UA_Node_hasSubTypeOrInstances(node)) {
  1038. UA_LOG_INFO_SESSION(server->config.logger, session,
  1039. "Delete Nodes: Cannot delete a type node "
  1040. "with active instances or subtypes");
  1041. UA_Nodestore_release(server, node);
  1042. *result = UA_STATUSCODE_BADINTERNALERROR;
  1043. return;
  1044. }
  1045. /* TODO: Check if the information model consistency is violated */
  1046. /* TODO: Check if the node is a mandatory child of a parent */
  1047. deconstructNode(server, session, node);
  1048. removeDeconstructedNode(server, session, node, item->deleteTargetReferences);
  1049. UA_Nodestore_release(server, node);
  1050. }
  1051. void Service_DeleteNodes(UA_Server *server, UA_Session *session,
  1052. const UA_DeleteNodesRequest *request,
  1053. UA_DeleteNodesResponse *response) {
  1054. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1055. "Processing DeleteNodesRequest");
  1056. if(server->config.maxNodesPerNodeManagement != 0 &&
  1057. request->nodesToDeleteSize > server->config.maxNodesPerNodeManagement) {
  1058. response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
  1059. return;
  1060. }
  1061. response->responseHeader.serviceResult =
  1062. UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)deleteNodeOperation, NULL,
  1063. &request->nodesToDeleteSize, &UA_TYPES[UA_TYPES_DELETENODESITEM],
  1064. &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  1065. }
  1066. UA_StatusCode
  1067. UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
  1068. UA_Boolean deleteReferences) {
  1069. UA_DeleteNodesItem item;
  1070. item.deleteTargetReferences = deleteReferences;
  1071. item.nodeId = nodeId;
  1072. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  1073. deleteNodeOperation(server, &adminSession, NULL, &item, &retval);
  1074. return retval;
  1075. }
  1076. /******************/
  1077. /* Add References */
  1078. /******************/
  1079. static UA_StatusCode
  1080. addOneWayReference(UA_Server *server, UA_Session *session,
  1081. UA_Node *node, const UA_AddReferencesItem *item) {
  1082. return UA_Node_addReference(node, item);
  1083. }
  1084. static UA_StatusCode
  1085. deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
  1086. const UA_DeleteReferencesItem *item) {
  1087. return UA_Node_deleteReference(node, item);
  1088. }
  1089. static void
  1090. Operation_addReference(UA_Server *server, UA_Session *session, void *context,
  1091. const UA_AddReferencesItem *item, UA_StatusCode *retval) {
  1092. /* Do not check access for server */
  1093. if(session != &adminSession && server->config.accessControl.allowAddReference &&
  1094. !server->config.accessControl. allowAddReference(&session->sessionId, session->sessionHandle, item)) {
  1095. *retval = UA_STATUSCODE_BADUSERACCESSDENIED;
  1096. return;
  1097. }
  1098. /* Currently no expandednodeids are allowed */
  1099. if(item->targetServerUri.length > 0) {
  1100. *retval = UA_STATUSCODE_BADNOTIMPLEMENTED;
  1101. return;
  1102. }
  1103. /* Add the first direction */
  1104. *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  1105. (UA_EditNodeCallback)addOneWayReference, item);
  1106. if(*retval != UA_STATUSCODE_GOOD)
  1107. return;
  1108. /* Add the second direction */
  1109. UA_AddReferencesItem secondItem;
  1110. UA_AddReferencesItem_init(&secondItem);
  1111. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  1112. secondItem.referenceTypeId = item->referenceTypeId;
  1113. secondItem.isForward = !item->isForward;
  1114. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  1115. /* keep default secondItem.targetNodeClass = UA_NODECLASS_UNSPECIFIED */
  1116. *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  1117. (UA_EditNodeCallback)addOneWayReference, &secondItem);
  1118. /* remove reference if the second direction failed */
  1119. if(*retval != UA_STATUSCODE_GOOD) {
  1120. UA_DeleteReferencesItem deleteItem;
  1121. deleteItem.sourceNodeId = item->sourceNodeId;
  1122. deleteItem.referenceTypeId = item->referenceTypeId;
  1123. deleteItem.isForward = item->isForward;
  1124. deleteItem.targetNodeId = item->targetNodeId;
  1125. deleteItem.deleteBidirectional = false;
  1126. /* ignore returned status code */
  1127. UA_Server_editNode(server, session, &item->sourceNodeId,
  1128. (UA_EditNodeCallback)deleteOneWayReference, &deleteItem);
  1129. }
  1130. }
  1131. void Service_AddReferences(UA_Server *server, UA_Session *session,
  1132. const UA_AddReferencesRequest *request,
  1133. UA_AddReferencesResponse *response) {
  1134. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1135. "Processing AddReferencesRequest");
  1136. if(server->config.maxNodesPerNodeManagement != 0 &&
  1137. request->referencesToAddSize > server->config.maxNodesPerNodeManagement) {
  1138. response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
  1139. return;
  1140. }
  1141. response->responseHeader.serviceResult =
  1142. UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addReference, NULL,
  1143. &request->referencesToAddSize, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
  1144. &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  1145. }
  1146. UA_StatusCode
  1147. UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
  1148. const UA_NodeId refTypeId,
  1149. const UA_ExpandedNodeId targetId,
  1150. UA_Boolean isForward) {
  1151. UA_AddReferencesItem item;
  1152. UA_AddReferencesItem_init(&item);
  1153. item.sourceNodeId = sourceId;
  1154. item.referenceTypeId = refTypeId;
  1155. item.isForward = isForward;
  1156. item.targetNodeId = targetId;
  1157. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  1158. Operation_addReference(server, &adminSession, NULL, &item, &retval);
  1159. return retval;
  1160. }
  1161. /*********************/
  1162. /* Delete References */
  1163. /*********************/
  1164. static void
  1165. Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
  1166. const UA_DeleteReferencesItem *item, UA_StatusCode *retval) {
  1167. /* Do not check access for server */
  1168. if(session != &adminSession && server->config.accessControl.allowDeleteReference &&
  1169. !server->config.accessControl.allowDeleteReference(&session->sessionId, session->sessionHandle, item)) {
  1170. *retval = UA_STATUSCODE_BADUSERACCESSDENIED;
  1171. return;
  1172. }
  1173. // TODO: Check consistency constraints, remove the references.
  1174. *retval = UA_Server_editNode(server, session, &item->sourceNodeId,
  1175. (UA_EditNodeCallback)deleteOneWayReference, item);
  1176. if(*retval != UA_STATUSCODE_GOOD)
  1177. return;
  1178. if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
  1179. return;
  1180. UA_DeleteReferencesItem secondItem;
  1181. UA_DeleteReferencesItem_init(&secondItem);
  1182. secondItem.isForward = !item->isForward;
  1183. secondItem.sourceNodeId = item->targetNodeId.nodeId;
  1184. secondItem.targetNodeId.nodeId = item->sourceNodeId;
  1185. secondItem.referenceTypeId = item->referenceTypeId;
  1186. *retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
  1187. (UA_EditNodeCallback)deleteOneWayReference,
  1188. &secondItem);
  1189. }
  1190. void
  1191. Service_DeleteReferences(UA_Server *server, UA_Session *session,
  1192. const UA_DeleteReferencesRequest *request,
  1193. UA_DeleteReferencesResponse *response) {
  1194. UA_LOG_DEBUG_SESSION(server->config.logger, session,
  1195. "Processing DeleteReferencesRequest");
  1196. if(server->config.maxNodesPerNodeManagement != 0 &&
  1197. request->referencesToDeleteSize > server->config.maxNodesPerNodeManagement) {
  1198. response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
  1199. return;
  1200. }
  1201. response->responseHeader.serviceResult =
  1202. UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_deleteReference, NULL,
  1203. &request->referencesToDeleteSize, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
  1204. &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  1205. }
  1206. UA_StatusCode
  1207. UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
  1208. const UA_NodeId referenceTypeId, UA_Boolean isForward,
  1209. const UA_ExpandedNodeId targetNodeId,
  1210. UA_Boolean deleteBidirectional) {
  1211. UA_DeleteReferencesItem item;
  1212. item.sourceNodeId = sourceNodeId;
  1213. item.referenceTypeId = referenceTypeId;
  1214. item.isForward = isForward;
  1215. item.targetNodeId = targetNodeId;
  1216. item.deleteBidirectional = deleteBidirectional;
  1217. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  1218. Operation_deleteReference(server, &adminSession, NULL, &item, &retval);
  1219. return retval;
  1220. }
  1221. /**********************/
  1222. /* Set Value Callback */
  1223. /**********************/
  1224. static UA_StatusCode
  1225. setValueCallback(UA_Server *server, UA_Session *session,
  1226. UA_VariableNode *node, UA_ValueCallback *callback) {
  1227. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1228. return UA_STATUSCODE_BADNODECLASSINVALID;
  1229. node->value.data.callback = *callback;
  1230. return UA_STATUSCODE_GOOD;
  1231. }
  1232. UA_StatusCode
  1233. UA_Server_setVariableNode_valueCallback(UA_Server *server,
  1234. const UA_NodeId nodeId,
  1235. const UA_ValueCallback callback) {
  1236. return UA_Server_editNode(server, &adminSession, &nodeId,
  1237. (UA_EditNodeCallback)setValueCallback, &callback);
  1238. }
  1239. /***************************************************/
  1240. /* Special Handling of Variables with Data Sources */
  1241. /***************************************************/
  1242. UA_StatusCode
  1243. UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  1244. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  1245. const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
  1246. const UA_VariableAttributes attr, const UA_DataSource dataSource,
  1247. void *nodeContext, UA_NodeId *outNewNodeId) {
  1248. UA_AddNodesItem item;
  1249. UA_AddNodesItem_init(&item);
  1250. item.nodeClass = UA_NODECLASS_VARIABLE;
  1251. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  1252. item.browseName = browseName;
  1253. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  1254. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)&attr;
  1255. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES];
  1256. UA_NodeId newNodeId;
  1257. UA_Boolean deleteNodeId = UA_FALSE;
  1258. if(!outNewNodeId) {
  1259. newNodeId = UA_NODEID_NULL;
  1260. outNewNodeId = &newNodeId;
  1261. deleteNodeId = UA_TRUE;
  1262. }
  1263. UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, nodeContext,
  1264. &item, outNewNodeId);
  1265. if(retval != UA_STATUSCODE_GOOD)
  1266. return retval;
  1267. retval = UA_Server_setVariableNode_dataSource(server, *outNewNodeId, dataSource);
  1268. if(retval == UA_STATUSCODE_GOOD)
  1269. retval = Operation_addNode_finish(server, &adminSession, outNewNodeId,
  1270. &parentNodeId, &referenceTypeId, &typeDefinition);
  1271. if(retval != UA_STATUSCODE_GOOD || deleteNodeId)
  1272. UA_NodeId_deleteMembers(outNewNodeId);
  1273. return retval;
  1274. }
  1275. static UA_StatusCode
  1276. setDataSource(UA_Server *server, UA_Session *session,
  1277. UA_VariableNode* node, UA_DataSource *dataSource) {
  1278. if(node->nodeClass != UA_NODECLASS_VARIABLE)
  1279. return UA_STATUSCODE_BADNODECLASSINVALID;
  1280. if(node->valueSource == UA_VALUESOURCE_DATA)
  1281. UA_DataValue_deleteMembers(&node->value.data.value);
  1282. node->value.dataSource = *dataSource;
  1283. node->valueSource = UA_VALUESOURCE_DATASOURCE;
  1284. return UA_STATUSCODE_GOOD;
  1285. }
  1286. UA_StatusCode
  1287. UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
  1288. const UA_DataSource dataSource) {
  1289. return UA_Server_editNode(server, &adminSession, &nodeId,
  1290. (UA_EditNodeCallback)setDataSource,
  1291. &dataSource);
  1292. }
  1293. /************************************/
  1294. /* Special Handling of Method Nodes */
  1295. /************************************/
  1296. #ifdef UA_ENABLE_METHODCALLS
  1297. static const UA_NodeId hasproperty = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASPROPERTY}};
  1298. static const UA_NodeId propertytype = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_PROPERTYTYPE}};
  1299. UA_StatusCode
  1300. UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
  1301. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  1302. UA_MethodCallback method,
  1303. size_t inputArgumentsSize, const UA_Argument* inputArguments,
  1304. size_t outputArgumentsSize, const UA_Argument* outputArguments) {
  1305. /* Browse to see which argument nodes exist */
  1306. UA_BrowseDescription bd;
  1307. UA_BrowseDescription_init(&bd);
  1308. bd.nodeId = nodeId;
  1309. bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
  1310. bd.includeSubtypes = false;
  1311. bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
  1312. bd.nodeClassMask = UA_NODECLASS_VARIABLE;
  1313. bd.resultMask = UA_BROWSERESULTMASK_BROWSENAME;
  1314. UA_BrowseResult br;
  1315. UA_BrowseResult_init(&br);
  1316. Service_Browse_single(server, &adminSession, NULL, &bd, 0, &br);
  1317. UA_StatusCode retval = br.statusCode;
  1318. if(retval != UA_STATUSCODE_GOOD) {
  1319. UA_Server_deleteNode(server, nodeId, true);
  1320. UA_BrowseResult_deleteMembers(&br);
  1321. return retval;
  1322. }
  1323. /* Filter out the argument nodes */
  1324. UA_NodeId inputArgsId = UA_NODEID_NULL;
  1325. UA_NodeId outputArgsId = UA_NODEID_NULL;
  1326. const UA_NodeId newArgsId = UA_NODEID_NUMERIC(nodeId.namespaceIndex, 0);
  1327. const UA_QualifiedName inputArgsName = UA_QUALIFIEDNAME(0, "InputArguments");
  1328. const UA_QualifiedName outputArgsName = UA_QUALIFIEDNAME(0, "OutputArguments");
  1329. for(size_t i = 0; i < br.referencesSize; i++) {
  1330. UA_ReferenceDescription *rd = &br.references[i];
  1331. if(rd->browseName.namespaceIndex == 0 &&
  1332. UA_String_equal(&rd->browseName.name, &inputArgsName.name))
  1333. inputArgsId = rd->nodeId.nodeId;
  1334. else if(rd->browseName.namespaceIndex == 0 &&
  1335. UA_String_equal(&rd->browseName.name, &outputArgsName.name))
  1336. outputArgsId = rd->nodeId.nodeId;
  1337. }
  1338. /* Add the Input Arguments VariableNode */
  1339. if(inputArgumentsSize > 0 && UA_NodeId_isNull(&inputArgsId)) {
  1340. UA_VariableAttributes inputargs = UA_VariableAttributes_default;
  1341. inputargs.displayName = UA_LOCALIZEDTEXT("", "InputArguments");
  1342. /* UAExpert creates a monitoreditem on inputarguments ... */
  1343. inputargs.minimumSamplingInterval = 100000.0f;
  1344. inputargs.valueRank = 1;
  1345. inputargs.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
  1346. /* dirty-cast, but is treated as const ... */
  1347. UA_Variant_setArray(&inputargs.value, (void*)(uintptr_t)inputArguments,
  1348. inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
  1349. retval = UA_Server_addVariableNode(server, newArgsId, nodeId, hasproperty,
  1350. inputArgsName, propertytype, inputargs,
  1351. NULL, &inputArgsId);
  1352. }
  1353. /* Add the Output Arguments VariableNode */
  1354. if(outputArgumentsSize > 0 && UA_NodeId_isNull(&outputArgsId)) {
  1355. UA_VariableAttributes outputargs = UA_VariableAttributes_default;
  1356. outputargs.displayName = UA_LOCALIZEDTEXT("", "OutputArguments");
  1357. /* UAExpert creates a monitoreditem on outputarguments ... */
  1358. outputargs.minimumSamplingInterval = 100000.0f;
  1359. outputargs.valueRank = 1;
  1360. outputargs.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
  1361. /* dirty-cast, but is treated as const ... */
  1362. UA_Variant_setArray(&outputargs.value, (void*)(uintptr_t)outputArguments,
  1363. outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
  1364. retval |= UA_Server_addVariableNode(server, newArgsId, nodeId, hasproperty,
  1365. outputArgsName, propertytype, outputargs,
  1366. NULL, &outputArgsId);
  1367. }
  1368. retval |= UA_Server_setMethodNode_callback(server, nodeId, method);
  1369. /* Call finish to add the parent reference */
  1370. retval |= Operation_addNode_finish(server, &adminSession, &nodeId, &parentNodeId,
  1371. &referenceTypeId, &UA_NODEID_NULL);
  1372. if(retval != UA_STATUSCODE_GOOD) {
  1373. UA_Server_deleteNode(server, nodeId, true);
  1374. UA_Server_deleteNode(server, inputArgsId, true);
  1375. UA_Server_deleteNode(server, outputArgsId, true);
  1376. }
  1377. UA_BrowseResult_deleteMembers(&br);
  1378. return retval;
  1379. }
  1380. UA_StatusCode
  1381. UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
  1382. const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
  1383. const UA_QualifiedName browseName, const UA_MethodAttributes attr,
  1384. UA_MethodCallback method,
  1385. size_t inputArgumentsSize, const UA_Argument* inputArguments,
  1386. size_t outputArgumentsSize, const UA_Argument* outputArguments,
  1387. void *nodeContext, UA_NodeId *outNewNodeId) {
  1388. UA_AddNodesItem item;
  1389. UA_AddNodesItem_init(&item);
  1390. item.nodeClass = UA_NODECLASS_METHOD;
  1391. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  1392. item.browseName = browseName;
  1393. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  1394. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)&attr;
  1395. item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_METHODATTRIBUTES];
  1396. UA_NodeId newId;
  1397. if(!outNewNodeId) {
  1398. UA_NodeId_init(&newId);
  1399. outNewNodeId = &newId;
  1400. }
  1401. UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, nodeContext,
  1402. &item, outNewNodeId);
  1403. if(retval != UA_STATUSCODE_GOOD)
  1404. return retval;
  1405. retval = UA_Server_addMethodNode_finish(server, *outNewNodeId,
  1406. parentNodeId, referenceTypeId, method,
  1407. inputArgumentsSize, inputArguments,
  1408. outputArgumentsSize, outputArguments);
  1409. if(outNewNodeId == &newId)
  1410. UA_NodeId_deleteMembers(&newId);
  1411. return retval;
  1412. }
  1413. static UA_StatusCode
  1414. editMethodCallback(UA_Server *server, UA_Session* session,
  1415. UA_Node* node, void* handle) {
  1416. if(node->nodeClass != UA_NODECLASS_METHOD)
  1417. return UA_STATUSCODE_BADNODECLASSINVALID;
  1418. UA_MethodNode *mnode = (UA_MethodNode*) node;
  1419. mnode->method = (UA_MethodCallback)(uintptr_t)handle;
  1420. return UA_STATUSCODE_GOOD;
  1421. }
  1422. UA_StatusCode
  1423. UA_Server_setMethodNode_callback(UA_Server *server,
  1424. const UA_NodeId methodNodeId,
  1425. UA_MethodCallback methodCallback) {
  1426. return UA_Server_editNode(server, &adminSession, &methodNodeId,
  1427. (UA_EditNodeCallback)editMethodCallback,
  1428. (void*)(uintptr_t)methodCallback);
  1429. }
  1430. #endif
  1431. /************************/
  1432. /* Lifecycle Management */
  1433. /************************/
  1434. static UA_StatusCode
  1435. setNodeTypeLifecycle(UA_Server *server, UA_Session *session,
  1436. UA_Node* node, UA_NodeTypeLifecycle *lifecycle) {
  1437. if(node->nodeClass == UA_NODECLASS_OBJECTTYPE) {
  1438. UA_ObjectTypeNode *ot = (UA_ObjectTypeNode*)node;
  1439. ot->lifecycle = *lifecycle;
  1440. return UA_STATUSCODE_GOOD;
  1441. }
  1442. if(node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
  1443. UA_VariableTypeNode *vt = (UA_VariableTypeNode*)node;
  1444. vt->lifecycle = *lifecycle;
  1445. return UA_STATUSCODE_GOOD;
  1446. }
  1447. return UA_STATUSCODE_BADNODECLASSINVALID;
  1448. }
  1449. UA_StatusCode
  1450. UA_Server_setNodeTypeLifecycle(UA_Server *server, UA_NodeId nodeId,
  1451. UA_NodeTypeLifecycle lifecycle) {
  1452. return UA_Server_editNode(server, &adminSession, &nodeId,
  1453. (UA_EditNodeCallback)setNodeTypeLifecycle,
  1454. &lifecycle);
  1455. }