|
@@ -1,6 +1,83 @@
|
|
|
#include "ua_server_internal.h"
|
|
|
#include "ua_services.h"
|
|
|
|
|
|
+/**********************/
|
|
|
+/* Consistency Checks */
|
|
|
+/**********************/
|
|
|
+
|
|
|
+/* Check if the requested parent node exists, has the right node class and is
|
|
|
+ * referenced with an allowed (hierarchical) reference type. For "type" nodes,
|
|
|
+ * only hasSubType references are allowed. */
|
|
|
+static UA_StatusCode
|
|
|
+checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
|
|
|
+ const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
|
|
|
+ /* See if the parent exists */
|
|
|
+ const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
|
|
|
+ if(!parent) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Parent node not found");
|
|
|
+ return UA_STATUSCODE_BADPARENTNODEIDINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check the referencetype exists */
|
|
|
+ const UA_ReferenceTypeNode *referenceType =
|
|
|
+ (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
|
|
|
+ if(!referenceType) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Reference type to the parent not found");
|
|
|
+ return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if the referencetype is a reference type node */
|
|
|
+ if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Reference type to the parent invalid");
|
|
|
+ return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check that the reference type is not abstract */
|
|
|
+ if(referenceType->isAbstract == true) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Abstract reference type to the parent invalid");
|
|
|
+ return UA_STATUSCODE_BADREFERENCENOTALLOWED;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check hassubtype relation for type nodes */
|
|
|
+ const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
+ if(nodeClass == UA_NODECLASS_DATATYPE ||
|
|
|
+ nodeClass == UA_NODECLASS_VARIABLETYPE ||
|
|
|
+ nodeClass == UA_NODECLASS_OBJECTTYPE ||
|
|
|
+ nodeClass == UA_NODECLASS_REFERENCETYPE) {
|
|
|
+ /* type needs hassubtype reference to the supertype */
|
|
|
+ if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: New type node need to have a "
|
|
|
+ "hassubtype reference");
|
|
|
+ return UA_STATUSCODE_BADREFERENCENOTALLOWED;
|
|
|
+ }
|
|
|
+ /* supertype needs to be of the same node type */
|
|
|
+ if(parent->nodeClass != nodeClass) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: New type node needs to be of the same "
|
|
|
+ "node type as the parent");
|
|
|
+ return UA_STATUSCODE_BADPARENTNODEIDINVALID;
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Test if the referencetype is hierarchical */
|
|
|
+ const UA_NodeId hierarchicalReference =
|
|
|
+ UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
|
|
|
+ if(!isNodeInTree(server->nodestore, referenceTypeId,
|
|
|
+ &hierarchicalReference, &subtypeId, 1)) {
|
|
|
+ UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
+ "AddNodes: Reference type is not hierarchical");
|
|
|
+ return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
+ }
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
/************/
|
|
|
/* Add Node */
|
|
|
/************/
|
|
@@ -329,76 +406,6 @@ copyChildNodesToNode(UA_Server* server, UA_Session* session,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-static UA_StatusCode
|
|
|
-checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
|
|
|
- const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId) {
|
|
|
- /* See if the parent exists */
|
|
|
- const UA_Node *parent = UA_NodeStore_get(server->nodestore, parentNodeId);
|
|
|
- if(!parent) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Parent node not found");
|
|
|
- return UA_STATUSCODE_BADPARENTNODEIDINVALID;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check the referencetype exists */
|
|
|
- const UA_ReferenceTypeNode *referenceType =
|
|
|
- (const UA_ReferenceTypeNode*)UA_NodeStore_get(server->nodestore, referenceTypeId);
|
|
|
- if(!referenceType) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Reference type to the parent not found");
|
|
|
- return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check if the referencetype is a reference type node */
|
|
|
- if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Reference type to the parent invalid");
|
|
|
- return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check that the reference type is not abstract */
|
|
|
- if(referenceType->isAbstract == true) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Abstract reference type to the parent invalid");
|
|
|
- return UA_STATUSCODE_BADREFERENCENOTALLOWED;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check hassubtype relation for type nodes */
|
|
|
- const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
|
- if(nodeClass == UA_NODECLASS_DATATYPE ||
|
|
|
- nodeClass == UA_NODECLASS_VARIABLETYPE ||
|
|
|
- nodeClass == UA_NODECLASS_OBJECTTYPE ||
|
|
|
- nodeClass == UA_NODECLASS_REFERENCETYPE) {
|
|
|
- /* type needs hassubtype reference to the supertype */
|
|
|
- if(!UA_NodeId_equal(referenceTypeId, &subtypeId)) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: New type node need to have a "
|
|
|
- "hassubtype reference");
|
|
|
- return UA_STATUSCODE_BADREFERENCENOTALLOWED;
|
|
|
- }
|
|
|
- /* supertype needs to be of the same node type */
|
|
|
- if(parent->nodeClass != nodeClass) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: New type node needs to be of the same "
|
|
|
- "node type as the parent");
|
|
|
- return UA_STATUSCODE_BADPARENTNODEIDINVALID;
|
|
|
- }
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
- }
|
|
|
-
|
|
|
- /* Test if the referencetype is hierarchical */
|
|
|
- const UA_NodeId hierarchicalReference =
|
|
|
- UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
|
|
|
- if(!isNodeInTree(server->nodestore, referenceTypeId,
|
|
|
- &hierarchicalReference, &subtypeId, 1)) {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session,
|
|
|
- "AddNodes: Reference type is not hierarchical");
|
|
|
- return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
|
|
|
- }
|
|
|
-
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
-}
|
|
|
-
|
|
|
UA_StatusCode
|
|
|
Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
|
|
|
const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
|
|
@@ -491,9 +498,9 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-/***********************************************/
|
|
|
-/* Create a node from an attribute description */
|
|
|
-/***********************************************/
|
|
|
+/*******************************************/
|
|
|
+/* Create nodes from attribute description */
|
|
|
+/*******************************************/
|
|
|
|
|
|
static UA_StatusCode
|
|
|
copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
|
|
@@ -571,7 +578,7 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-variableNodeFromAttributes(UA_Server *server, UA_VariableNode *vnode,
|
|
|
+copyVariableNodeAttributes(UA_Server *server, UA_VariableNode *vnode,
|
|
|
const UA_AddNodesItem *item,
|
|
|
const UA_VariableAttributes *attr) {
|
|
|
vnode->accessLevel = attr->accessLevel;
|
|
@@ -582,7 +589,7 @@ variableNodeFromAttributes(UA_Server *server, UA_VariableNode *vnode,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-variableTypeNodeFromAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
|
|
|
+copyVariableTypeNodeAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
|
|
|
const UA_AddNodesItem *item,
|
|
|
const UA_VariableTypeAttributes *attr) {
|
|
|
vtnode->isAbstract = attr->isAbstract;
|
|
@@ -591,13 +598,13 @@ variableTypeNodeFromAttributes(UA_Server *server, UA_VariableTypeNode *vtnode,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-objectNodeFromAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
|
|
|
+copyObjectNodeAttributes(UA_ObjectNode *onode, const UA_ObjectAttributes *attr) {
|
|
|
onode->eventNotifier = attr->eventNotifier;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-referenceTypeNodeFromAttributes(UA_ReferenceTypeNode *rtnode,
|
|
|
+copyReferenceTypeNodeAttributes(UA_ReferenceTypeNode *rtnode,
|
|
|
const UA_ReferenceTypeAttributes *attr) {
|
|
|
rtnode->isAbstract = attr->isAbstract;
|
|
|
rtnode->symmetric = attr->symmetric;
|
|
@@ -605,21 +612,21 @@ referenceTypeNodeFromAttributes(UA_ReferenceTypeNode *rtnode,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-objectTypeNodeFromAttributes(UA_ObjectTypeNode *otnode,
|
|
|
+copyObjectTypeNodeAttributes(UA_ObjectTypeNode *otnode,
|
|
|
const UA_ObjectTypeAttributes *attr) {
|
|
|
otnode->isAbstract = attr->isAbstract;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-viewNodeFromAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
|
|
|
+copyViewNodeAttributes(UA_ViewNode *vnode, const UA_ViewAttributes *attr) {
|
|
|
vnode->containsNoLoops = attr->containsNoLoops;
|
|
|
vnode->eventNotifier = attr->eventNotifier;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-dataTypeNodeFromAttributes(UA_DataTypeNode *dtnode,
|
|
|
+copyDataTypeNodeAttributes(UA_DataTypeNode *dtnode,
|
|
|
const UA_DataTypeAttributes *attr) {
|
|
|
dtnode->isAbstract = attr->isAbstract;
|
|
|
return UA_STATUSCODE_GOOD;
|
|
@@ -627,77 +634,83 @@ dataTypeNodeFromAttributes(UA_DataTypeNode *dtnode,
|
|
|
|
|
|
#define CHECK_ATTRIBUTES(TYPE) \
|
|
|
if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_##TYPE]) { \
|
|
|
- result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
|
|
|
+ retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID; \
|
|
|
break; \
|
|
|
}
|
|
|
|
|
|
-static void
|
|
|
-Service_AddNodes_single(UA_Server *server, UA_Session *session,
|
|
|
- const UA_AddNodesItem *item, UA_AddNodesResult *result,
|
|
|
- UA_InstantiationCallback *instantiationCallback) {
|
|
|
+static UA_StatusCode
|
|
|
+createNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item, UA_Node **newNode) {
|
|
|
+ /* Check that we can read the attributes */
|
|
|
if(item->nodeAttributes.encoding < UA_EXTENSIONOBJECT_DECODED ||
|
|
|
- !item->nodeAttributes.content.decoded.type) {
|
|
|
- result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
|
|
|
- return;
|
|
|
- }
|
|
|
+ !item->nodeAttributes.content.decoded.type)
|
|
|
+ return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
|
|
|
|
|
|
/* Create the node */
|
|
|
- UA_Node *node = UA_NodeStore_newNode(item->nodeClass);
|
|
|
- if(!node) {
|
|
|
- // todo: case where the nodeclass is faulty
|
|
|
- result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- return;
|
|
|
- }
|
|
|
+ // todo: error case where the nodeclass is faulty
|
|
|
+ void *node = UA_NodeStore_newNode(item->nodeClass);
|
|
|
+ if(!node)
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
|
|
+ /* Copy the attributes into the node */
|
|
|
void *data = item->nodeAttributes.content.decoded.data;
|
|
|
- result->statusCode = copyStandardAttributes(node, item, data);
|
|
|
+ UA_StatusCode retval = copyStandardAttributes(node, item, data);
|
|
|
switch(item->nodeClass) {
|
|
|
case UA_NODECLASS_OBJECT:
|
|
|
CHECK_ATTRIBUTES(OBJECTATTRIBUTES);
|
|
|
- result->statusCode |= objectNodeFromAttributes((UA_ObjectNode*)node, data);
|
|
|
+ retval |= copyObjectNodeAttributes(node, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_VARIABLE:
|
|
|
CHECK_ATTRIBUTES(VARIABLEATTRIBUTES);
|
|
|
- result->statusCode |= variableNodeFromAttributes(server, (UA_VariableNode*)node,
|
|
|
- item, data);
|
|
|
+ retval |= copyVariableNodeAttributes(server, node, item, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_OBJECTTYPE:
|
|
|
CHECK_ATTRIBUTES(OBJECTTYPEATTRIBUTES);
|
|
|
- result->statusCode |= objectTypeNodeFromAttributes((UA_ObjectTypeNode*)node, data);
|
|
|
+ retval |= copyObjectTypeNodeAttributes(node, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_VARIABLETYPE:
|
|
|
CHECK_ATTRIBUTES(VARIABLETYPEATTRIBUTES);
|
|
|
- result->statusCode |= variableTypeNodeFromAttributes(server, (UA_VariableTypeNode*)node,
|
|
|
- item, data);
|
|
|
+ retval |= copyVariableTypeNodeAttributes(server, node, item, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_REFERENCETYPE:
|
|
|
CHECK_ATTRIBUTES(REFERENCETYPEATTRIBUTES);
|
|
|
- result->statusCode |= referenceTypeNodeFromAttributes((UA_ReferenceTypeNode*)node, data);
|
|
|
+ retval |= copyReferenceTypeNodeAttributes(node, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_DATATYPE:
|
|
|
CHECK_ATTRIBUTES(DATATYPEATTRIBUTES);
|
|
|
- result->statusCode |= dataTypeNodeFromAttributes((UA_DataTypeNode*)node, data);
|
|
|
+ retval |= copyDataTypeNodeAttributes(node, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_VIEW:
|
|
|
CHECK_ATTRIBUTES(VIEWATTRIBUTES);
|
|
|
- result->statusCode |= viewNodeFromAttributes((UA_ViewNode*)node, data);
|
|
|
+ retval |= copyViewNodeAttributes(node, data);
|
|
|
break;
|
|
|
case UA_NODECLASS_METHOD:
|
|
|
case UA_NODECLASS_UNSPECIFIED:
|
|
|
default:
|
|
|
- result->statusCode = UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
+ retval = UA_STATUSCODE_BADNODECLASSINVALID;
|
|
|
}
|
|
|
|
|
|
- if(result->statusCode == UA_STATUSCODE_GOOD) {
|
|
|
- result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
|
|
|
- &item->referenceTypeId, &item->typeDefinition.nodeId,
|
|
|
- instantiationCallback, &result->addedNodeId);
|
|
|
- } else {
|
|
|
- UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Could not "
|
|
|
- "prepare the new node with status code %s",
|
|
|
- UA_StatusCode_name(result->statusCode));
|
|
|
+ if(retval == UA_STATUSCODE_GOOD)
|
|
|
+ *newNode = node;
|
|
|
+ else
|
|
|
UA_NodeStore_deleteNode(node);
|
|
|
- }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+Service_AddNodes_single(UA_Server *server, UA_Session *session,
|
|
|
+ const UA_AddNodesItem *item, UA_AddNodesResult *result,
|
|
|
+ UA_InstantiationCallback *instantiationCallback) {
|
|
|
+ /* Create the node from the attributes*/
|
|
|
+ UA_Node *node;
|
|
|
+ result->statusCode = createNodeFromAttributes(server, item, &node);
|
|
|
+ if(result->statusCode != UA_STATUSCODE_GOOD)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Run consistency checks and add the node */
|
|
|
+ UA_assert(node != NULL);
|
|
|
+ result->statusCode = Service_AddNodes_existing(server, session, node, &item->parentNodeId.nodeId,
|
|
|
+ &item->referenceTypeId, &item->typeDefinition.nodeId,
|
|
|
+ instantiationCallback, &result->addedNodeId);
|
|
|
}
|
|
|
|
|
|
void Service_AddNodes(UA_Server *server, UA_Session *session,
|