blob: 07930c81f648021c2e2a7f7c2739df11d1dc4daa [file] [log] [blame]
/* 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-2018 (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;
dst->constructed = src->constructed;
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 */
size_t nodesize = 0;
switch(src->nodeClass) {
case UA_NODECLASS_OBJECT:
nodesize = sizeof(UA_ObjectNode);
break;
case UA_NODECLASS_VARIABLE:
nodesize = sizeof(UA_VariableNode);
break;
case UA_NODECLASS_METHOD:
nodesize = sizeof(UA_MethodNode);
break;
case UA_NODECLASS_OBJECTTYPE:
nodesize = sizeof(UA_ObjectTypeNode);
break;
case UA_NODECLASS_VARIABLETYPE:
nodesize = sizeof(UA_VariableTypeNode);
break;
case UA_NODECLASS_REFERENCETYPE:
nodesize = sizeof(UA_ReferenceTypeNode);
break;
case UA_NODECLASS_DATATYPE:
nodesize = sizeof(UA_DataTypeNode);
break;
case UA_NODECLASS_VIEW:
nodesize = sizeof(UA_ViewNode);
break;
default:
return NULL;
}
UA_Node *dst = (UA_Node*)UA_calloc(1,nodesize);
if(!dst)
return NULL;
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;
/* The new nodeset format has optional display name.
* See https://github.com/open62541/open62541/issues/2627
* If display name is NULL, then we take the name part of the browse name */
if (attr->displayName.text.length == 0) {
retval = UA_String_copy(&node->browseName.name,
&node->displayName.text);
} else {
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);
if(retval != UA_STATUSCODE_GOOD)
return retval;
node->valueRank = attr->valueRank;
/* Copy the value */
retval = UA_Variant_copy(&attr->value, &node->value.data.value.value);
node->valueSource = UA_VALUESOURCE_DATA;
node->value.data.value.hasValue = (node->value.data.value.value.type != NULL);
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) {
UA_ExpandedNodeId *target = &refs->targetIds[j-1];
if(!UA_NodeId_equal(&item->targetNodeId.nodeId, &target->nodeId))
continue;
/* Ok, delete the reference */
UA_ExpandedNodeId_deleteMembers(target);
refs->targetIdsSize--;
/* One matching target remaining */
if(refs->targetIdsSize > 0) {
if(j-1 != refs->targetIdsSize) // avoid valgrind error: Source
// and destination overlap in
// memcpy
*target = refs->targetIds[refs->targetIdsSize];
return UA_STATUSCODE_GOOD;
}
/* No target for the ReferenceType remaining. Remove entry. */
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;
}
/* No remaining references of any ReferenceType */
UA_free(node->references);
node->references = NULL;
return UA_STATUSCODE_GOOD;
}
}
return UA_STATUSCODE_UNCERTAINREFERENCENOTDELETED;
}
void
UA_Node_deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize,
UA_NodeId* referencesSkip) {
/* Nothing to do */
if(node->referencesSize == 0 || node->references == NULL)
return;
for(size_t i = node->referencesSize; i > 0; --i) {
UA_NodeReferenceKind *refs = &node->references[i-1];
/* Shall we keep the references of this type? */
UA_Boolean skip = false;
for(size_t j = 0; j < referencesSkipSize; j++) {
if(UA_NodeId_equal(&refs->referenceTypeId, &referencesSkip[j])) {
skip = true;
break;
}
}
if(skip)
continue;
/* Remove references */
UA_Array_delete(refs->targetIds, refs->targetIdsSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
UA_NodeId_deleteMembers(&refs->referenceTypeId);
node->referencesSize--;
/* Move last references-kind entry to this position */
if(i-1 == node->referencesSize) /* Don't memcpy over the same position */
continue;
node->references[i-1] = node->references[node->referencesSize];
}
if(node->referencesSize == 0) {
/* The array is empty. Remove. */
UA_free(node->references);
node->references = NULL;
} else {
/* Realloc to save memory */
UA_NodeReferenceKind *refs = (UA_NodeReferenceKind*)
UA_realloc(node->references, sizeof(UA_NodeReferenceKind) * node->referencesSize);
if(refs) /* Do nothing if realloc fails */
node->references = refs;
}
}
void UA_Node_deleteReferences(UA_Node *node) {
UA_Node_deleteReferencesSubset(node, 0, NULL);
}