Browse Source

more consistency checks for addnodes; improve instantiation

Julius Pfrommer 8 years ago
parent
commit
350bcb7aa1

+ 3 - 0
src/server/ua_server_internal.h

@@ -127,6 +127,9 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode,
              const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds,
              size_t referenceTypeIdsSize, UA_Boolean *found);
 
+const UA_Node *
+getNodeType(UA_Server *server, const UA_Node *node);
+
 /***************************************/
 /* Check Information Model Consistency */
 /***************************************/

+ 1 - 1
src/server/ua_server_utils.c

@@ -182,7 +182,7 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeT
     return UA_STATUSCODE_GOOD;
 }
 
-static const UA_Node *
+const UA_Node *
 getNodeType(UA_Server *server, const UA_Node *node) {
     /* The reference to the parent is different for variable and variabletype */ 
     UA_NodeId parentRef;

+ 5 - 5
src/server/ua_services_attribute.c

@@ -93,10 +93,10 @@ static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"Defa
 /* static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"}; */
 
 #define CHECK_NODECLASS(CLASS)                                  \
-        if(!(node->nodeClass & (CLASS))) {                      \
-            retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;       \
-            break;                                              \
-        }
+    if(!(node->nodeClass & (CLASS))) {                          \
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
+        break;                                                  \
+    }
 
 /* Reads a single attribute from a node in the nodestore */
 void Service_Read_single(UA_Server *server, UA_Session *session,
@@ -448,7 +448,7 @@ UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableD
      * correct type definition after the following paragraph */
     if(!UA_NodeId_equal(valueDataTypeId, variableDataTypeId)) {
         /* is this a subtype? */
-        UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+        const UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
         UA_Boolean found = false;
         UA_StatusCode retval = isNodeInTree(server->nodestore, valueDataTypeId,
                                             variableDataTypeId, &subtypeId, 1, &found);

+ 96 - 71
src/server/ua_services_nodemanagement.c

@@ -17,59 +17,119 @@ static UA_StatusCode
 instantiateObjectNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
                       const UA_NodeId *typeId, UA_InstantiationCallback *instantiationCallback);
 
-UA_StatusCode
-Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
-                          const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
-                          const UA_NodeId *typeDefinition,
-                          UA_InstantiationCallback *instantiationCallback,
-                          UA_NodeId *addedNodeId) {
-    /* Check the namespaceindex */
-    if(node->nodeId.namespaceIndex >= server->namespacesSize) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
-        UA_NodeStore_deleteNode(node);
-        return UA_STATUSCODE_BADNODEIDINVALID;
-    }
-
+static UA_StatusCode
+checkNewNodeReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeClass,
+                      const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                      const UA_NodeId *typeDefinition) {
     /* 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");
-        UA_NodeStore_deleteNode(node);
         return UA_STATUSCODE_BADPARENTNODEIDINVALID;
     }
 
-    /* Check the referencetype to the parent */
-    // TODO: test if the referencetype is hierarchical
-    // TODO: test if referencetype is hassubtype for type nodes
+    /* 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");
-        UA_NodeStore_deleteNode(node);
         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");
-        UA_NodeStore_deleteNode(node);
         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");
-        UA_NodeStore_deleteNode(node);
         return UA_STATUSCODE_BADREFERENCENOTALLOWED;
     }
 
+    /* Checks that apply only for type nodeids */
+    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;
+        }
+    } else {
+        /* Test if the referencetype is hierarchical */
+        const UA_NodeId hierarchicalReference = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
+        UA_Boolean found = false;
+        UA_StatusCode retval = isNodeInTree(server->nodestore, referenceTypeId,
+                                            &hierarchicalReference, &subtypeId, 1, &found);
+        if(retval != UA_STATUSCODE_GOOD || !found) {
+            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,
+                          const UA_NodeId *typeDefinition,
+                          UA_InstantiationCallback *instantiationCallback,
+                          UA_NodeId *addedNodeId) {
+
+    /* Fall back to a default typedefinition for variables and objects */
+    const UA_NodeId basedatavariabletype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
+    const UA_NodeId baseobjecttype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
+    if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_OBJECT) {
+        if(UA_NodeId_isNull(typeDefinition)) {
+            if(node->nodeClass == UA_NODECLASS_VARIABLE)
+                typeDefinition = &basedatavariabletype;
+            else
+                typeDefinition = &baseobjecttype;
+        }
+    }
+    
+    /* Check the namespaceindex */
+    if(node->nodeId.namespaceIndex >= server->namespacesSize) {
+        UA_LOG_DEBUG_SESSION(server->config.logger, session, "AddNodes: Namespace invalid");
+        UA_NodeStore_deleteNode(node);
+        return UA_STATUSCODE_BADNODEIDINVALID;
+    }
+
+    /* Check the reference to the parent */
+    UA_StatusCode retval = checkNewNodeReference(server, session, node->nodeClass,
+                                                 parentNodeId, referenceTypeId, typeDefinition);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_NodeStore_deleteNode(node);
+        return retval;
+    }
+
     /* Add the node to the nodestore */
-    UA_StatusCode retval = UA_NodeStore_insert(server->nodestore, node);
+    retval = UA_NodeStore_insert(server->nodestore, node);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_DEBUG_SESSION(server->config.logger, session,
                              "AddNodes: Node could not be added to the nodestore "
                              "with error code 0x%08x", retval);
         return retval;
     }
+
+    /* Copy the nodeid if needed */
     if(addedNodeId) {
         retval = UA_NodeId_copy(&node->nodeId, addedNodeId);
         if(retval != UA_STATUSCODE_GOOD)
@@ -158,36 +218,18 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES];
     item.nodeAttributes.content.decoded.data = &attr;
-    //item.typeDefinition // is handled below
+    const UA_Node *vartype = getNodeType(server, (const UA_Node*)node);
+    if(vartype)
+        item.typeDefinition.nodeId = vartype->nodeId;
+    else
+        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
 
-    // add the new variable
+    /* add the new variable */
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
-
-    // now instantiate the variable for its hastypedefinition references
-    for(size_t i = 0; i < node->referencesSize; i++) {
-        UA_ReferenceNode *rn = &node->references[i];
-        if(rn->isInverse)
-            continue;
-        const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
-        if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef))
-            continue;
-        UA_StatusCode retval = instantiateVariableNode(server, session, &res.addedNodeId,
-                                                       &rn->targetId.nodeId, instantiationCallback);
-        if(retval != UA_STATUSCODE_GOOD) {
-            Service_DeleteNodes_single(server, &adminSession, &res.addedNodeId, true);
-            UA_AddNodesResult_deleteMembers(&res);
-            return retval;
-        }
-    }
-
-    if(instantiationCallback)
-        instantiationCallback->method(res.addedNodeId, node->nodeId,
-                                      instantiationCallback->handle);
-
-    UA_AddNodesResult_deleteMembers(&res);
-    return UA_STATUSCODE_GOOD;
+    UA_NodeId_deleteMembers(&res.addedNodeId);
+    return res.statusCode;
 }
 
 /* Copy an existing object under the given parent. Then instantiate for all
@@ -221,35 +263,18 @@ copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *obje
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES];
     item.nodeAttributes.content.decoded.data = &attr;
-    // don't add a typedefinition here.
+    const UA_Node *objtype = getNodeType(server, (const UA_Node*)node);
+    if(objtype)
+        item.typeDefinition.nodeId = objtype->nodeId;
+    else
+        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
 
     /* add the new object */
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
-
-    /* instantiate the object for all hastypedefinition references */
-    for(size_t i = 0; i < node->referencesSize; i++) {
-        UA_ReferenceNode *rn = &node->references[i];
-        if(rn->isInverse)
-            continue;
-        const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
-        if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef))
-            continue;
-        UA_StatusCode retval = instantiateObjectNode(server, session, &res.addedNodeId,
-                                                     &rn->targetId.nodeId, instantiationCallback);
-        if(retval != UA_STATUSCODE_GOOD) {
-            Service_DeleteNodes_single(server, &adminSession, &res.addedNodeId, true);
-            UA_AddNodesResult_deleteMembers(&res);
-            return retval;
-        }
-    }
-
-    if(instantiationCallback)
-        instantiationCallback->method(res.addedNodeId, node->nodeId, instantiationCallback->handle);
-
-    UA_AddNodesResult_deleteMembers(&res);
-    return UA_STATUSCODE_GOOD;
+    UA_NodeId_deleteMembers(&res.addedNodeId);
+    return res.statusCode;
 }
 
 static UA_StatusCode