/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer) * Copyright 2015-2016 (c) Sten GrĂ¼ner * Copyright 2015 (c) Chris Iatrou * Copyright 2015, 2017 (c) Florian Palm * Copyright 2015 (c) Oleksiy Vasylyev * Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH * Copyright 2017 (c) Julian Grothoff */ #include "ua_server_internal.h" #include "ua_types_encoding_binary.h" /* There is no UA_Node_new() method here. Creating nodes is part of the * NodeStore layer */ void UA_Node_deleteMembers(UA_Node *node) { /* Delete standard content */ UA_NodeId_deleteMembers(&node->nodeId); UA_QualifiedName_deleteMembers(&node->browseName); UA_LocalizedText_deleteMembers(&node->displayName); UA_LocalizedText_deleteMembers(&node->description); /* Delete references */ UA_Node_deleteReferences(node); /* Delete unique content of the nodeclass */ switch(node->nodeClass) { case UA_NODECLASS_OBJECT: break; case UA_NODECLASS_METHOD: break; case UA_NODECLASS_OBJECTTYPE: break; case UA_NODECLASS_VARIABLE: case UA_NODECLASS_VARIABLETYPE: { UA_VariableNode *p = (UA_VariableNode*)node; UA_NodeId_deleteMembers(&p->dataType); UA_Array_delete(p->arrayDimensions, p->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); p->arrayDimensions = NULL; p->arrayDimensionsSize = 0; if(p->valueSource == UA_VALUESOURCE_DATA) UA_DataValue_deleteMembers(&p->value.data.value); break; } case UA_NODECLASS_REFERENCETYPE: { UA_ReferenceTypeNode *p = (UA_ReferenceTypeNode*)node; UA_LocalizedText_deleteMembers(&p->inverseName); break; } case UA_NODECLASS_DATATYPE: break; case UA_NODECLASS_VIEW: break; default: break; } } static UA_StatusCode UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) { dst->eventNotifier = src->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_CommonVariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { UA_StatusCode retval = UA_Array_copy(src->arrayDimensions, src->arrayDimensionsSize, (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32]); if(retval != UA_STATUSCODE_GOOD) return retval; dst->arrayDimensionsSize = src->arrayDimensionsSize; retval = UA_NodeId_copy(&src->dataType, &dst->dataType); dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_DATA) { retval |= UA_DataValue_copy(&src->value.data.value, &dst->value.data.value); dst->value.data.callback = src->value.data.callback; } else dst->value.dataSource = src->value.dataSource; return retval; } static UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { UA_StatusCode retval = UA_CommonVariableNode_copy(src, dst); dst->accessLevel = src->accessLevel; dst->minimumSamplingInterval = src->minimumSamplingInterval; dst->historizing = src->historizing; return retval; } static UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) { UA_StatusCode retval = UA_CommonVariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); dst->isAbstract = src->isAbstract; return retval; } static UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) { dst->executable = src->executable; dst->method = src->method; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) { dst->isAbstract = src->isAbstract; dst->lifecycle = src->lifecycle; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) { UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName); dst->isAbstract = src->isAbstract; dst->symmetric = src->symmetric; return retval; } static UA_StatusCode UA_DataTypeNode_copy(const UA_DataTypeNode *src, UA_DataTypeNode *dst) { dst->isAbstract = src->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) { dst->containsNoLoops = src->containsNoLoops; dst->eventNotifier = src->eventNotifier; return UA_STATUSCODE_GOOD; } UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) { if(src->nodeClass != dst->nodeClass) return UA_STATUSCODE_BADINTERNALERROR; /* Copy standard content */ UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId); retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName); retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName); retval |= UA_LocalizedText_copy(&src->description, &dst->description); dst->writeMask = src->writeMask; dst->context = src->context; if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteMembers(dst); return retval; } /* Copy the references */ dst->references = NULL; if(src->referencesSize > 0) { dst->references = (UA_NodeReferenceKind*) UA_calloc(src->referencesSize, sizeof(UA_NodeReferenceKind)); if(!dst->references) { UA_Node_deleteMembers(dst); return UA_STATUSCODE_BADOUTOFMEMORY; } dst->referencesSize = src->referencesSize; for(size_t i = 0; i < src->referencesSize; ++i) { UA_NodeReferenceKind *srefs = &src->references[i]; UA_NodeReferenceKind *drefs = &dst->references[i]; drefs->isInverse = srefs->isInverse; retval = UA_NodeId_copy(&srefs->referenceTypeId, &drefs->referenceTypeId); if(retval != UA_STATUSCODE_GOOD) break; retval = UA_Array_copy(srefs->targetIds, srefs->targetIdsSize, (void**)&drefs->targetIds, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); if(retval != UA_STATUSCODE_GOOD) break; drefs->targetIdsSize = srefs->targetIdsSize; } if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteMembers(dst); return retval; } } /* Copy unique content of the nodeclass */ switch(src->nodeClass) { case UA_NODECLASS_OBJECT: retval = UA_ObjectNode_copy((const UA_ObjectNode*)src, (UA_ObjectNode*)dst); break; case UA_NODECLASS_VARIABLE: retval = UA_VariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst); break; case UA_NODECLASS_METHOD: retval = UA_MethodNode_copy((const UA_MethodNode*)src, (UA_MethodNode*)dst); break; case UA_NODECLASS_OBJECTTYPE: retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src, (UA_ObjectTypeNode*)dst); break; case UA_NODECLASS_VARIABLETYPE: retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src, (UA_VariableTypeNode*)dst); break; case UA_NODECLASS_REFERENCETYPE: retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src, (UA_ReferenceTypeNode*)dst); break; case UA_NODECLASS_DATATYPE: retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src, (UA_DataTypeNode*)dst); break; case UA_NODECLASS_VIEW: retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst); break; default: break; } if(retval != UA_STATUSCODE_GOOD) UA_Node_deleteMembers(dst); return retval; } UA_Node * UA_Node_copy_alloc(const UA_Node *src) { // use dstPtr to trick static code analysis in accepting dirty cast void *dstPtr; switch(src->nodeClass) { case UA_NODECLASS_OBJECT: dstPtr = UA_malloc(sizeof(UA_ObjectNode)); break; case UA_NODECLASS_VARIABLE: dstPtr =UA_malloc(sizeof(UA_VariableNode)); break; case UA_NODECLASS_METHOD: dstPtr = UA_malloc(sizeof(UA_MethodNode)); break; case UA_NODECLASS_OBJECTTYPE: dstPtr = UA_malloc(sizeof(UA_ObjectTypeNode)); break; case UA_NODECLASS_VARIABLETYPE: dstPtr = UA_malloc(sizeof(UA_VariableTypeNode)); break; case UA_NODECLASS_REFERENCETYPE: dstPtr = UA_malloc(sizeof(UA_ReferenceTypeNode)); break; case UA_NODECLASS_DATATYPE: dstPtr = UA_malloc(sizeof(UA_DataTypeNode)); break; case UA_NODECLASS_VIEW: dstPtr = UA_malloc(sizeof(UA_ViewNode)); break; default: return NULL; } UA_Node *dst = (UA_Node*)dstPtr; dst->nodeClass = src->nodeClass; UA_StatusCode retval = UA_Node_copy(src, dst); if(retval != UA_STATUSCODE_GOOD) { UA_free(dst); return NULL; } return dst; } /******************************/ /* Copy Attributes into Nodes */ /******************************/ static UA_StatusCode copyStandardAttributes(UA_Node *node, const UA_NodeAttributes *attr) { /* retval = UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId); */ /* retval |= UA_QualifiedName_copy(&item->browseName, &node->browseName); */ UA_StatusCode retval = UA_LocalizedText_copy(&attr->displayName, &node->displayName); retval |= UA_LocalizedText_copy(&attr->description, &node->description); node->writeMask = attr->writeMask; return retval; } static UA_StatusCode copyCommonVariableAttributes(UA_VariableNode *node, const UA_VariableAttributes *attr) { /* Copy the array dimensions */ UA_StatusCode retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize, (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval != UA_STATUSCODE_GOOD) return retval; node->arrayDimensionsSize = attr->arrayDimensionsSize; /* Data type and value rank */ retval |= UA_NodeId_copy(&attr->dataType, &node->dataType); node->valueRank = attr->valueRank; /* Copy the value */ node->valueSource = UA_VALUESOURCE_DATA; UA_NodeId extensionObject = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE); /* if we have an extension object which is still encoded (e.g. from the nodeset compiler) * we need to decode it and set the decoded value instead of the encoded object */ UA_Boolean valueSet = false; if(attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) { if (attr->value.data == UA_EMPTY_ARRAY_SENTINEL) { /* do nothing since we got an empty array of extension objects */ return UA_STATUSCODE_GOOD; } const UA_ExtensionObject *obj = (const UA_ExtensionObject *)attr->value.data; if(obj && obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) { /* TODO: Once we generate type description in the nodeset compiler, * UA_findDatatypeByBinary can be made internal to the decoding * layer. */ const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId); if(type) { void *dst = UA_Array_new(attr->value.arrayLength, type); uint8_t *tmpPos = (uint8_t *)dst; for(size_t i=0; ivalue.arrayLength; i++) { size_t offset =0; const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i]; UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, 0, NULL); if(ret != UA_STATUSCODE_GOOD) { return ret; } tmpPos += type->memSize; } UA_Variant_setArray(&node->value.data.value.value, dst, attr->value.arrayLength, type); valueSet = true; } } } if(!valueSet) retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value); node->value.data.value.hasValue = true; return retval; } static UA_StatusCode copyVariableNodeAttributes(UA_VariableNode *vnode, const UA_VariableAttributes *attr) { vnode->accessLevel = attr->accessLevel; vnode->historizing = attr->historizing; vnode->minimumSamplingInterval = attr->minimumSamplingInterval; return copyCommonVariableAttributes(vnode, attr); } static UA_StatusCode copyVariableTypeNodeAttributes(UA_VariableTypeNode *vtnode, const UA_VariableTypeAttributes *attr) { vtnode->isAbstract = attr->isAbstract; return copyCommonVariableAttributes((UA_VariableNode*)vtnode, (const UA_VariableAttributes*)attr); } static UA_StatusCode copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) { onode->eventNotifier = attr->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode, const UA_ReferenceTypeAttributes *attr) { rtnode->isAbstract = attr->isAbstract; rtnode->symmetric = attr->symmetric; return UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName); } static UA_StatusCode copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode, const UA_ObjectTypeAttributes *attr) { otnode->isAbstract = attr->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) { vnode->containsNoLoops = attr->containsNoLoops; vnode->eventNotifier = attr->eventNotifier; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode, const UA_DataTypeAttributes *attr) { dtnode->isAbstract = attr->isAbstract; return UA_STATUSCODE_GOOD; } static UA_StatusCode copyMethodNodeAttributes(UA_MethodNode *mnode, const UA_MethodAttributes *attr) { mnode->executable = attr->executable; return UA_STATUSCODE_GOOD; } #define CHECK_ATTRIBUTES(TYPE) \ if(attributeType != &UA_TYPES[UA_TYPES_##TYPE]) { \ retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \ break; \ } UA_StatusCode UA_Node_setAttributes(UA_Node *node, const void *attributes, const UA_DataType *attributeType) { /* Copy the attributes into the node */ UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(node->nodeClass) { case UA_NODECLASS_OBJECT: CHECK_ATTRIBUTES(OBJECTATTRIBUTES); retval = copyObjectNodeAttributes((UA_ObjectNode*)node, (const UA_ObjectAttributes*)attributes); break; case UA_NODECLASS_VARIABLE: CHECK_ATTRIBUTES(VARIABLEATTRIBUTES); retval = copyVariableNodeAttributes((UA_VariableNode*)node, (const UA_VariableAttributes*)attributes); break; case UA_NODECLASS_OBJECTTYPE: CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES); retval = copyObjectTypeNodeAttributes((UA_ObjectTypeNode*)node, (const UA_ObjectTypeAttributes*)attributes); break; case UA_NODECLASS_VARIABLETYPE: CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES); retval = copyVariableTypeNodeAttributes((UA_VariableTypeNode*)node, (const UA_VariableTypeAttributes*)attributes); break; case UA_NODECLASS_REFERENCETYPE: CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES); retval = copyReferenceTypeNodeAttributes((UA_ReferenceTypeNode*)node, (const UA_ReferenceTypeAttributes*)attributes); break; case UA_NODECLASS_DATATYPE: CHECK_ATTRIBUTES(DATATYPEATTRIBUTES); retval = copyDataTypeNodeAttributes((UA_DataTypeNode*)node, (const UA_DataTypeAttributes*)attributes); break; case UA_NODECLASS_VIEW: CHECK_ATTRIBUTES(VIEWATTRIBUTES); retval = copyViewNodeAttributes((UA_ViewNode*)node, (const UA_ViewAttributes*)attributes); break; case UA_NODECLASS_METHOD: CHECK_ATTRIBUTES(METHODATTRIBUTES); retval = copyMethodNodeAttributes((UA_MethodNode*)node, (const UA_MethodAttributes*)attributes); break; case UA_NODECLASS_UNSPECIFIED: default: retval = UA_STATUSCODE_BADNODECLASSINVALID; } if(retval == UA_STATUSCODE_GOOD) retval = copyStandardAttributes(node, (const UA_NodeAttributes*)attributes); if(retval != UA_STATUSCODE_GOOD) UA_Node_deleteMembers(node); return retval; } /*********************/ /* Manage References */ /*********************/ static UA_StatusCode addReferenceTarget(UA_NodeReferenceKind *refs, const UA_ExpandedNodeId *target) { UA_ExpandedNodeId *targets = (UA_ExpandedNodeId*) UA_realloc(refs->targetIds, sizeof(UA_ExpandedNodeId) * (refs->targetIdsSize+1)); if(!targets) return UA_STATUSCODE_BADOUTOFMEMORY; refs->targetIds = targets; UA_StatusCode retval = UA_ExpandedNodeId_copy(target, &refs->targetIds[refs->targetIdsSize]); if(retval == UA_STATUSCODE_GOOD) { refs->targetIdsSize++; } else if(refs->targetIdsSize == 0) { /* We had zero references before (realloc was a malloc) */ UA_free(refs->targetIds); refs->targetIds = NULL; } return retval; } static UA_StatusCode addReferenceKind(UA_Node *node, const UA_AddReferencesItem *item) { UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * (node->referencesSize+1)); if(!refs) return UA_STATUSCODE_BADOUTOFMEMORY; node->references = refs; UA_NodeReferenceKind *newRef = &refs[node->referencesSize]; memset(newRef, 0, sizeof(UA_NodeReferenceKind)); newRef->isInverse = !item->isForward; UA_StatusCode retval = UA_NodeId_copy(&item->referenceTypeId, &newRef->referenceTypeId); retval |= addReferenceTarget(newRef, &item->targetNodeId); if(retval == UA_STATUSCODE_GOOD) { node->referencesSize++; } else { UA_NodeId_deleteMembers(&newRef->referenceTypeId); if(node->referencesSize == 0) { UA_free(node->references); node->references = NULL; } } return retval; } UA_StatusCode UA_Node_addReference(UA_Node *node, const UA_AddReferencesItem *item) { UA_NodeReferenceKind *existingRefs = NULL; for(size_t i = 0; i < node->referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->references[i]; if(refs->isInverse != item->isForward && UA_NodeId_equal(&refs->referenceTypeId, &item->referenceTypeId)) { existingRefs = refs; break; } } if(existingRefs != NULL) { for(size_t i = 0; i < existingRefs->targetIdsSize; i++) { if(UA_ExpandedNodeId_equal(&existingRefs->targetIds[i], &item->targetNodeId)) { return UA_STATUSCODE_BADDUPLICATEREFERENCENOTALLOWED; } } return addReferenceTarget(existingRefs, &item->targetNodeId); } return addReferenceKind(node, item); } UA_StatusCode UA_Node_deleteReference(UA_Node *node, const UA_DeleteReferencesItem *item) { for(size_t i = node->referencesSize; i > 0; --i) { UA_NodeReferenceKind *refs = &node->references[i-1]; if(item->isForward == refs->isInverse) continue; if(!UA_NodeId_equal(&item->referenceTypeId, &refs->referenceTypeId)) continue; for(size_t j = refs->targetIdsSize; j > 0; --j) { if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &refs->targetIds[j-1].nodeId)) continue; /* Ok, delete the reference */ UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j-1]); refs->targetIdsSize--; /* One matching target remaining */ if(refs->targetIdsSize > 0) { if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source // and destination overlap in // memcpy refs->targetIds[j-1] = refs->targetIds[refs->targetIdsSize]; return UA_STATUSCODE_GOOD; } /* Remove refs */ UA_free(refs->targetIds); UA_NodeId_deleteMembers(&refs->referenceTypeId); node->referencesSize--; if(node->referencesSize > 0) { if(i-1 != node->referencesSize) // avoid valgrind error: Source // and destination overlap in // memcpy node->references[i-1] = node->references[node->referencesSize]; return UA_STATUSCODE_GOOD; } /* Remove the node references */ UA_free(node->references); node->references = NULL; return UA_STATUSCODE_GOOD; } } return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED; } void UA_Node_deleteReferences(UA_Node *node) { for(size_t i = 0; i < node->referencesSize; ++i) { UA_NodeReferenceKind *refs = &node->references[i]; UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]); UA_NodeId_deleteMembers(&refs->referenceTypeId); } if(node->references) UA_free(node->references); node->references = NULL; node->referencesSize = 0; }