Browse Source

Merge branch '0.2'

Julius Pfrommer 8 years ago
parent
commit
f782827364

+ 8 - 72
src/server/ua_server_binary.c

@@ -12,11 +12,6 @@
 /* Helper Functions */
 /********************/
 
-static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
-    r->requestHandle = p->requestHandle;
-    r->timestamp = UA_DateTime_now();
-}
-
 static void
 sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
           size_t offset, const UA_DataType *responseType,
@@ -28,7 +23,8 @@ sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
     void *response = UA_alloca(responseType->memSize);
     UA_init(response, responseType);
     UA_ResponseHeader *responseHeader = (UA_ResponseHeader*)response;
-    init_response_header(&requestHeader, responseHeader);
+    responseHeader->requestHandle = requestHeader.requestHandle;
+    responseHeader->timestamp = UA_DateTime_now();
     responseHeader->serviceResult = error;
     UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
     UA_RequestHeader_deleteMembers(&requestHeader);
@@ -40,16 +36,12 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
                    UA_Boolean *requiresSession) {
     switch(requestTypeId) {
-    case UA_NS0ID_GETENDPOINTSREQUEST:
-    case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_GetEndpoints;
         *requestType = &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE];
         *requiresSession = false;
         break;
-    case UA_NS0ID_FINDSERVERSREQUEST:
-    case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_FINDSERVERSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_FindServers;
         *requestType = &UA_TYPES[UA_TYPES_FINDSERVERSREQUEST];
@@ -57,78 +49,58 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *requiresSession = false;
         break;
 #ifdef UA_ENABLE_DISCOVERY
-    case UA_NS0ID_REGISTERSERVERREQUEST:
+    case UA_NS0ID_REGISTERSERVERREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_RegisterServer;
         *requestType = &UA_TYPES[UA_TYPES_REGISTERSERVERREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_REGISTERSERVERRESPONSE];
         break;
 #endif
-    case UA_NS0ID_CREATESESSIONREQUEST:
-    case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateSession;
         *requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
         *requiresSession = false;
         break;
-    case UA_NS0ID_ACTIVATESESSIONREQUEST:
-    case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ActivateSession;
         *requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
         break;
-    case UA_NS0ID_CLOSESESSIONREQUEST:
-    case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_CLOSESESSIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CloseSession;
         *requestType = &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
         break;
-    case UA_NS0ID_READREQUEST:
-    case UA_NS0ID_READREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Read;
         *requestType = &UA_TYPES[UA_TYPES_READREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
         break;
-    case UA_NS0ID_WRITEREQUEST:
-    case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_WRITEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Write;
         *requestType = &UA_TYPES[UA_TYPES_WRITEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_WRITERESPONSE];
         break;
-    case UA_NS0ID_BROWSEREQUEST:
-    case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_BROWSEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Browse;
         *requestType = &UA_TYPES[UA_TYPES_BROWSEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_BROWSERESPONSE];
         break;
-    case UA_NS0ID_BROWSENEXTREQUEST:
-    case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_BROWSENEXTREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_BrowseNext;
         *requestType = &UA_TYPES[UA_TYPES_BROWSENEXTREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_BROWSENEXTRESPONSE];
         break;
-    case UA_NS0ID_REGISTERNODESREQUEST:
-    case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_REGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_RegisterNodes;
         *requestType = &UA_TYPES[UA_TYPES_REGISTERNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_REGISTERNODESRESPONSE];
         break;
-    case UA_NS0ID_UNREGISTERNODESREQUEST:
-    case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_UNREGISTERNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_UnregisterNodes;
         *requestType = &UA_TYPES[UA_TYPES_UNREGISTERNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_UNREGISTERNODESRESPONSE];
         break;
-    case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
-    case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_TranslateBrowsePathsToNodeIds;
         *requestType = &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST];
@@ -136,70 +108,50 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         break;
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    case UA_NS0ID_CREATESUBSCRIPTIONREQUEST:
-    case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_CREATESUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateSubscription;
         *requestType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATESUBSCRIPTIONRESPONSE];
         break;
-    case UA_NS0ID_PUBLISHREQUEST:
-    case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_PUBLISHREQUEST_ENCODING_DEFAULTBINARY:
         *requestType = &UA_TYPES[UA_TYPES_PUBLISHREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_PUBLISHRESPONSE];
         break;
-    case UA_NS0ID_REPUBLISHREQUEST:
-    case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_REPUBLISHREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Republish;
         *requestType = &UA_TYPES[UA_TYPES_REPUBLISHREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_REPUBLISHRESPONSE];
         break;
-    case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST:
-    case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_MODIFYSUBSCRIPTIONREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ModifySubscription;
         *requestType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_MODIFYSUBSCRIPTIONRESPONSE];
         break;
-    case UA_NS0ID_SETPUBLISHINGMODEREQUEST:
-    case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_SETPUBLISHINGMODEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_SetPublishingMode;
         *requestType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODEREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_SETPUBLISHINGMODERESPONSE];
         break;
-    case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST:
-    case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_DELETESUBSCRIPTIONSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteSubscriptions;
         *requestType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSRESPONSE];
         break;
-    case UA_NS0ID_CREATEMONITOREDITEMSREQUEST:
-    case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_CREATEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_CreateMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_CREATEMONITOREDITEMSRESPONSE];
         break;
-    case UA_NS0ID_DELETEMONITOREDITEMSREQUEST:
-    case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_DELETEMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETEMONITOREDITEMSRESPONSE];
         break;
-    case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST:
-    case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_MODIFYMONITOREDITEMSREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_ModifyMonitoredItems;
         *requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE];
         break;
-    case UA_NS0ID_SETMONITORINGMODEREQUEST:
-    case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_SETMONITORINGMODEREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_SetMonitoringMode;
         *requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST];
@@ -208,8 +160,6 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 #endif
 
 #ifdef UA_ENABLE_METHODCALLS
-    case UA_NS0ID_CALLREQUEST:
-    case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_CALLREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_Call;
         *requestType = &UA_TYPES[UA_TYPES_CALLREQUEST];
@@ -218,29 +168,21 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 #endif
 
 #ifdef UA_ENABLE_NODEMANAGEMENT
-    case UA_NS0ID_ADDNODESREQUEST:
-    case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_ADDNODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_AddNodes;
         *requestType = &UA_TYPES[UA_TYPES_ADDNODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ADDNODESRESPONSE];
         break;
-    case UA_NS0ID_ADDREFERENCESREQUEST:
-    case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_ADDREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_AddReferences;
         *requestType = &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE];
         break;
-    case UA_NS0ID_DELETENODESREQUEST:
-    case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_DELETENODESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteNodes;
         *requestType = &UA_TYPES[UA_TYPES_DELETENODESREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_DELETENODESRESPONSE];
         break;
-    case UA_NS0ID_DELETEREFERENCESREQUEST:
-    case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTXML:
     case UA_NS0ID_DELETEREFERENCESREQUEST_ENCODING_DEFAULTBINARY:
         *service = (UA_Service)Service_DeleteReferences;
         *requestType = &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST];
@@ -418,20 +360,12 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_StatusCode retval = UA_NodeId_decodeBinary(msg, offset, &requestTypeId);
     if(retval != UA_STATUSCODE_GOOD)
         return;
+    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
+        UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
 
     /* Store the start-position of the request */
     size_t requestPos = *offset;
 
-    /* Test if the service type nodeid has the right format */
-    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC ||
-       requestTypeId.namespaceIndex != 0) {
-        UA_NodeId_deleteMembers(&requestTypeId);
-        UA_LOG_DEBUG_CHANNEL(server->config.logger, channel,
-                             "Received a non-numeric message type NodeId");
-        sendError(channel, msg, requestPos, &UA_TYPES[UA_TYPES_SERVICEFAULT],
-                  requestId, UA_STATUSCODE_BADSERVICEUNSUPPORTED);
-    }
-
     /* Get the service pointers */
     UA_Service service = NULL;
     const UA_DataType *requestType = NULL;
@@ -554,11 +488,13 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 #endif
 
     /* Call the service */
+    UA_assert(service); /* For all services besides publish, the service pointer is non-NULL*/
     service(server, session, request, response);
 
  send_response:
     /* Send the response */
-    init_response_header(request, response);
+    ((UA_ResponseHeader*)response)->requestHandle = requestHeader->requestHandle;
+    ((UA_ResponseHeader*)response)->timestamp = UA_DateTime_now();
     retval = UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
 
     if(retval != UA_STATUSCODE_GOOD)

+ 8 - 4
src/server/ua_server_internal.h

@@ -141,14 +141,18 @@ getVariableNodeType(UA_Server *server, const UA_VariableNode *node);
 const UA_ObjectTypeNode *
 getObjectNodeType(UA_Server *server, const UA_ObjectNode *node);
 
+/* Returns an array with all subtype nodeids (including the root). Subtypes need
+ * to have the same nodeClass as root and are (recursively) related with a
+ * hasSubType reference. Since multi-inheritance is possible, we test for
+ * duplicates and return evey nodeid at most once. */
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
-                 UA_NodeId **reftypes, size_t *reftypes_count);
+getTypeHierarchy(UA_NodeStore *ns, const UA_Node *rootRef, UA_Boolean inverse,
+                 UA_NodeId **typeHierarchy, size_t *typeHierarchySize);
 
-UA_StatusCode
+UA_Boolean
 isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode,
              const UA_NodeId *nodeToFind, const UA_NodeId *referenceTypeIds,
-             size_t referenceTypeIdsSize, UA_Boolean *found);
+             size_t referenceTypeIdsSize);
 
 const UA_Node *
 getNodeType(UA_Server *server, const UA_Node *node);

+ 50 - 50
src/server/ua_server_utils.c

@@ -81,60 +81,69 @@ parse_numericrange(const UA_String *str, UA_NumericRange *range) {
 /* Information Model Operations */
 /********************************/
 
-/* Returns the type and all subtypes. We start with an array with a single root
- * nodeid. When a relevant reference is found, we add the nodeids to the back of
- * the array and increase the size. Since the hierarchy is not cyclic, we can
- * safely progress in the array to process the newly found referencetype
- * nodeids. */
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
+getTypeHierarchy(UA_NodeStore *ns, const UA_Node *rootRef, UA_Boolean inverse,
                  UA_NodeId **typeHierarchy, size_t *typeHierarchySize) {
-    const UA_Node *node = UA_NodeStore_get(ns, root);
-    if(!node)
-        return UA_STATUSCODE_BADNOMATCH;
-    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-
     size_t results_size = 20; // probably too big, but saves mallocs
     UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
     if(!results)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
+    UA_StatusCode retval = UA_NodeId_copy(&rootRef->nodeId, &results[0]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_free(results);
         return retval;
     }
 
-    size_t idx = 0; // where are we currently in the array?
-    size_t last = 0; // where is the last element in the array?
+    const UA_Node *node = rootRef;
+    size_t idx = 0; /* Current index (contains NodeId of node) */
+    size_t last = 0; /* Index of the last element in the array */
     const UA_NodeId hasSubtypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    do {
-        node = UA_NodeStore_get(ns, &results[idx]);
-        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-            continue;
+    while(true) {
         for(size_t i = 0; i < node->referencesSize; i++) {
-            if(node->references[i].isInverse == true ||
+            /* is the reference relevant? */
+            if(node->references[i].isInverse != inverse ||
                !UA_NodeId_equal(&hasSubtypeNodeId, &node->references[i].referenceTypeId))
                 continue;
 
-            if(++last >= results_size) { // is the array big enough?
-                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
-                if(!new_results) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            /* is the target already considered? (multi-inheritance) */
+            UA_Boolean duplicate = false;
+            for(size_t j = 0; j <= last; j++) {
+                if(UA_NodeId_equal(&node->references[i].targetId.nodeId, &results[j])) {
+                    duplicate = true;
                     break;
                 }
-                results = new_results;
-                results_size *= 2;
             }
+            if(duplicate)
+                continue;
 
-            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
-            if(retval != UA_STATUSCODE_GOOD) {
-                last--; // for array_delete
-                break;
+            /* increase array length if necessary */
+            if(last + 1 >= results_size) {
+                                UA_NodeId *new_results =
+                                    UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
+                                if(!new_results) {
+                                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                                    break;
+                                }
+                                results = new_results;
+                                results_size *= 2;
             }
+
+            /* copy new nodeid to the end of the list */
+            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[++last]);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
         }
-    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
+
+        /* Get the next node */
+    next:
+        idx++;
+        if(idx > last || retval != UA_STATUSCODE_GOOD)
+            break;
+        node = UA_NodeStore_get(ns, &results[idx]);
+        if(!node || node->nodeClass != rootRef->nodeClass)
+            goto next;
+    }
 
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
@@ -147,18 +156,15 @@ getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
 }
 
 /* Recursively searches "upwards" in the tree following specific reference types */
-UA_StatusCode
+UA_Boolean
 isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
-             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize, UA_Boolean *found) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(UA_NodeId_equal(leafNode, nodeToFind)) {
-        *found = true;
-        return UA_STATUSCODE_GOOD;
-    }
+             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
+    if(UA_NodeId_equal(leafNode, nodeToFind))
+        return true;
 
     const UA_Node *node = UA_NodeStore_get(ns,leafNode);
     if(!node)
-        return UA_STATUSCODE_BADINTERNALERROR;
+        return false;
 
     /* Search upwards in the tree */
     for(size_t i = 0; i < node->referencesSize; i++) {
@@ -167,19 +173,13 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeT
 
         /* Recurse only for valid reference types */
         for(size_t j = 0; j < referenceTypeIdsSize; j++) {
-            if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &referenceTypeIds[j]))
-                continue;
-            retval = isNodeInTree(ns, &node->references[i].targetId.nodeId, nodeToFind,
-                                  referenceTypeIds, referenceTypeIdsSize, found);
-            if(*found || retval != UA_STATUSCODE_GOOD)
-                return retval;
-            break;
+            if(UA_NodeId_equal(&node->references[i].referenceTypeId, &referenceTypeIds[j]) &&
+               isNodeInTree(ns, &node->references[i].targetId.nodeId, nodeToFind,
+                            referenceTypeIds, referenceTypeIdsSize))
+                return true;
         }
     }
-
-    /* Dead end */
-    *found = false;
-    return UA_STATUSCODE_GOOD;
+    return false;
 }
 
 const UA_Node *

+ 5 - 10
src/server/ua_services_attribute.c

@@ -505,12 +505,8 @@ UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableD
     if(!UA_NodeId_equal(valueDataTypeId, variableDataTypeId)) {
         /* is this a subtype? */
         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);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        if(found)
+        if(isNodeInTree(server->nodestore, valueDataTypeId,
+                        variableDataTypeId, &subtypeId, 1))
             goto check_array;
 
         const UA_DataType *variableDataType = findDataType(variableDataTypeId);
@@ -595,13 +591,12 @@ UA_VariableNode_setDataType(UA_Server *server, UA_VariableNode *node,
 
     /* Does the new type match the constraints of the variabletype? */
     UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    UA_Boolean found = false;
-    UA_StatusCode retval = isNodeInTree(server->nodestore, dataType,
-                                        &vt->dataType, &subtypeId, 1, &found);
-    if(retval != UA_STATUSCODE_GOOD || !found)
+    if(!isNodeInTree(server->nodestore, dataType,
+                     &vt->dataType, &subtypeId, 1))
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
     /* Check if the current value would match the new type */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(node->value.data.value.hasValue) {
         retval = UA_Variant_matchVariableDefinition(server, dataType, node->valueRank,
                                                     node->arrayDimensionsSize,

+ 2 - 3
src/server/ua_services_call.c

@@ -92,9 +92,8 @@ Service_Call_single(UA_Server *server, UA_Session *session,
     for(size_t i = 0; i < methodCalled->referencesSize; i++) {
         if(methodCalled->references[i].isInverse &&
            UA_NodeId_equal(&methodCalled->references[i].targetId.nodeId, &withObject->nodeId)) {
-            //TODO adjust maxDepth to needed tree depth (define a variable in config?)
-            isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId,
-                         &hasComponentNodeId, &hasSubTypeNodeId, 1, &found);
+            found = isNodeInTree(server->nodestore, &methodCalled->references[i].referenceTypeId,
+                                 &hasComponentNodeId, &hasSubTypeNodeId, 1);
             if(found)
                 break;
         }

+ 169 - 243
src/server/ua_services_nodemanagement.c

@@ -16,11 +16,6 @@ copyChildNodesToNode(UA_Server *server, UA_Session *session,
                      const UA_NodeId *sourceNodeId, const UA_NodeId *destinationNodeId,
                      UA_InstantiationCallback *instantiationCallback);
 
-static UA_NodeId
-instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session, 
-                                  const UA_NodeId *searchInstance, 
-                                  const UA_QualifiedName *browseName);
-
 /* copy an existing variable under the given parent. then instantiate the
  * variable for its type */
 static UA_StatusCode
@@ -76,8 +71,7 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
     
     /* Copy any aggregated/nested variables/methods/subobjects this object contains 
-     * These objects may not be part of the nodes type.
-     */
+     * These objects may not be part of the nodes type. */
     copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
     if(instantiationCallback)
         instantiationCallback->method(res.addedNodeId, node->nodeId, 
@@ -130,8 +124,7 @@ copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *obje
     Service_AddNodes_single(server, session, &item, &res, instantiationCallback);
     
     /* Copy any aggregated/nested variables/methods/subobjects this object contains 
-     * These objects may not be part of the nodes type.
-     */
+     * These objects may not be part of the nodes type. */
     copyChildNodesToNode(server, session, &node->nodeId, &res.addedNodeId, instantiationCallback);
     if(instantiationCallback)
         instantiationCallback->method(res.addedNodeId, node->nodeId, 
@@ -153,166 +146,93 @@ setObjectInstanceHandle(UA_Server *server, UA_Session *session,
 }
 
 static UA_StatusCode
-instantiateObjectNode(UA_Server *server, UA_Session *session,
-                      const UA_NodeId *nodeId, const UA_NodeId *typeId, size_t depth,
-                      UA_InstantiationCallback *instantiationCallback) {
-    /* see if the type is derived from baseobjecttype */
-    UA_Boolean found = false;
-    const UA_NodeId hassubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    const UA_NodeId baseobjtype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-    UA_StatusCode retval = isNodeInTree(server->nodestore, typeId,
-                                        &baseobjtype, &hassubtype, 1, &found);
-    if(!found || retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: The object if not derived from BaseObjectType");
-        return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-    }
-
+instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                UA_NodeClass nodeClass, const UA_NodeId *typeId,
+                UA_InstantiationCallback *instantiationCallback) {
     /* see if the type node is correct */
-    const UA_ObjectTypeNode *typenode =
-        (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
-    if(!typenode || typenode->nodeClass != UA_NODECLASS_OBJECTTYPE || typenode->isAbstract)
+    const UA_Node *typenode = UA_NodeStore_get(server->nodestore, typeId);
+    if(!typenode)
         return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
-
-    /* Add all the child nodes */
-    copyChildNodesToNode(server, session, typeId, nodeId, instantiationCallback);
-    
-    /* Instantiate supertype attributes if a supertype is available */
-    UA_BrowseDescription browseChildren;
-    UA_BrowseDescription_init(&browseChildren);
-    UA_BrowseResult browseResult;
-    UA_BrowseResult_init(&browseResult);
-    browseChildren.nodeId = *typeId;
-    browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    browseChildren.includeSubtypes = false;
-    browseChildren.browseDirection = UA_BROWSEDIRECTION_INVERSE; // isSubtypeOf
-    browseChildren.nodeClassMask = UA_NODECLASS_OBJECTTYPE;
-    browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS;
-    // todo: continuation points if there are too many results
-    Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
-    for(size_t i = 0; i < browseResult.referencesSize; i++) {
-        UA_ReferenceDescription *rd = &browseResult.references[i];
-        instantiateObjectNode(server, session, nodeId, &rd->nodeId.nodeId,
-                              depth+1, instantiationCallback);
-    }
-    UA_BrowseResult_deleteMembers(&browseResult);
-    
-    /* add a hastypedefinition reference */
-    if(depth == 0) {
-        UA_AddReferencesItem addref;
-        UA_AddReferencesItem_init(&addref);
-        addref.sourceNodeId = *nodeId;
-        addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
-        addref.isForward = true;
-        addref.targetNodeId.nodeId = *typeId;
-        addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE;
-        Service_AddReferences_single(server, session, &addref);
-    }
-
-    /* call the constructor */
-    const UA_ObjectLifecycleManagement *olm = &typenode->lifecycleManagement;
-    if(olm->constructor)
-        UA_Server_editNode(server, session, nodeId,
-                           (UA_EditNodeCallback)setObjectInstanceHandle,
-                           olm->constructor);
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-instantiateVariableNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
-                        const UA_NodeId *typeId, size_t depth,
-                        UA_InstantiationCallback *instantiationCallback) {
-    /* see if the type is derived from basevariabletype */
-    UA_Boolean found = false;
-    const UA_NodeId hassubtype = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    const UA_NodeId basevartype = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE);
-    UA_StatusCode retval = isNodeInTree(server->nodestore, typeId,
-                                        &basevartype, &hassubtype, 1, &found);
-    if(!found || retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: The variable is not derived from BaseVariableType");
+    if(nodeClass == UA_NODECLASS_VARIABLE) {
+        if(typenode->nodeClass != UA_NODECLASS_VARIABLETYPE ||
+           ((const UA_VariableTypeNode*)typenode)->isAbstract)
+            return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+    } else if(nodeClass == UA_NODECLASS_OBJECT) {
+        if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE ||
+           ((const UA_ObjectTypeNode*)typenode)->isAbstract)
+            return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
+    } else {
         return UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
     }
 
-    /* see if the type is correct */
-    const UA_VariableTypeNode *typenode =
-        (const UA_VariableTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
-    if(!typenode || typenode->nodeClass != UA_NODECLASS_VARIABLETYPE || typenode->isAbstract)
-        return UA_STATUSCODE_BADNODEIDINVALID;
-
-    /* get the references to child properties */
-    /* Add all the child nodes */
-    copyChildNodesToNode(server, session, typeId, nodeId, instantiationCallback);
+    /* Get the hierarchy of the type and all its supertypes */
+    UA_NodeId *hierarchy = NULL;
+    size_t hierarchySize = 0;
+    UA_StatusCode retval =
+        getTypeHierarchy(server->nodestore, typenode, true, &hierarchy, &hierarchySize);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     
-    /* Instantiate supertypes */
-    UA_BrowseDescription browseChildren;
-    UA_BrowseDescription_init(&browseChildren);
-    UA_BrowseResult browseResult;
-    UA_BrowseResult_init(&browseResult);
-    browseChildren.nodeId = *typeId;
-    browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    browseChildren.includeSubtypes = false;
-    browseChildren.browseDirection = UA_BROWSEDIRECTION_INVERSE; // isSubtypeOf
-    browseChildren.nodeClassMask = UA_NODECLASS_VARIABLETYPE;
-    browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS;
-    // todo: continuation points if there are too many results
-    Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
-    for(size_t i = 0; i < browseResult.referencesSize; i++) {
-        UA_ReferenceDescription *rd = &browseResult.references[i];
-        instantiateVariableNode(server, session, nodeId, &rd->nodeId.nodeId,
-                                depth+1, instantiationCallback);
-    }
-    UA_BrowseResult_deleteMembers(&browseResult);
-
-    /* add a hastypedefinition reference */
-    if(depth == 0) {
-        UA_AddReferencesItem addref;
-        UA_AddReferencesItem_init(&addref);
-        addref.sourceNodeId = *nodeId;
-        addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
-        addref.isForward = true;
-        addref.targetNodeId.nodeId = *typeId;
-        addref.targetNodeClass = UA_NODECLASS_OBJECTTYPE;
-        /* TODO: Check result */
-        Service_AddReferences_single(server, session, &addref);
+    /* Copy members of the type and supertypes */
+    for(size_t i = 0; i < hierarchySize; i++)
+        retval |= copyChildNodesToNode(server, session, &hierarchy[i], nodeId, instantiationCallback);
+    UA_Array_delete(hierarchy, hierarchySize, &UA_TYPES[UA_TYPES_NODEID]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Call the object constructor */
+    if(typenode->nodeClass == UA_NODECLASS_OBJECTTYPE) {
+        const UA_ObjectLifecycleManagement *olm =
+            &((const UA_ObjectTypeNode*)typenode)->lifecycleManagement;
+        if(olm->constructor)
+            UA_Server_editNode(server, session, nodeId,
+                               (UA_EditNodeCallback)setObjectInstanceHandle,
+                               olm->constructor);
     }
 
-    return UA_STATUSCODE_GOOD;
-} 
+    /* Add a hasType reference */
+    UA_AddReferencesItem addref;
+    UA_AddReferencesItem_init(&addref);
+    addref.sourceNodeId = *nodeId;
+    addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
+    addref.isForward = true;
+    addref.targetNodeId.nodeId = *typeId;
+    return Service_AddReferences_single(server, session, &addref);
+}
 
 /* Search for an instance of "browseName" in node searchInstance
- * Used during copyChildNodes to find overwritable/mergable nodes
- */
-static UA_NodeId
+ * Used during copyChildNodes to find overwritable/mergable nodes */
+static UA_StatusCode
 instanceFindAggregateByBrowsename(UA_Server *server, UA_Session *session, 
                                   const UA_NodeId *searchInstance, 
-                                  const UA_QualifiedName *browseName) {
-    UA_NodeId retval = UA_NODEID_NULL;
-    
-    UA_BrowseDescription browseChildren;
-    UA_BrowseDescription_init(&browseChildren);
-    UA_NodeId_copy(searchInstance, &browseChildren.nodeId);
-    browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
-    browseChildren.includeSubtypes = true;
-    browseChildren.browseDirection = UA_BROWSEDIRECTION_FORWARD;
-    browseChildren.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
-    browseChildren.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
+                                  const UA_QualifiedName *browseName,
+                                  UA_NodeId *outInstanceNodeId) {
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = *searchInstance;
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
+    bd.includeSubtypes = true;
+    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
+    bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
+    bd.resultMask = UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
     
-    UA_BrowseResult browseResult;
-    UA_BrowseResult_init(&browseResult);
-    Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
+    UA_BrowseResult br;
+    UA_BrowseResult_init(&br);
+    Service_Browse_single(server, session, NULL, &bd, 0, &br);
+    if(br.statusCode != UA_STATUSCODE_GOOD)
+        return br.statusCode;
     
-    for(size_t i = 0; i < browseResult.referencesSize; i++) {
-      UA_ReferenceDescription *rd = &browseResult.references[i];
-      if (strncmp((const char*) rd->browseName.name.data, (const char*) browseName->name.data, 
-        (browseName->name.length>rd->browseName.name.length ? browseName->name.length : rd->browseName.name.length) ) == 0) {
-        // Found
-        retval = rd->nodeId.nodeId;
-        break;
-      }
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    for(size_t i = 0; i < br.referencesSize; i++) {
+        UA_ReferenceDescription *rd = &br.references[i];
+        if(rd->browseName.namespaceIndex == browseName->namespaceIndex &&
+           UA_String_equal(&rd->browseName.name, &browseName->name)) {
+            retval = UA_NodeId_copy(&rd->nodeId.nodeId, outInstanceNodeId);
+            break;
+        }
     }
     
-    UA_BrowseResult_deleteMembers(&browseResult);
+    UA_BrowseResult_deleteMembers(&br);
     return retval;
 }
 
@@ -327,62 +247,76 @@ static UA_StatusCode
 copyChildNodesToNode(UA_Server* server, UA_Session* session, 
                      const UA_NodeId* sourceNodeId, const UA_NodeId* destinationNodeId, 
                      UA_InstantiationCallback* instantiationCallback) {
-  /* Add all the child nodes */
-  UA_BrowseDescription browseChildren;
-  UA_BrowseDescription_init(&browseChildren);
-  UA_NodeId_copy(sourceNodeId, &browseChildren.nodeId);
-  browseChildren.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
-  browseChildren.includeSubtypes = true;
-  browseChildren.browseDirection = UA_BROWSEDIRECTION_FORWARD;
-  browseChildren.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
-  browseChildren.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS | UA_BROWSERESULTMASK_BROWSENAME;
+    /* Browse to get all children */
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = *sourceNodeId;
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES);
+    bd.includeSubtypes = true;
+    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
+    bd.nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_METHOD;
+    bd.resultMask = UA_BROWSERESULTMASK_REFERENCETYPEID | UA_BROWSERESULTMASK_NODECLASS |
+        UA_BROWSERESULTMASK_BROWSENAME;
+
+    UA_BrowseResult br;
+    UA_BrowseResult_init(&br);
+    Service_Browse_single(server, session, NULL, &bd, 0, &br);
+    if(br.statusCode != UA_STATUSCODE_GOOD)
+        return br.statusCode;
   
-  UA_BrowseResult browseResult;
-  UA_BrowseResult_init(&browseResult);
-  // todo: continuation points if there are too many results
-  Service_Browse_single(server, session, NULL, &browseChildren, 100, &browseResult);
-  
-  for(size_t i = 0; i < browseResult.referencesSize; i++) {
-    UA_ReferenceDescription *rd = &browseResult.references[i];
-    // Check for deduplication
-    UA_NodeId existing = instanceFindAggregateByBrowsename(server, session, destinationNodeId, &rd->browseName);
-    if (UA_NodeId_equal(&UA_NODEID_NULL, &existing) == UA_TRUE) { /* New node in child */
-      if(rd->nodeClass == UA_NODECLASS_METHOD) {
-        /* add a reference to the method in the objecttype */
-        UA_AddReferencesItem newItem;
-        UA_AddReferencesItem_init(&newItem);
-        UA_NodeId_copy(destinationNodeId, &newItem.sourceNodeId);
-        newItem.referenceTypeId = rd->referenceTypeId;
-        newItem.isForward = true;
-        newItem.targetNodeId = rd->nodeId;
-        newItem.targetNodeClass = UA_NODECLASS_METHOD;
-        Service_AddReferences_single(server, session, &newItem);
-      } else if(rd->nodeClass == UA_NODECLASS_VARIABLE)
-          copyExistingVariable(server, session, &rd->nodeId.nodeId,
-                              &rd->referenceTypeId, destinationNodeId, instantiationCallback);
-        else if(rd->nodeClass == UA_NODECLASS_OBJECT)
-          copyExistingObject(server, session, &rd->nodeId.nodeId,
-                            &rd->referenceTypeId, destinationNodeId, instantiationCallback);
-    }
-    else { /* Preexistent node in child */
-      /* General strategy if we meet an already existing node:
-       * - Preexistent variable contents always 'win' overwriting anything supertypes would instantiate
-       * - Always copy contents of template *into* existant node (merge contents of e.g. Folders like ParameterSet)
-       */
-      if(rd->nodeClass == UA_NODECLASS_METHOD) {} // Do nothing, existent method wins, right?
-      else if(rd->nodeClass == UA_NODECLASS_VARIABLE) {
-        if (UA_NodeId_equal(&rd->nodeId.nodeId, &existing) != UA_TRUE) 
-          copyChildNodesToNode(server, session, &rd->nodeId.nodeId, &existing, instantiationCallback);
-      }
-      else if(rd->nodeClass == UA_NODECLASS_OBJECT) {
-        if (UA_NodeId_equal(&rd->nodeId.nodeId, &existing) != UA_TRUE) 
-          copyChildNodesToNode(server, session, &rd->nodeId.nodeId, &existing, instantiationCallback);
-      }
+    /* Copy all children */
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_NodeId existingChild = UA_NODEID_NULL;
+    for(size_t i = 0; i < br.referencesSize; i++) {
+        UA_ReferenceDescription *rd = &br.references[i];
+        // Check for deduplication
+        retval = instanceFindAggregateByBrowsename(server, session, destinationNodeId,
+                                                   &rd->browseName, &existingChild);
+        if(retval != UA_STATUSCODE_GOOD)
+            break;
+        
+        if(UA_NodeId_equal(&UA_NODEID_NULL, &existingChild)) {
+            /* New node in child */
+            if(rd->nodeClass == UA_NODECLASS_METHOD) {
+                /* add a reference to the method in the objecttype */
+                UA_AddReferencesItem newItem;
+                UA_AddReferencesItem_init(&newItem);
+                newItem.sourceNodeId = *destinationNodeId;
+                newItem.referenceTypeId = rd->referenceTypeId;
+                newItem.isForward = true;
+                newItem.targetNodeId = rd->nodeId;
+                newItem.targetNodeClass = UA_NODECLASS_METHOD;
+                retval = Service_AddReferences_single(server, session, &newItem);
+            } else if(rd->nodeClass == UA_NODECLASS_VARIABLE)
+                retval = copyExistingVariable(server, session, &rd->nodeId.nodeId,
+                                              &rd->referenceTypeId, destinationNodeId,
+                                              instantiationCallback);
+            else if(rd->nodeClass == UA_NODECLASS_OBJECT)
+                retval = copyExistingObject(server, session, &rd->nodeId.nodeId,
+                                            &rd->referenceTypeId, destinationNodeId,
+                                            instantiationCallback);
+        } else {
+            /* Preexistent node in child
+             * General strategy if we meet an already existing node:
+             * - Preexistent variable contents always 'win' overwriting anything
+             *   supertypes would instantiate
+             * - Always copy contents of template *into* existant node (merge
+             *   contents of e.g. Folders like ParameterSet) */
+            if(rd->nodeClass == UA_NODECLASS_METHOD) {
+                /* Do nothing, existent method wins */
+            } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
+                      rd->nodeClass == UA_NODECLASS_OBJECT) {
+                if(!UA_NodeId_equal(&rd->nodeId.nodeId, &existingChild))
+                    retval = copyChildNodesToNode(server, session, &rd->nodeId.nodeId,
+                                                  &existingChild, instantiationCallback);
+            }
+            UA_NodeId_deleteMembers(&existingChild);
+        }
+        if(retval != UA_STATUSCODE_GOOD)
+            break;
     }
-  }
-  UA_BrowseResult_deleteMembers(&browseResult);
-  
-  return UA_STATUSCODE_GOOD;
+    UA_BrowseResult_deleteMembers(&br);
+    return retval;
 }
 
 static UA_StatusCode
@@ -445,10 +379,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
     /* 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) {
+    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;
@@ -470,19 +402,6 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
         return UA_STATUSCODE_BADNODEIDINVALID;
     }
 
-    /* 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(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
-            if(node->nodeClass == UA_NODECLASS_VARIABLE)
-                typeDefinition = &basedatavariabletype;
-            else
-                typeDefinition = &baseobjecttype;
-        }
-    }
-
     /* Check the reference to the parent */
     UA_StatusCode retval = checkParentReference(server, session, node->nodeClass,
                                                 parentNodeId, referenceTypeId);
@@ -524,17 +443,26 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
         goto remove_node;
     }
 
-    /* Instantiate variables and objects */
-    if(node->nodeClass == UA_NODECLASS_OBJECT)
-        retval = instantiateObjectNode(server, session, &node->nodeId,
-                                       typeDefinition, 0, instantiationCallback);
-    else if(node->nodeClass == UA_NODECLASS_VARIABLE)
-        retval = instantiateVariableNode(server, session, &node->nodeId,
-                                         typeDefinition, 0, instantiationCallback);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                             "AddNodes: Could not instantiate the node");
-        goto remove_node;
+    if(node->nodeClass == UA_NODECLASS_VARIABLE ||
+       node->nodeClass == UA_NODECLASS_OBJECT) {
+        /* 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(!typeDefinition || UA_NodeId_isNull(typeDefinition)) {
+            if(node->nodeClass == UA_NODECLASS_VARIABLE)
+                typeDefinition = &basedatavariabletype;
+            else
+                typeDefinition = &baseobjecttype;
+        }
+
+        /* Instantiate variables and objects */
+        retval = instantiateNode(server, session, &node->nodeId, node->nodeClass,
+                                 typeDefinition, instantiationCallback);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_DEBUG_SESSION(server->config.logger, session,
+                                 "AddNodes: Could not instantiate the node");
+            goto remove_node;
+        }
     }
 
     /* Custom callback */
@@ -578,9 +506,8 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
     
     /* Make sure we can instantiate the basetypes themselves */
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(UA_NodeId_equal(&node->nodeId, &basevartype) == UA_TRUE || 
-       UA_NodeId_equal(&node->nodeId, &basedatavartype) == UA_TRUE
-    ) {
+    if(UA_NodeId_equal(&node->nodeId, &basevartype) || 
+       UA_NodeId_equal(&node->nodeId, &basedatavartype)) {
       node->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
       node->valueRank = -2;
       return retval;
@@ -595,10 +522,6 @@ copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
     if(!UA_NodeId_isNull(&attr->dataType))
         retval  = UA_VariableNode_setDataType(server, node, vt, &attr->dataType);
     else /* workaround common error where the datatype is left as NA_NODEID_NULL */
-      /* Note that most NS0 VarTypeNodes are DataTypes, not VariableTypes! */
-      if (vt->nodeClass == UA_NODECLASS_DATATYPE) 
-        UA_NodeId_copy(&vt->nodeId, &node->dataType);
-      else
         retval = UA_VariableNode_setDataType(server, node, vt, &vt->dataType);
         
     node->valueRank = -2; /* allow all dimensions first */
@@ -1009,6 +932,9 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
 static UA_StatusCode
 addOneWayReference(UA_Server *server, UA_Session *session,
                    UA_Node *node, const UA_AddReferencesItem *item) {
+    if(item->targetNodeClass != UA_NODECLASS_UNSPECIFIED &&
+       item->targetNodeClass != node->nodeClass)
+        return UA_STATUSCODE_BADNODECLASSINVALID;
     size_t i = node->referencesSize;
     size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
     UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize);
@@ -1186,7 +1112,7 @@ Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
         /* browse type definitions with admin rights */
         UA_BrowseResult result;
         UA_BrowseResult_init(&result);
-        Service_Browse_single(server, &adminSession, NULL, &bd, UA_UINT32_MAX, &result);
+        Service_Browse_single(server, &adminSession, NULL, &bd, 0, &result);
         for(size_t i = 0; i < result.referencesSize; i++) {
             /* call the destructor */
             UA_ReferenceDescription *rd = &result.references[i];

+ 22 - 83
src/server/ua_services_view.c

@@ -125,70 +125,6 @@ returnRelevantNode(UA_Server *server, const UA_BrowseDescription *descr, UA_Bool
     return node;
 }
 
-/**
- * We find all subtypes by a single iteration over the array. We start with an array with a single
- * root nodeid at the beginning. When we find relevant references, we add the nodeids to the back of
- * the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the
- * array to process the newly found referencetype nodeids (emulated recursion).
- */
-static UA_StatusCode
-findSubTypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) {
-    const UA_Node *node = UA_NodeStore_get(ns, root);
-    if(!node)
-        return UA_STATUSCODE_BADNOMATCH;
-    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-
-    size_t results_size = 20; // probably too big, but saves mallocs
-    UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
-    if(!results)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(results);
-        return retval;
-    }
-        
-    size_t idx = 0; // where are we currently in the array?
-    size_t last = 0; // where is the last element in the array?
-    do {
-        node = UA_NodeStore_get(ns, &results[idx]);
-        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-            continue;
-        for(size_t i = 0; i < node->referencesSize; i++) {
-            if(node->references[i].referenceTypeId.identifier.numeric != UA_NS0ID_HASSUBTYPE ||
-               node->references[i].isInverse == true)
-                continue;
-
-            if(++last >= results_size) { // is the array big enough?
-                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
-                if(!new_results) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                results = new_results;
-                results_size *= 2;
-            }
-
-            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
-            if(retval != UA_STATUSCODE_GOOD) {
-                last--; // for array_delete
-                break;
-            }
-        }
-    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
-
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
-        return retval;
-    }
-
-    *reftypes = results;
-    *reftypes_count = last + 1;
-    return UA_STATUSCODE_GOOD;
-}
-
 static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     LIST_REMOVE(cp, pointers);
     UA_ByteString_deleteMembers(&cp->identifier);
@@ -197,16 +133,17 @@ static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     session->availableContinuationPoints++;
 }
 
-/**
- * Results for a single browsedescription. This is the inner loop for both Browse and BrowseNext
+/* Results for a single browsedescription. This is the inner loop for both
+ * Browse and BrowseNext
+ *
  * @param session Session to save continuationpoints
  * @param ns The nodstore where the to-be-browsed node can be found
- * @param cp If cp is not null, we continue from here
- *           If cp is null, we can add a new continuation point if possible and necessary.
+ * @param cp If cp is not null, we continue from here If cp is null, we can add
+ *           a new continuation point if possible and necessary.
  * @param descr If no cp is set, we take the browsedescription from there
- * @param maxrefs The maximum number of references the client has requested
- * @param result The entry in the request
- */
+ * @param maxrefs The maximum number of references the client has requested. If 0,
+ *                all matching references are returned at once.
+ * @param result The entry in the request */
 void
 Service_Browse_single(UA_Server *server, UA_Session *session,
                       struct ContinuationPointEntry *cp, const UA_BrowseDescription *descr,
@@ -234,17 +171,17 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
     UA_NodeId *relevant_refs = NULL;
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
     if(!all_refs) {
+        const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
+        if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+            result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+            return;
+        }
         if(descr->includeSubtypes) {
-            result->statusCode = findSubTypes(server->nodestore, &descr->referenceTypeId,
-                                              &relevant_refs, &relevant_refs_size);
+            result->statusCode = getTypeHierarchy(server->nodestore, rootRef, false,
+                                                  &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
-            const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
-            if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-                result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-                return;
-            }
             relevant_refs = (UA_NodeId*)(uintptr_t)&descr->referenceTypeId;
             relevant_refs_size = 1;
         }
@@ -271,8 +208,6 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
     size_t real_maxrefs = maxrefs;
     if(real_maxrefs == 0)
         real_maxrefs = node->referencesSize;
-    if(node->referencesSize == 0)
-        real_maxrefs = 0;
     else if(real_maxrefs > node->referencesSize)
         real_maxrefs = node->referencesSize;
     result->references = UA_Array_new(real_maxrefs, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION]);
@@ -303,11 +238,12 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
             referencesCount++;
         }
     }
-
     result->referencesSize = referencesCount;
+
     if(referencesCount == 0) {
         UA_free(result->references);
         result->references = NULL;
+        result->referencesSize = 0;
     }
 
     if(retval != UA_STATUSCODE_GOOD) {
@@ -318,7 +254,7 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
         result->statusCode = retval;
     }
 
-    cleanup:
+ cleanup:
     if(!all_refs && descr->includeSubtypes)
         UA_Array_delete(relevant_refs, relevant_refs_size, &UA_TYPES[UA_TYPES_NODEID]);
     if(result->statusCode != UA_STATUSCODE_GOOD)
@@ -490,7 +426,10 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     } else if(!elem->includeSubtypes) {
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
     } else {
-        retval = findSubTypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &elem->referenceTypeId);
+        if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE)
+            return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        retval = getTypeHierarchy(server->nodestore, rootRef, false, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }

+ 1 - 1
src/ua_util.h

@@ -30,7 +30,7 @@
 #endif
 
 #ifndef NO_ALLOCA
-# ifdef __GNUC__
+# if defined(__GNUC__) || defined(__clang__)
 #  define UA_alloca(size) __builtin_alloca (size)
 # elif defined(_WIN32)
 #  define UA_alloca(SIZE) _alloca(SIZE)

+ 2 - 1
tools/travis/travis_push_doc.sh

@@ -4,7 +4,7 @@ set -ev
 git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-www
 cd open62541-www
 
-git rm -r -f ./doc/current/*
+rm -r -f ./doc/current/* || true # ignore result
 mkdir ./doc/current
 cp -r ../../doc/* ./doc/current/
 git add -A ./doc/current
@@ -13,4 +13,5 @@ git config --global user.name "Open62541 travis-ci"
 git config --global push.default simple
 git commit -am "updated generated documentation on webpage by travis-ci [ci skip]"
 git push https://$GITAUTH@github.com/open62541/open62541-www
+
 cd .. && rm -rf open62541-www