Parcourir la source

Merge branch 'master' into monitoring_mode

ChaCha il y a 6 ans
Parent
commit
ca8cc8ec3a
57 fichiers modifiés avec 1253 ajouts et 1278 suppressions
  1. 5 0
      .travis.yml
  2. 4 4
      examples/client.c
  3. 5 5
      examples/client_connect_loop.c
  4. 6 6
      examples/client_subscription_loop.c
  5. 2 2
      examples/custom_datatype/client_types_custom.c
  6. 12 12
      examples/discovery/client_find_servers.c
  7. 7 7
      examples/discovery/server_multicast.c
  8. 2 2
      examples/discovery/server_register.c
  9. 2 0
      examples/encryption/server_basic128rsa15.c
  10. 3 1
      examples/server.c
  11. 1 1
      examples/server_certificate.c
  12. 1 1
      examples/tutorial_client_events.c
  13. 2 2
      examples/tutorial_client_firststeps.c
  14. 5 0
      include/ua_client.h
  15. 4 1
      include/ua_config.h.in
  16. 8 8
      include/ua_constants.h
  17. 2 1
      plugins/ua_config_default.c
  18. 10 3
      src/client/ua_client.c
  19. 8 8
      src/client/ua_client_connect.c
  20. 1 1
      src/client/ua_client_highlevel.c
  21. 5 5
      src/client/ua_client_highlevel_subscriptions.c
  22. 14 14
      src/server/ua_mdns.c
  23. 13 7
      src/server/ua_nodes.c
  24. 1 1
      src/server/ua_server.c
  25. 3 2
      src/server/ua_server_binary.c
  26. 1 1
      src/server/ua_server_discovery.c
  27. 7 14
      src/server/ua_server_internal.h
  28. 7 7
      src/server/ua_server_ns0.c
  29. 50 6
      src/server/ua_server_utils.c
  30. 1 1
      src/server/ua_server_worker.c
  31. 7 8
      src/server/ua_services_attribute.c
  32. 6 8
      src/server/ua_services_call.c
  33. 23 23
      src/server/ua_services_discovery.c
  34. 8 8
      src/server/ua_services_discovery_multicast.c
  35. 92 107
      src/server/ua_services_nodemanagement.c
  36. 11 0
      src/server/ua_services_session.c
  37. 106 119
      src/server/ua_services_subscription.c
  38. 115 126
      src/server/ua_services_view.c
  39. 10 10
      src/server/ua_session.c
  40. 6 6
      src/server/ua_session.h
  41. 5 1
      src/server/ua_session_manager.c
  42. 17 17
      src/server/ua_subscription.c
  43. 5 5
      src/server/ua_subscription.h
  44. 7 7
      src/server/ua_subscription_datachange.c
  45. 1 1
      src/ua_connection.c
  46. 1 0
      src/ua_securechannel.h
  47. 1 1
      src/ua_types.c
  48. 508 589
      src/ua_types_encoding_binary.c
  49. 5 6
      src/ua_util.h
  50. 4 5
      tests/check_types_builtin.c
  51. 5 0
      tests/fuzz/fuzz_binary_message.cc
  52. 20 21
      tests/server/check_discovery.c
  53. 15 18
      tests/server/check_server_userspace.c
  54. 33 34
      tools/appveyor/build.ps1
  55. 0 2
      tools/appveyor/install.ps1
  56. 12 0
      tools/travis/travis_linux_before_install.sh
  57. 38 33
      tools/travis/travis_linux_script.sh

+ 5 - 0
.travis.yml

@@ -39,6 +39,11 @@ matrix:
       env:
         - ANALYZE=false
         - PYTHON=python3
+    - os: linux
+      compiler: tcc
+      env:
+        - ANALYZE=false
+        - PYTHON=python2
     - os: linux
       compiler: clang
       env: ANALYZE=true

+ 4 - 4
examples/client.c

@@ -37,7 +37,7 @@ int main(int argc, char *argv[]) {
         return (int)retval;
     }
     printf("%i endpoints found\n", (int)endpointArraySize);
-    for(size_t i=0;i<endpointArraySize;i++){
+    for(size_t i=0;i<endpointArraySize;i++) {
         printf("URL of endpoint %i is %.*s\n", (int)i,
                (int)endpointArray[i].endpointUrl.length,
                endpointArray[i].endpointUrl.data);
@@ -63,8 +63,8 @@ int main(int argc, char *argv[]) {
     bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
     UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
     printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
-    for (size_t i = 0; i < bResp.resultsSize; ++i) {
-        for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
+    for(size_t i = 0; i < bResp.resultsSize; ++i) {
+        for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
             UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
             if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
                 printf("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
@@ -102,7 +102,7 @@ int main(int argc, char *argv[]) {
     UA_UInt32 monId = 0;
     UA_Client_Subscriptions_addMonitoredItem(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE,
                                              &handler_TheAnswerChanged, NULL, &monId, 250);
-    if (monId)
+    if(monId)
         printf("Monitoring 'the.answer', id %u\n", subId);
     /* The first publish request should return the initial value of the variable */
     UA_Client_Subscriptions_manuallySendPublishRequest(client);

+ 5 - 5
examples/client_connect_loop.c

@@ -56,12 +56,12 @@ int main(void) {
     UA_Variant_init(&value);
 
     /* Endless loop reading the server time */
-    while (running) {
+    while(running) {
         /* if already connected, this will return GOOD and do nothing */
         /* if the connection is closed/errored, the connection will be reset and then reconnected */
         /* Alternatively you can also use UA_Client_getState to get the current state */
         UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Not connected. Retrying to connect in 1 second");
             /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */
             /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */
@@ -74,12 +74,12 @@ int main(void) {
                 UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
 
         retval = UA_Client_readValueAttribute(client, nodeId, &value);
-        if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_CLIENT, "Connection was closed. Reconnecting ...");
             continue;
         }
-        if (retval == UA_STATUSCODE_GOOD &&
-            UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
+        if(retval == UA_STATUSCODE_GOOD &&
+           UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
             UA_DateTime raw_date = *(UA_DateTime *) value.data;
             UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
             UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "date is: %02u-%02u-%04u %02u:%02u:%02u.%03u",

+ 6 - 6
examples/client_subscription_loop.c

@@ -45,7 +45,7 @@ static void stopHandler(int sign) {
 static void
 handler_currentTimeChanged(UA_UInt32 monId, UA_DataValue *value, void *context) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "currentTime has changed!");
-    if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
+    if(UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
         UA_DateTime raw_date = *(UA_DateTime *) value->value.data;
         UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
         UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "date is: %02u-%02u-%04u %02u:%02u:%02u.%03u",
@@ -54,9 +54,9 @@ handler_currentTimeChanged(UA_UInt32 monId, UA_DataValue *value, void *context)
 }
 
 static void
-stateCallback (UA_Client *client, UA_ClientState clientState){
+stateCallback (UA_Client *client, UA_ClientState clientState) {
 
-    switch (clientState){
+    switch(clientState) {
         case UA_CLIENTSTATE_DISCONNECTED:
             UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "The client is disconnected");
         break; 
@@ -82,7 +82,7 @@ stateCallback (UA_Client *client, UA_ClientState clientState){
             UA_NodeId monitorThis = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
             UA_Client_Subscriptions_addMonitoredItem(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE,
                                                      &handler_currentTimeChanged, NULL, &monId, 250);
-            if (monId)
+            if(monId)
                 UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Monitoring UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME', id %u", subId);
         }
         break; 
@@ -103,12 +103,12 @@ int main(void) {
     UA_Client *client = UA_Client_new(config);
 
     /* Endless loop SendPublishRequest */
-    while (running) {
+    while(running) {
         /* if already connected, this will return GOOD and do nothing */
         /* if the connection is closed/errored, the connection will be reset and then reconnected */
         /* Alternatively you can also use UA_Client_getState to get the current state */
         UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_USERLAND, "Not connected. Retrying to connect in 1 second");
             /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */
             /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */

+ 2 - 2
examples/custom_datatype/client_types_custom.c

@@ -16,7 +16,7 @@ int main(void) {
 
     UA_Client *client = UA_Client_new(config);
     UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         return (int)retval;
     }
@@ -29,7 +29,7 @@ int main(void) {
 
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
             
-    if (retval == UA_STATUSCODE_GOOD) {
+    if(retval == UA_STATUSCODE_GOOD) {
         Point *p = (Point *)value.data;
         printf("Point = %f, %f, %f \n", p->x, p->y, p->z);
     }

+ 12 - 12
examples/discovery/client_find_servers.c

@@ -26,7 +26,7 @@ int main(void) {
         UA_Client *client = UA_Client_new(UA_ClientConfig_default);
         UA_StatusCode retval = UA_Client_findServersOnNetwork(client, DISCOVERY_SERVER_ENDPOINT, 0, 0,
                                                               0, NULL, &serverOnNetworkSize, &serverOnNetwork);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                          "Could not call FindServersOnNetwork service. "
                          "Is the discovery server started? StatusCode %s",
@@ -36,7 +36,7 @@ int main(void) {
         }
 
         // output all the returned/registered servers
-        for (size_t i = 0; i < serverOnNetworkSize; i++) {
+        for(size_t i = 0; i < serverOnNetworkSize; i++) {
             UA_ServerOnNetwork *server = &serverOnNetwork[i];
             printf("Server[%lu]: %.*s", (unsigned long) i,
                    (int) server->serverName.length, server->serverName.data);
@@ -44,7 +44,7 @@ int main(void) {
             printf("\n\tDiscovery URL: %.*s", (int) server->discoveryUrl.length,
                    server->discoveryUrl.data);
             printf("\n\tCapabilities: ");
-            for (size_t j = 0; j < server->serverCapabilitiesSize; j++) {
+            for(size_t j = 0; j < server->serverCapabilitiesSize; j++) {
                 printf("%.*s,", (int) server->serverCapabilities[j].length,
                        server->serverCapabilities[j].data);
             }
@@ -66,14 +66,14 @@ int main(void) {
                                        &applicationDescriptionArraySize, &applicationDescriptionArray);
         UA_Client_delete(client);
     }
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER, "Could not call FindServers service. "
                 "Is the discovery server started? StatusCode %s", UA_StatusCode_name(retval));
         return (int) retval;
     }
 
     // output all the returned/registered servers
-    for (size_t i = 0; i < applicationDescriptionArraySize; i++) {
+    for(size_t i = 0; i < applicationDescriptionArraySize; i++) {
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
         printf("Server[%lu]: %.*s", (unsigned long) i, (int) description->applicationUri.length,
                description->applicationUri.data);
@@ -84,7 +84,7 @@ int main(void) {
         printf("\n\tProduct URI: %.*s", (int) description->productUri.length,
                description->productUri.data);
         printf("\n\tType: ");
-        switch (description->applicationType) {
+        switch(description->applicationType) {
             case UA_APPLICATIONTYPE_SERVER:
                 printf("Server");
                 break;
@@ -101,7 +101,7 @@ int main(void) {
                 printf("Unknown");
         }
         printf("\n\tDiscovery URLs:");
-        for (size_t j = 0; j < description->discoveryUrlsSize; j++) {
+        for(size_t j = 0; j < description->discoveryUrlsSize; j++) {
             printf("\n\t\t[%lu]: %.*s", (unsigned long) j,
                    (int) description->discoveryUrls[j].length,
                    description->discoveryUrls[j].data);
@@ -116,9 +116,9 @@ int main(void) {
 
     printf("-------- Server Endpoints --------\n");
 
-    for (size_t i = 0; i < applicationDescriptionArraySize; i++) {
+    for(size_t i = 0; i < applicationDescriptionArraySize; i++) {
         UA_ApplicationDescription *description = &applicationDescriptionArray[i];
-        if (description->discoveryUrlsSize == 0) {
+        if(description->discoveryUrlsSize == 0) {
             UA_LOG_INFO(logger, UA_LOGCATEGORY_CLIENT,
                         "[GetEndpoints] Server %.*s did not provide any discovery urls. Skipping.",
                         (int)description->applicationUri.length, description->applicationUri.data);
@@ -138,20 +138,20 @@ int main(void) {
         size_t endpointArraySize = 0;
         retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray);
         UA_free(discoveryUrl);
-        if (retval != UA_STATUSCODE_GOOD) {
+        if(retval != UA_STATUSCODE_GOOD) {
             UA_Client_disconnect(client);
             UA_Client_delete(client);
             break;
         }
 
-        for (size_t j = 0; j < endpointArraySize; j++) {
+        for(size_t j = 0; j < endpointArraySize; j++) {
             UA_EndpointDescription *endpoint = &endpointArray[j];
             printf("\n\tEndpoint[%lu]:", (unsigned long) j);
             printf("\n\t\tEndpoint URL: %.*s", (int) endpoint->endpointUrl.length, endpoint->endpointUrl.data);
             printf("\n\t\tTransport profile URI: %.*s", (int) endpoint->transportProfileUri.length,
                    endpoint->transportProfileUri.data);
             printf("\n\t\tSecurity Mode: ");
-            switch (endpoint->securityMode) {
+            switch(endpoint->securityMode) {
             case UA_MESSAGESECURITYMODE_INVALID:
                 printf("Invalid");
                 break;

+ 7 - 7
examples/discovery/server_multicast.c

@@ -64,19 +64,19 @@ static void
 serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce,
                         UA_Boolean isTxtReceived, void *data) {
 
-    if (discovery_url != NULL || !isServerAnnounce) {
+    if(discovery_url != NULL || !isServerAnnounce) {
         UA_LOG_DEBUG(logger, UA_LOGCATEGORY_SERVER,
                      "serverOnNetworkCallback called, but discovery URL "
                      "already initialized or is not announcing. Ignoring.");
         return; // we already have everything we need or we only want server announces
     }
 
-    if (self_discovery_url != NULL && UA_String_equal(&serverOnNetwork->discoveryUrl, self_discovery_url)) {
+    if(self_discovery_url != NULL && UA_String_equal(&serverOnNetwork->discoveryUrl, self_discovery_url)) {
         // skip self
         return;
     }
 
-    if (!isTxtReceived)
+    if(!isTxtReceived)
         return; // we wait until the corresponding TXT record is announced.
                 // Problem: how to handle if a Server does not announce the
                 // optional TXT?
@@ -87,7 +87,7 @@ serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean is
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Another server announced itself on %.*s",
                 (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
 
-    if (discovery_url != NULL)
+    if(discovery_url != NULL)
         UA_free(discovery_url);
     discovery_url = (char*)UA_malloc(serverOnNetwork->discoveryUrl.length + 1);
     memcpy(discovery_url, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length);
@@ -134,7 +134,7 @@ int main(int argc, char **argv) {
 
     // Start the server and call iterate to wait for the multicast discovery of the LDS
     UA_StatusCode retval = UA_Server_run_startup(server);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));
@@ -147,7 +147,7 @@ int main(int argc, char **argv) {
                 "Server started. Waiting for announce of LDS Server.");
     while (running && discovery_url == NULL)
         UA_Server_run_iterate(server, true);
-    if (!running) {
+    if(!running) {
         UA_Server_delete(server);
         UA_ServerConfig_delete(config);
         UA_free(discovery_url);
@@ -158,7 +158,7 @@ int main(int argc, char **argv) {
     // periodic server register after 10 Minutes, delay first register for 500ms
     retval = UA_Server_addPeriodicServerRegisterCallback(server, discovery_url,
                                                          10 * 60 * 1000, 500, NULL);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));

+ 2 - 2
examples/discovery/server_register.c

@@ -98,7 +98,7 @@ int main(int argc, char **argv) {
                                                     10 * 60 * 1000, 500, NULL);
     // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server,
     // "opc.tcp://localhost:4840", 10*60*1000, 500, NULL);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not create periodic job for server register. StatusCode %s",
                      UA_StatusCode_name(retval));
@@ -108,7 +108,7 @@ int main(int argc, char **argv) {
     }
 
     retval = UA_Server_run(server, &running);
-    if (retval != UA_STATUSCODE_GOOD) {
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_ERROR(logger, UA_LOGCATEGORY_SERVER,
                      "Could not start the server. StatusCode %s",
                      UA_StatusCode_name(retval));

+ 2 - 0
examples/encryption/server_basic128rsa15.c

@@ -26,6 +26,8 @@ static UA_ByteString loadFile(const char *const path) {
         size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
         if(read != fileContents.length)
             UA_ByteString_deleteMembers(&fileContents);
+    } else {
+        fileContents.length = 0;
     }
     fclose(fp);
 

+ 3 - 1
examples/server.c

@@ -40,6 +40,8 @@ loadFile(const char *const path) {
         size_t read = fread(fileContents.data, sizeof(UA_Byte), fileContents.length, fp);
         if(read != fileContents.length)
             UA_ByteString_deleteMembers(&fileContents);
+    } else {
+        fileContents.length = 0;
     }
     fclose(fp);
 
@@ -210,7 +212,7 @@ main(int argc, char **argv) {
     UA_VariableAttributes v_attr = UA_VariableAttributes_default;
     v_attr.description = UA_LOCALIZEDTEXT("en-US", "current time");
     v_attr.displayName = UA_LOCALIZEDTEXT("en-US", "current time");
-    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
+    v_attr.accessLevel = UA_ACCESSLEVELMASK_READ;
     v_attr.dataType = UA_TYPES[UA_TYPES_DATETIME].typeId;
     v_attr.valueRank = -1;
     const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");

+ 1 - 1
examples/server_certificate.c

@@ -27,7 +27,7 @@ static UA_ByteString loadCertificate(void) {
     fseek(fp, 0, SEEK_END);
     certificate.length = (size_t)ftell(fp);
     certificate.data = (UA_Byte *)UA_malloc(certificate.length*sizeof(UA_Byte));
-    if(!certificate.data){
+    if(!certificate.data) {
         fclose(fp);
         return UA_STRING_NULL;
     }

+ 1 - 1
examples/tutorial_client_events.c

@@ -22,7 +22,7 @@ handler_events(const UA_UInt32 monId, const size_t nEventFields,
     UA_assert(*(UA_UInt32*)context == monId);
 
     for(size_t i = 0; i < nEventFields; ++i) {
-        if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
+        if(UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
             UA_UInt16 severity = *(UA_UInt16 *)eventFields[i].data;
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Severity: %u", severity);
         } else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {

+ 2 - 2
examples/tutorial_client_firststeps.c

@@ -28,8 +28,8 @@ int main(void) {
     const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
     retval = UA_Client_readValueAttribute(client, nodeId, &value);
 
-    if (retval == UA_STATUSCODE_GOOD &&
-        UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
+    if(retval == UA_STATUSCODE_GOOD &&
+       UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
         UA_DateTime raw_date = *(UA_DateTime *) value.data;
         UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
         UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03u\n",

+ 5 - 0
include/ua_client.h

@@ -70,6 +70,7 @@ typedef struct UA_ClientConfig {
 
     /* Callback function */
     UA_ClientStateCallback stateCallback;
+    void *clientContext;
 } UA_ClientConfig;
 
 
@@ -81,6 +82,10 @@ UA_Client_new(UA_ClientConfig config);
 UA_ClientState UA_EXPORT
 UA_Client_getState(UA_Client *client);
 
+/* Get the client context */
+void UA_EXPORT *
+UA_Client_getContext(UA_Client *client);
+
 /* Reset a client */
 void UA_EXPORT
 UA_Client_reset(UA_Client *client);

+ 4 - 1
include/ua_config.h.in

@@ -254,7 +254,10 @@ extern "C" {
  * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
  * point number representation of the target architecture is IEEE 754. Note that
  * this cannot be reliable detected with macros for the clang compiler
- * (beginning of 2017). Just override if necessary. */
+ * (beginning of 2017). ``UA_BINARY_OVERLAYABLE_FLOAT`` can be manually set if
+ * the target is known to be little endian with floats in the IEEE 754
+ * format. */
+
 #if defined(_WIN32)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \

+ 8 - 8
include/ua_constants.h

@@ -156,7 +156,7 @@ typedef enum {
 #define UA_STATUSCODE_GOODCOMPLETESASYNCHRONOUSLY 0x002e0000 // The processing will complete asynchronously.
 #define UA_STATUSCODE_GOODOVERLOAD 0x002f0000 // Sampling has slowed down due to resource limitations.
 #define UA_STATUSCODE_GOODCLAMPED 0x00300000 // The value written was accepted but was clamped.
-#define UA_STATUSCODE_BADNOCOMMUNICATION 0x80310000 // Communication with the data source is defined
+#define UA_STATUSCODE_BADNOCOMMUNICATION 0x80310000 // Communication with the data source is defined, but not established, and there is no last known value available.
 #define UA_STATUSCODE_BADWAITINGFORINITIALDATA 0x80320000 // Waiting for the server to obtain values from the underlying data source.
 #define UA_STATUSCODE_BADNODEIDINVALID 0x80330000 // The syntax of the node id is not valid.
 #define UA_STATUSCODE_BADNODEIDUNKNOWN 0x80340000 // The node id refers to a node that does not exist in the server address space.
@@ -181,7 +181,7 @@ typedef enum {
 #define UA_STATUSCODE_BADEVENTFILTERINVALID 0x80470000 // The event filter is not valid.
 #define UA_STATUSCODE_BADCONTENTFILTERINVALID 0x80480000 // The content filter is not valid.
 #define UA_STATUSCODE_BADFILTEROPERATORINVALID 0x80c10000 // An unregognized operator was provided in a filter.
-#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided
+#define UA_STATUSCODE_BADFILTEROPERATORUNSUPPORTED 0x80c20000 // A valid operator was provided, but the server does not provide support for this filter operator.
 #define UA_STATUSCODE_BADFILTEROPERANDCOUNTMISMATCH 0x80c30000 // The number of operands provided for the filter operator was less then expected for the operand provided.
 #define UA_STATUSCODE_BADFILTEROPERANDINVALID 0x80490000 // The operand used in a content filter is not valid.
 #define UA_STATUSCODE_BADFILTERELEMENTINVALID 0x80c40000 // The referenced element is not a valid element in the content filter.
@@ -237,7 +237,7 @@ typedef enum {
 #define UA_STATUSCODE_BADHISTORYOPERATIONINVALID 0x80710000 // The history details parameter is not valid.
 #define UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED 0x80720000 // The server does not support the requested operation.
 #define UA_STATUSCODE_BADINVALIDTIMESTAMPARGUMENT 0x80bd0000 // The defined timestamp to return was invalid.
-#define UA_STATUSCODE_BADWRITENOTSUPPORTED 0x80730000 // The server not does support writing the combination of value
+#define UA_STATUSCODE_BADWRITENOTSUPPORTED 0x80730000 // The server not does support writing the combination of value, status and timestamps provided.
 #define UA_STATUSCODE_BADTYPEMISMATCH 0x80740000 // The value supplied for the attribute is not of the same type as the attribute's value.
 #define UA_STATUSCODE_BADMETHODINVALID 0x80750000 // The method id does not refer to a method for the specified object.
 #define UA_STATUSCODE_BADARGUMENTSMISSING 0x80760000 // The client did not specify all of the input arguments for the method.
@@ -262,7 +262,7 @@ typedef enum {
 #define UA_STATUSCODE_BADSEQUENCENUMBERINVALID 0x80880000 // The sequence number is not valid.
 #define UA_STATUSCODE_BADPROTOCOLVERSIONUNSUPPORTED 0x80be0000 // The applications do not have compatible protocol versions.
 #define UA_STATUSCODE_BADCONFIGURATIONERROR 0x80890000 // There is a problem with the configuration that affects the usefulness of the value.
-#define UA_STATUSCODE_BADNOTCONNECTED 0x808a0000 // The variable should receive its value from another variable
+#define UA_STATUSCODE_BADNOTCONNECTED 0x808a0000 // The variable should receive its value from another variable, but has never been configured to do so.
 #define UA_STATUSCODE_BADDEVICEFAILURE 0x808b0000 // There has been a failure in the device/data source that generates the value that has affected the value.
 #define UA_STATUSCODE_BADSENSORFAILURE 0x808c0000 // There has been a failure in the sensor from which the value is derived by the device/data source.
 #define UA_STATUSCODE_BADOUTOFSERVICE 0x808d0000 // The source of the data is not operational.
@@ -275,10 +275,10 @@ typedef enum {
 #define UA_STATUSCODE_UNCERTAINENGINEERINGUNITSEXCEEDED 0x40940000 // The value is outside of the range of values defined for this parameter.
 #define UA_STATUSCODE_UNCERTAINSUBNORMAL 0x40950000 // The value is derived from multiple sources and has less than the required number of Good sources.
 #define UA_STATUSCODE_GOODLOCALOVERRIDE 0x00960000 // The value has been overridden.
-#define UA_STATUSCODE_BADREFRESHINPROGRESS 0x80970000 // This Condition refresh failed
+#define UA_STATUSCODE_BADREFRESHINPROGRESS 0x80970000 // This Condition refresh failed, a Condition refresh operation is already in progress.
 #define UA_STATUSCODE_BADCONDITIONALREADYDISABLED 0x80980000 // This condition has already been disabled.
 #define UA_STATUSCODE_BADCONDITIONALREADYENABLED 0x80cc0000 // This condition has already been enabled.
-#define UA_STATUSCODE_BADCONDITIONDISABLED 0x80990000 // Property not available
+#define UA_STATUSCODE_BADCONDITIONDISABLED 0x80990000 // Property not available, this condition is disabled.
 #define UA_STATUSCODE_BADEVENTIDUNKNOWN 0x809a0000 // The specified event id is not recognized.
 #define UA_STATUSCODE_BADEVENTNOTACKNOWLEDGEABLE 0x80bb0000 // The event cannot be acknowledged.
 #define UA_STATUSCODE_BADDIALOGNOTACTIVE 0x80cd0000 // The dialog condition is not active.
@@ -292,7 +292,7 @@ typedef enum {
 #define UA_STATUSCODE_BADBOUNDNOTFOUND 0x80d70000 // No data found to provide upper or lower bound value.
 #define UA_STATUSCODE_BADBOUNDNOTSUPPORTED 0x80d80000 // The server cannot retrieve a bound for the variable.
 #define UA_STATUSCODE_BADDATALOST 0x809d0000 // Data is missing due to collection started/stopped/lost.
-#define UA_STATUSCODE_BADDATAUNAVAILABLE 0x809e0000 // Expected data is unavailable for the requested time range due to an un-mounted volume
+#define UA_STATUSCODE_BADDATAUNAVAILABLE 0x809e0000 // Expected data is unavailable for the requested time range due to an un-mounted volume, an off-line archive or tape, or similar reason for temporary unavailability.
 #define UA_STATUSCODE_BADENTRYEXISTS 0x809f0000 // The data or event was not successfully inserted because a matching entry exists.
 #define UA_STATUSCODE_BADNOENTRYEXISTS 0x80a00000 // The data or event was not successfully updated because no matching entry exists.
 #define UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED 0x80a10000 // The client requested history using a timestamp format the server does not support (i.e requested ServerTimestamp when server only supports SourceTimestamp).
@@ -322,7 +322,7 @@ typedef enum {
 #define UA_STATUSCODE_BADCONNECTIONREJECTED 0x80ac0000 // Could not establish a network connection to remote server.
 #define UA_STATUSCODE_BADDISCONNECT 0x80ad0000 // The server has disconnected from the client.
 #define UA_STATUSCODE_BADCONNECTIONCLOSED 0x80ae0000 // The network connection has been closed.
-#define UA_STATUSCODE_BADINVALIDSTATE 0x80af0000 // The operation cannot be completed because the object is closed
+#define UA_STATUSCODE_BADINVALIDSTATE 0x80af0000 // The operation cannot be completed because the object is closed, uninitialized or in some other invalid state.
 #define UA_STATUSCODE_BADENDOFSTREAM 0x80b00000 // Cannot move beyond end of the stream.
 #define UA_STATUSCODE_BADNODATAAVAILABLE 0x80b10000 // No data is currently available for reading from a non-blocking stream.
 #define UA_STATUSCODE_BADWAITINGFORRESPONSE 0x80b20000 // The asynchronous operation is waiting for a response.

+ 2 - 1
plugins/ua_config_default.c

@@ -458,7 +458,8 @@ const UA_ClientConfig UA_ClientConfig_default = {
 
     0, /* .customDataTypesSize */
     NULL, /*.customDataTypes */
-    NULL /*.stateCallback */
+    NULL, /*.stateCallback */
+    NULL  /*.clientContext */
 };
 
 /****************************************/

+ 10 - 3
src/client/ua_client.c

@@ -22,7 +22,7 @@ UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     client->channel.securityPolicy = &client->securityPolicy;
     client->channel.securityMode = UA_MESSAGESECURITYMODE_NONE;
     client->config = config;
-    if (client->config.stateCallback)
+    if(client->config.stateCallback)
         client->config.stateCallback(client, client->state);
 }
 
@@ -80,6 +80,13 @@ UA_Client_getState(UA_Client *client) {
     return client->state;
 }
 
+void *
+UA_Client_getContext(UA_Client *client) {
+    if(!client)
+        return NULL;
+    return client->config.clientContext;
+}
+
 /****************/
 /* Raw Services */
 /****************/
@@ -278,7 +285,7 @@ receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *res
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
 
         /* round always to upper value to avoid timeout to be set to 0
-         * if (maxDate - now) < (UA_DATETIME_MSEC/2) */
+         * if(maxDate - now) < (UA_DATETIME_MSEC/2) */
         UA_UInt32 timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
 
         retval = UA_Connection_receiveChunksBlocking(&client->connection, &rd, client_processChunk, timeout);
@@ -316,7 +323,7 @@ __UA_Client_Service(UA_Client *client, const void *request,
     UA_DateTime maxDate = UA_DateTime_nowMonotonic() +
         (client->config.timeout * UA_DATETIME_MSEC);
     retval = receiveServiceResponse(client, response, responseType, maxDate, &requestId);
-    if (retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT){
+    if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
         /* In synchronous service, if we have don't have a reply we need to close the connection */
         UA_Client_close(client);
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;

+ 8 - 8
src/client/ua_client_connect.c

@@ -19,9 +19,9 @@
 static void
 setClientState(UA_Client *client, UA_ClientState state)
 {
-    if (client->state != state){
+    if(client->state != state) {
         client->state = state;
-        if (client->config.stateCallback)
+        if(client->config.stateCallback)
             client->config.stateCallback(client, client->state);
     }
 }
@@ -110,7 +110,7 @@ HelAckHandshake(UA_Client *client) {
     /* Loop until we have a complete chunk */
     retval = UA_Connection_receiveChunksBlocking(conn, client, processACKResponse,
                                                  client->config.timeout);
-    if(retval != UA_STATUSCODE_GOOD){
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
                     "Receiving ACK message failed");
         if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
@@ -418,13 +418,13 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
         if(retval == UA_STATUSCODE_BADSESSIONIDINVALID) {
             /* Could not recover an old session. Remove authenticationToken */
             UA_NodeId_deleteMembers(&client->authenticationToken);
-        }else{
+        } else {
             if(retval != UA_STATUSCODE_GOOD)
                 goto cleanup;
             setClientState(client, UA_CLIENTSTATE_SESSION_RENEWED);
             return retval;
         }
-    }else{
+    } else {
         UA_NodeId_deleteMembers(&client->authenticationToken);
     }
 
@@ -518,7 +518,7 @@ sendCloseSecureChannel(UA_Client *client) {
 UA_StatusCode
 UA_Client_disconnect(UA_Client *client) {
     /* Is a session established? */
-    if(client->state >= UA_CLIENTSTATE_SESSION){
+    if(client->state >= UA_CLIENTSTATE_SESSION) {
         client->state = UA_CLIENTSTATE_SECURECHANNEL;
         sendCloseSession(client);
     }
@@ -526,7 +526,7 @@ UA_Client_disconnect(UA_Client *client) {
     client->requestHandle = 0;
 
     /* Is a secure channel established? */
-    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL){
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL) {
         client->state = UA_CLIENTSTATE_CONNECTED;
         sendCloseSecureChannel(client);
     }
@@ -543,7 +543,7 @@ UA_StatusCode
 UA_Client_close(UA_Client *client) {
     client->requestHandle = 0;
 
-    if (client->state >= UA_CLIENTSTATE_SECURECHANNEL)
+    if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
         UA_SecureChannel_deleteMembersCleanup(&client->channel);
 
     /* Close the TCP connection */

+ 1 - 1
src/client/ua_client_highlevel.c

@@ -35,7 +35,7 @@ UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
 
     retval = UA_STATUSCODE_BADNOTFOUND;
     UA_String *ns = (UA_String *)response.results[0].value.data;
-    for(size_t i = 0; i < response.results[0].value.arrayLength; ++i){
+    for(size_t i = 0; i < response.results[0].value.arrayLength; ++i) {
         if(UA_String_equal(namespaceUri, &ns[i])) {
             *namespaceIndex = (UA_UInt16)i;
             retval = UA_STATUSCODE_GOOD;

+ 5 - 5
src/client/ua_client_highlevel_subscriptions.c

@@ -320,7 +320,7 @@ UA_Client_Subscriptions_removeMonitoredItems(UA_Client *client, UA_UInt32 subscr
         }
         UA_Client_MonitoredItem *mon;
         LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-            if(mon->monitoredItemId == monitoredItemId[i]){
+            if(mon->monitoredItemId == monitoredItemId[i]) {
                 LIST_REMOVE(mon, listEntry);
                 UA_NodeId_deleteMembers(&mon->monitoredNodeId);
                 UA_free(mon);
@@ -424,7 +424,7 @@ UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request,
         if(msg->notificationData[k].content.decoded.type == &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST]) {
             UA_EventNotificationList *eventNotificationList =
                 (UA_EventNotificationList *)msg->notificationData[k].content.decoded.data;
-            for (size_t j = 0; j < eventNotificationList->eventsSize; ++j) {
+            for(size_t j = 0; j < eventNotificationList->eventsSize; ++j) {
                 UA_EventFieldList *eventFieldList = &eventNotificationList->events[j];
 
                 /* Find the MonitoredItem */
@@ -505,10 +505,10 @@ UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
         UA_Client_processPublishResponse(client, &request, &response);
         
         now = UA_DateTime_nowMonotonic();
-        if (now > maxDate){
+        if(now > maxDate) {
             moreNotifications = UA_FALSE;
             retval = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
-        }else{
+        } else {
             moreNotifications = response.moreNotifications;
         }
         
@@ -523,7 +523,7 @@ UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *client) {
 }
 
 void
-UA_Client_Subscriptions_clean(UA_Client *client){
+UA_Client_Subscriptions_clean(UA_Client *client) {
     UA_Client_NotificationsAckNumber *n, *tmp;
     LIST_FOREACH_SAFE(n, &client->pendingNotificationsAcks, listEntry, tmp) {
         LIST_REMOVE(n, listEntry);

+ 14 - 14
src/server/ua_mdns.c

@@ -78,14 +78,14 @@ mdns_record_add_or_get(UA_Server *server, const char *record, const char *server
     int hashIdx = mdns_hash_record(record) % SERVER_ON_NETWORK_HASH_PRIME;
     struct serverOnNetwork_hash_entry *hash_entry = server->serverOnNetworkHash[hashIdx];
 
-    while (hash_entry) {
+    while(hash_entry) {
         size_t maxLen;
-        if (serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
+        if(serverNameLen > hash_entry->entry->serverOnNetwork.serverName.length)
             maxLen = hash_entry->entry->serverOnNetwork.serverName.length;
         else
             maxLen = serverNameLen;
 
-        if (strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data, serverName, maxLen) == 0)
+        if(strncmp((char *) hash_entry->entry->serverOnNetwork.serverName.data, serverName, maxLen) == 0)
             return hash_entry->entry;
         hash_entry = hash_entry->next;
     }
@@ -108,7 +108,7 @@ mdns_record_add_or_get(UA_Server *server, const char *record, const char *server
     listEntry->serverOnNetwork.serverName.data = (UA_Byte*)UA_malloc(serverNameLen);
     memcpy(listEntry->serverOnNetwork.serverName.data, serverName, serverNameLen);
     server->serverOnNetworkRecordIdCounter = UA_atomic_add(&server->serverOnNetworkRecordIdCounter, 1);
-    if (server->serverOnNetworkRecordIdCounter == 0)
+    if(server->serverOnNetworkRecordIdCounter == 0)
         server->serverOnNetworkRecordIdLastReset = UA_DateTime_now();
 
     // add to hash
@@ -190,7 +190,7 @@ setTxt(const struct resource *r,
     char *caps = (char *) xht_get(x, "caps");
 
     if(path && strlen(path) > 1) {
-        if (!entry->srvSet) {
+        if(!entry->srvSet) {
             /* txt arrived before SRV, thus cache path entry */
             // todo: malloc in strdup may fail: return a statuscode
             entry->pathTmp = UA_STRDUP(path);
@@ -220,7 +220,7 @@ setTxt(const struct resource *r,
             // todo: malloc may fail: return a statuscode
             entry->serverOnNetwork.serverCapabilities[i].data = (UA_Byte*)UA_malloc(len);
             memcpy(entry->serverOnNetwork.serverCapabilities[i].data, caps, len);
-            if (nextStr)
+            if(nextStr)
                 caps = nextStr + 1;
             else
                 break;
@@ -324,11 +324,11 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
                                     600, conflict, server);
     xht_t *h = xht_new(11);
     char *allocPath = NULL;
-    if (!path || strlen(path) == 0) {
+    if(!path || strlen(path) == 0) {
         xht_set(h, "path", "/");
     } else {
         // path does not contain slash, so add it here
-        if (path[0] == '/')
+        if(path[0] == '/')
             // todo: malloc in strdup may fail: return a statuscode
             allocPath = UA_STRDUP(path);
         else {
@@ -343,7 +343,7 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
 
     // calculate max string length:
     size_t capsLen = 0;
-    for (size_t i = 0; i < *capabilitiesSize; i++) {
+    for(size_t i = 0; i < *capabilitiesSize; i++) {
         // add comma or last \0
         capsLen += capabilites[i].length + 1;
     }
@@ -354,7 +354,7 @@ void mdns_create_txt(UA_Server *server, const char *fullServiceDomain, const cha
         // todo: malloc may fail: return a statuscode
         caps = (char*)UA_malloc(sizeof(char) * capsLen);
         size_t idx = 0;
-        for (size_t i = 0; i < *capabilitiesSize; i++) {
+        for(size_t i = 0; i < *capabilitiesSize; i++) {
             memcpy(caps + idx, (const char *) capabilites[i].data, capabilites[i].length);
             idx += capabilites[i].length + 1;
             caps[idx - 1] = ',';
@@ -421,7 +421,7 @@ getInterfaces(UA_Server *server) {
     for(size_t attempts = 0; attempts != 3; ++attempts) {
         // todo: malloc may fail: return a statuscode
         adapter_addresses = (IP_ADAPTER_ADDRESSES*)UA_malloc(adapter_addresses_buffer_size);
-        if (!adapter_addresses) {
+        if(!adapter_addresses) {
             UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                          "GetAdaptersAddresses out of memory");
             adapter_addresses = NULL;
@@ -457,7 +457,7 @@ getInterfaces(UA_Server *server) {
 void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
                              const char *localDomain) {
     IP_ADAPTER_ADDRESSES* adapter_addresses = getInterfaces(server);
-    if (!adapter_addresses)
+    if(!adapter_addresses)
         return;
 
     /* Iterate through all of the adapters */
@@ -476,7 +476,7 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
                 mdns_set_address_record_if(server, fullServiceDomain, localDomain,
                                            (char *)&ipv4->sin_addr, 4);
             }
-            /*else if (AF_INET6 == family) {
+            /*else if(AF_INET6 == family) {
             // IPv6
             SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*)(address->Address.lpSockaddr);
 
@@ -491,7 +491,7 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
 
             if(0 == ipv6_str.find("fe")) {
             char c = ipv6_str[2];
-            if (c == '8' || c == '9' || c == 'a' || c == 'b')
+            if(c == '8' || c == '9' || c == 'a' || c == 'b')
             is_link_local = true;
             } else if (0 == ipv6_str.find("2001:0:")) {
             is_special_use = true;

+ 13 - 7
src/server/ua_nodes.c

@@ -254,7 +254,7 @@ UA_Node_copy_alloc(const UA_Node *src) {
     dst->nodeClass = src->nodeClass;
 
     UA_StatusCode retval = UA_Node_copy(src, dst);
-    if (retval != UA_STATUSCODE_GOOD){
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_free(dst);
         return NULL;
     }
@@ -296,24 +296,30 @@ copyCommonVariableAttributes(UA_VariableNode *node,
     /* if we have an extension object which is still encoded (e.g. from the nodeset compiler)
      * we need to decode it and set the decoded value instead of the encoded object */
     UA_Boolean valueSet = false;
-    if (attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
+    if(attr->value.type != NULL && UA_NodeId_equal(&attr->value.type->typeId, &extensionObject)) {
+
+        if (attr->value.data == UA_EMPTY_ARRAY_SENTINEL) {
+            /* do nothing since we got an empty array of extension objects */
+            return UA_STATUSCODE_GOOD;
+        }
+
         const UA_ExtensionObject *obj = (const UA_ExtensionObject *)attr->value.data;
-        if (obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
+        if(obj && obj->encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
 
             /* TODO: Once we generate type description in the nodeset compiler,
              * UA_findDatatypeByBinary can be made internal to the decoding
              * layer. */
             const UA_DataType *type = UA_findDataTypeByBinary(&obj->content.encoded.typeId);
 
-            if (type) {
+            if(type) {
                 void *dst = UA_Array_new(attr->value.arrayLength, type);
                 uint8_t *tmpPos = (uint8_t *)dst;
 
-                for (size_t i=0; i<attr->value.arrayLength; i++) {
+                for(size_t i=0; i<attr->value.arrayLength; i++) {
                     size_t offset =0;
                     const UA_ExtensionObject *curr = &((const UA_ExtensionObject *)attr->value.data)[i];
                     UA_StatusCode ret = UA_decodeBinary(&curr->content.encoded.body, &offset, tmpPos, type, 0, NULL);
-                    if (ret != UA_STATUSCODE_GOOD) {
+                    if(ret != UA_STATUSCODE_GOOD) {
                         return ret;
                     }
                     tmpPos += type->memSize;
@@ -325,7 +331,7 @@ copyCommonVariableAttributes(UA_VariableNode *node,
         }
     }
 
-    if (!valueSet)
+    if(!valueSet)
         retval |= UA_Variant_copy(&attr->value, &node->value.data.value.value);
 
     node->value.data.value.hasValue = true;

+ 1 - 1
src/server/ua_server.c

@@ -70,7 +70,7 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = parentCopy->referencesSize; i > 0; --i) {
         UA_NodeReferenceKind *ref = &parentCopy->references[i - 1];
-        for (size_t j = 0; j<ref->targetIdsSize; j++)
+        for(size_t j = 0; j<ref->targetIdsSize; j++)
             retval |= callback(ref->targetIds[j].nodeId, ref->isInverse,
                                ref->referenceTypeId, handle);
     }

+ 3 - 2
src/server/ua_server_binary.c

@@ -380,7 +380,8 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
     UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
+    if(requestTypeId.namespaceIndex != 0 ||
+       requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC)
         UA_NodeId_deleteMembers(&requestTypeId); /* leads to badserviceunsupported */
 
     /* Store the start-position of the request */
@@ -442,7 +443,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
 
     #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     // set the authenticationToken from the create session request to help fuzzing cover more lines
-    if (!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken))
+    if(!UA_NodeId_isNull(&unsafe_fuzz_authenticationToken))
         UA_NodeId_copy(&unsafe_fuzz_authenticationToken, &requestHeader->authenticationToken);
     #endif
 

+ 1 - 1
src/server/ua_server_discovery.c

@@ -67,7 +67,7 @@ register_server_with_discovery_server(UA_Server *server,
     size_t nl_discurls = server->config.networkLayersSize;
     size_t total_discurls = config_discurls + nl_discurls;
     request.server.discoveryUrls = (UA_String*)UA_alloca(sizeof(UA_String) * total_discurls);
-    if (!request.server.discoveryUrls) {
+    if(!request.server.discoveryUrls) {
         UA_Client_disconnect(client);
         UA_Client_delete(client);
         return UA_STATUSCODE_BADOUTOFMEMORY;

+ 7 - 14
src/server/ua_server_internal.h

@@ -218,12 +218,14 @@ const UA_Node * getNodeType(UA_Server *server, const UA_Node *node);
 /* Many services come as an array of operations. This function generalizes the
  * processing of the operations. */
 typedef void (*UA_ServiceOperation)(UA_Server *server, UA_Session *session,
+                                    void *context,
                                     const void *requestOperation,
                                     void *responseOperation);
 
 UA_StatusCode
 UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
                                    UA_ServiceOperation operationCallback,
+                                   void *context,
                                    const size_t *requestOperations,
                                    const UA_DataType *requestOperationsType,
                                    size_t *responseOperations,
@@ -270,17 +272,9 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
 UA_Boolean
 compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
 
-/*******************/
-/* Single-Services */
-/*******************/
-
-/* Some services take an array of "independent" requests. The single-services
- * are stored here to keep ua_services.h clean for documentation purposes. */
-
-void Service_Browse_single(UA_Server *server, UA_Session *session,
-                           struct ContinuationPointEntry *cp,
-                           const UA_BrowseDescription *descr,
-                           UA_UInt32 maxrefs, UA_BrowseResult *result);
+void
+Operation_Browse(UA_Server *server, UA_Session *session, UA_UInt32 *maxrefs,
+                 const UA_BrowseDescription *descr, UA_BrowseResult *result);
 
 UA_DataValue
 UA_Server_readWithSession(UA_Server *server, UA_Session *session,
@@ -334,9 +328,8 @@ UA_Discovery_removeRecord(UA_Server *server, const UA_String *servername,
 
 /* Creates a new node in the nodestore. */
 UA_StatusCode
-Operation_addNode_begin(UA_Server *server, UA_Session *session,
-                        const UA_AddNodesItem *item, void *nodeContext,
-                        UA_NodeId *outNewNodeId);
+Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
+                        const UA_AddNodesItem *item, UA_NodeId *outNewNodeId);
 
 /* Children, references, type-checking, constructors. */
 UA_StatusCode

+ 7 - 7
src/server/ua_server_ns0.c

@@ -23,7 +23,7 @@ addNode_begin(UA_Server *server, UA_NodeClass nodeClass,
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.data = attributes;
     item.nodeAttributes.content.decoded.type = attributesType;
-    return Operation_addNode_begin(server, &adminSession, &item, NULL, NULL);
+    return Operation_addNode_begin(server, &adminSession, NULL, &item, NULL);
 }
 
 static UA_StatusCode
@@ -35,7 +35,7 @@ addNode_finish(UA_Server *server, UA_UInt32 nodeId,
     UA_NodeId referenceType = UA_NODEID_NUMERIC(0, referenceTypeId);
     UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, typeDefinitionId);
     return Operation_addNode_finish(server, &adminSession, &node, &parentNode,
-                             &referenceType, &typeDefinition);
+                                    &referenceType, &typeDefinition);
 }
 
 static UA_StatusCode
@@ -299,7 +299,7 @@ UA_Server_createNS0_base(UA_Server *server) {
     ret |= addObjectNode(server, "Views", UA_NS0ID_VIEWSFOLDER, UA_NS0ID_ROOTFOLDER,
                   UA_NS0ID_ORGANIZES, UA_NS0ID_FOLDERTYPE);
 
-    if (ret != UA_STATUSCODE_GOOD)
+    if(ret != UA_STATUSCODE_GOOD)
         return UA_STATUSCODE_BADINTERNALERROR;
     return UA_STATUSCODE_GOOD;
 }
@@ -480,7 +480,7 @@ readMonitoredItems(UA_Server *server, const UA_NodeId *sessionId, void *sessionC
     if(!session)
         return UA_STATUSCODE_BADINTERNALERROR;
     UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
-    UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
+    UA_Subscription* subscription = UA_Session_getSubscriptionById(session, subscriptionId);
     if(!subscription)
         return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
 
@@ -534,14 +534,14 @@ UA_Server_initNS0(UA_Server *server) {
     server->bootstrapNS0 = true;
     UA_StatusCode retVal = UA_Server_createNS0_base(server);
     server->bootstrapNS0 = false;
-    if (retVal != UA_STATUSCODE_GOOD)
+    if(retVal != UA_STATUSCODE_GOOD)
         return retVal;
 
     /* Load nodes and references generated from the XML ns0 definition */
     server->bootstrapNS0 = true;
     retVal = ua_namespace0(server);
     server->bootstrapNS0 = false;
-    if (retVal != UA_STATUSCODE_GOOD)
+    if(retVal != UA_STATUSCODE_GOOD)
         return retVal;
 
     /* NamespaceArray */
@@ -582,7 +582,7 @@ UA_Server_initNS0(UA_Server *server) {
 
     retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY,
                                     profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
-    for (int i=0; i<profileArraySize; i++) {
+    for(int i=0; i<profileArraySize; i++) {
         UA_String_deleteMembers(&profileArray[i]);
     }
 

+ 50 - 6
src/server/ua_server_utils.c

@@ -4,6 +4,8 @@
 
 #include "ua_server_internal.h"
 
+#define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */
+
 /**********************/
 /* Parse NumericRange */
 /**********************/
@@ -85,12 +87,23 @@ UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
 /* Information Model Operations */
 /********************************/
 
-UA_Boolean
-isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
-             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
+/* Keeps track of already visited nodes to detect circular references */
+struct ref_history {
+    struct ref_history *parent; /* the previous element */
+    const UA_NodeId *id; /* the id of the node at this depth */
+    UA_UInt16 depth;
+};
+
+static UA_Boolean
+isNodeInTreeNoCircular(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
+                       struct ref_history *visitedRefs, const UA_NodeId *referenceTypeIds,
+                       size_t referenceTypeIdsSize) {
     if(UA_NodeId_equal(nodeToFind, leafNode))
         return true;
 
+    if(visitedRefs->depth >= UA_MAX_TREE_RECURSE)
+        return false;
+
     const UA_Node *node = ns->getNode(ns->context, leafNode);
     if(!node)
         return false;
@@ -114,8 +127,31 @@ isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeT
 
         /* Match the targets or recurse */
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
-            if(isNodeInTree(ns, &refs->targetIds[j].nodeId, nodeToFind,
-                            referenceTypeIds, referenceTypeIdsSize)) {
+            /* Check if we already have seen the referenced node and skip to
+             * avoid endless recursion. Do this only at every 5th depth to save
+             * effort. Circular dependencies are rare and forbidden for most
+             * reference types. */
+            if(visitedRefs->depth % 5 == 4) {
+                struct ref_history *last = visitedRefs;
+                UA_Boolean skip = UA_FALSE;
+                while(!skip && last) {
+                    if(UA_NodeId_equal(last->id, &refs->targetIds[j].nodeId))
+                        skip = UA_TRUE;
+                    last = last->parent;
+                }
+                if(skip)
+                    continue;
+            }
+
+            /* Stack-allocate the visitedRefs structure for the next depth */
+            struct ref_history nextVisitedRefs = {visitedRefs, &refs->targetIds[j].nodeId,
+                                                  (UA_UInt16)(visitedRefs->depth+1)};
+
+            /* Recurse */
+            UA_Boolean foundRecursive =
+                isNodeInTreeNoCircular(ns, &refs->targetIds[j].nodeId, nodeToFind, &nextVisitedRefs,
+                                       referenceTypeIds, referenceTypeIdsSize);
+            if(foundRecursive) {
                 ns->releaseNode(ns->context, node);
                 return true;
             }
@@ -126,6 +162,13 @@ isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeT
     return false;
 }
 
+UA_Boolean
+isNodeInTree(UA_Nodestore *ns, const UA_NodeId *leafNode, const UA_NodeId *nodeToFind,
+             const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize) {
+    struct ref_history visitedRefs = {NULL, leafNode, 0};
+    return isNodeInTreeNoCircular(ns, leafNode, nodeToFind, &visitedRefs, referenceTypeIds, referenceTypeIdsSize);
+}
+
 const UA_Node *
 getNodeType(UA_Server *server, const UA_Node *node) {
     /* The reference to the parent is different for variable and variabletype */
@@ -333,6 +376,7 @@ UA_Server_editNode(UA_Server *server, UA_Session *session,
 UA_StatusCode
 UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
                                    UA_ServiceOperation operationCallback,
+                                   void *context,
                                    const size_t *requestOperations,
                                    const UA_DataType *requestOperationsType,
                                    size_t *responseOperations,
@@ -352,7 +396,7 @@ UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
     /* No padding after size_t */
     uintptr_t reqOp = *(uintptr_t*)((uintptr_t)requestOperations + sizeof(size_t));
     for(size_t i = 0; i < ops; i++) {
-        operationCallback(server, session, (void*)reqOp, (void*)respOp);
+        operationCallback(server, session, context, (void*)reqOp, (void*)respOp);
         reqOp += requestOperationsType->memSize;
         respOp += responseOperationsType->memSize;
     }

+ 1 - 1
src/server/ua_server_worker.c

@@ -328,7 +328,7 @@ UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
     UA_UInt16 timeout = 0;
 
     /* round always to upper value to avoid timeout to be set to 0
-    * if (nextRepeated - now) < (UA_DATETIME_MSEC/2) */
+    * if(nextRepeated - now) < (UA_DATETIME_MSEC/2) */
     if(waitInternal)
         timeout = (UA_UInt16)(((nextRepeated - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
 

+ 7 - 8
src/server/ua_services_attribute.c

@@ -918,7 +918,7 @@ writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value,
     }
 
     /* Check that the type is an exact match and not only "compatible" */
-    if(!node->value.data.value.value.type ||
+    if(!node->value.data.value.value.type || !v->type ||
        !UA_NodeId_equal(&node->value.data.value.value.type->typeId,
                         &v->type->typeId))
         return UA_STATUSCODE_BADTYPEMISMATCH;
@@ -970,8 +970,8 @@ writeValueAttribute(UA_Server *server, UA_Session *session,
          * extension object we check if the current node value is also an
          * extension object. */
         UA_Boolean compatible;
-        if (value->value.type->typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
-            value->value.type->typeId.identifier.numeric == UA_NS0ID_STRUCTURE) {
+        if(value->value.type->typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+           value->value.type->typeId.identifier.numeric == UA_NS0ID_STRUCTURE) {
             const UA_NodeId nodeDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
             compatible = compatibleValue(server, &nodeDataType, node->valueRank,
                                     node->arrayDimensionsSize, node->arrayDimensions,
@@ -1245,7 +1245,7 @@ copyAttributeIntoNode(UA_Server *server, UA_Session *session,
 }
 
 static void
-Operation_Write(UA_Server *server, UA_Session *session,
+Operation_Write(UA_Server *server, UA_Session *session, void *context,
                 UA_WriteValue *wv, UA_StatusCode *result) {
     *result = UA_Server_editNode(server, session, &wv->nodeId,
                         (UA_EditNodeCallback)copyAttributeIntoNode, wv);
@@ -1265,10 +1265,9 @@ Service_Write(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_Write,
-                  &request->nodesToWriteSize, &UA_TYPES[UA_TYPES_WRITEVALUE],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_Write, NULL,
+                                           &request->nodesToWriteSize, &UA_TYPES[UA_TYPES_WRITEVALUE],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode

+ 6 - 8
src/server/ua_services_call.c

@@ -194,9 +194,8 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
 }
 
 static void
-Operation_CallMethod(UA_Server *server, UA_Session *session,
-                     const UA_CallMethodRequest *request,
-                     UA_CallMethodResult *result) {
+Operation_CallMethod(UA_Server *server, UA_Session *session, void *context,
+                     const UA_CallMethodRequest *request, UA_CallMethodResult *result) {
     /* Get the method node */
     const UA_MethodNode *method = (const UA_MethodNode*)
         server->config.nodestore.getNode(server->config.nodestore.context,
@@ -240,17 +239,16 @@ void Service_Call(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_CallMethod,
-                  &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_CallMethod, NULL,
+                                           &request->methodsToCallSize, &UA_TYPES[UA_TYPES_CALLMETHODREQUEST],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_CALLMETHODRESULT]);
 }
 
 UA_CallMethodResult UA_EXPORT
 UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request) {
     UA_CallMethodResult result;
     UA_CallMethodResult_init(&result);
-    Operation_CallMethod(server, &adminSession, request, &result);
+    Operation_CallMethod(server, &adminSession, NULL, request, &result);
     return result;
 }
 

+ 23 - 23
src/server/ua_services_discovery.c

@@ -28,11 +28,11 @@ setApplicationDescriptionFromRegisteredServer(const UA_FindServersRequest *reque
     retval |= UA_String_copy(&registeredServer->productUri, &target->productUri);
 
     // if the client requests a specific locale, select the corresponding server name
-    if (request->localeIdsSize) {
+    if(request->localeIdsSize) {
         UA_Boolean appNameFound = UA_FALSE;
-        for (size_t i =0; i<request->localeIdsSize && !appNameFound; i++) {
-            for (size_t j =0; j<registeredServer->serverNamesSize; j++) {
-                if (UA_String_equal(&request->localeIds[i], &registeredServer->serverNames[j].locale)) {
+        for(size_t i =0; i<request->localeIdsSize && !appNameFound; i++) {
+            for(size_t j =0; j<registeredServer->serverNamesSize; j++) {
+                if(UA_String_equal(&request->localeIds[i], &registeredServer->serverNames[j].locale)) {
                     retval |= UA_LocalizedText_copy(&registeredServer->serverNames[j],
                                                     &target->applicationName);
                     appNameFound = UA_TRUE;
@@ -46,7 +46,7 @@ setApplicationDescriptionFromRegisteredServer(const UA_FindServersRequest *reque
         if(!appNameFound && registeredServer->serverNamesSize)
             retval |= UA_LocalizedText_copy(&registeredServer->serverNames[0],
                                             &target->applicationName);
-    } else if (registeredServer->serverNamesSize) {
+    } else if(registeredServer->serverNamesSize) {
         // just take the first name
         retval |= UA_LocalizedText_copy(&registeredServer->serverNames[0], &target->applicationName);
     }
@@ -80,7 +80,7 @@ setApplicationDescriptionFromServer(UA_ApplicationDescription *target, const UA_
     }
     // UaExpert does not list DiscoveryServer, thus set it to Server
     // See http://forum.unified-automation.com/topic1987.html
-    if (target->applicationType == UA_APPLICATIONTYPE_DISCOVERYSERVER)
+    if(target->applicationType == UA_APPLICATIONTYPE_DISCOVERYSERVER)
         target->applicationType = UA_APPLICATIONTYPE_SERVER;
 
     /* add the discoveryUrls from the networklayers */
@@ -94,7 +94,7 @@ setApplicationDescriptionFromServer(UA_ApplicationDescription *target, const UA_
     target->discoveryUrlsSize += server->config.networkLayersSize;
 
     // TODO: Add nl only if discoveryUrl not already present
-    for (size_t i = 0; i < server->config.networkLayersSize; i++) {
+    for(size_t i = 0; i < server->config.networkLayersSize; i++) {
         UA_ServerNetworkLayer* nl = &server->config.networkLayers[i];
         UA_String_copy(&nl->discoveryUrl, &target->discoveryUrls[existing + i]);
     }
@@ -117,7 +117,7 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
 
 #ifdef UA_ENABLE_DISCOVERY
     // check if client only requested a specific set of servers
-    if (request->serverUrisSize) {
+    if(request->serverUrisSize) {
         size_t fsfpSize = sizeof(UA_RegisteredServer*) * server->registeredServersSize;
         foundServerFilteredPointer = (UA_RegisteredServer **)UA_malloc(fsfpSize);
         if(!foundServerFilteredPointer) {
@@ -135,13 +135,13 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
                     if(UA_String_equal(&current->registeredServer.serverUri, &request->serverUris[i])) {
                         // check if entry already in list:
                         UA_Boolean existing = false;
-                        for (size_t j=0; j<foundServersSize; j++) {
-                            if (UA_String_equal(&foundServerFilteredPointer[j]->serverUri, &request->serverUris[i])) {
+                        for(size_t j=0; j<foundServersSize; j++) {
+                            if(UA_String_equal(&foundServerFilteredPointer[j]->serverUri, &request->serverUris[i])) {
                                 existing = true;
                                 break;
                             }
                         }
-                        if (!existing)
+                        if(!existing)
                             foundServerFilteredPointer[foundServersSize++] = &current->registeredServer;
                         break;
                     }
@@ -188,7 +188,7 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
                 setApplicationDescriptionFromServer(&foundServers[0], server);
             if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
                 UA_free(foundServers);
-                if (foundServerFilteredPointer)
+                if(foundServerFilteredPointer)
                     UA_free(foundServerFilteredPointer);
                 return;
             }
@@ -196,20 +196,20 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
 
 #ifdef UA_ENABLE_DISCOVERY
         size_t currentIndex = 0;
-        if (addSelf)
+        if(addSelf)
             currentIndex++;
 
         // add all the registered servers to the list
 
-        if (foundServerFilteredPointer) {
+        if(foundServerFilteredPointer) {
             // use filtered list because client only requested specific uris
             // -1 because foundServersSize also includes this self server
             size_t iterCount = addSelf ? foundServersSize - 1 : foundServersSize;
-            for (size_t i = 0; i < iterCount; i++) {
+            for(size_t i = 0; i < iterCount; i++) {
                 response->responseHeader.serviceResult =
                         setApplicationDescriptionFromRegisteredServer(request, &foundServers[currentIndex++],
                                                                       foundServerFilteredPointer[i]);
-                if (response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+                if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
                     UA_free(foundServers);
                     UA_free(foundServerFilteredPointer);
                     return;
@@ -223,7 +223,7 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
                 response->responseHeader.serviceResult =
                         setApplicationDescriptionFromRegisteredServer(request, &foundServers[currentIndex++],
                                                                       &current->registeredServer);
-                if (response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+                if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
                     UA_free(foundServers);
                     return;
                 }
@@ -232,7 +232,7 @@ void Service_FindServers(UA_Server *server, UA_Session *session,
 #endif
     }
 
-    if (foundServerFilteredPointer)
+    if(foundServerFilteredPointer)
         UA_free(foundServerFilteredPointer);
 
     response->servers = foundServers;
@@ -347,7 +347,7 @@ process_RegisterServer(UA_Server *server, UA_Session *session,
     registeredServer_list_entry* current;
     registeredServer_list_entry *registeredServer_entry = NULL;
     LIST_FOREACH(current, &server->registeredServers, pointers) {
-        if (UA_String_equal(&current->registeredServer.serverUri, &requestServer->serverUri)) {
+        if(UA_String_equal(&current->registeredServer.serverUri, &requestServer->serverUri)) {
             registeredServer_entry = current;
             break;
         }
@@ -397,7 +397,7 @@ process_RegisterServer(UA_Server *server, UA_Session *session,
 #ifdef UA_ENABLE_DISCOVERY_SEMAPHORE
         char* filePath = (char*)
             UA_malloc(sizeof(char)*requestServer->semaphoreFilePath.length+1);
-        if (!filePath) {
+        if(!filePath) {
             UA_LOG_ERROR_SESSION(server->config.logger, session,
                                    "Cannot allocate memory for semaphore path. Out of memory.");
             responseHeader->serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -460,7 +460,7 @@ process_RegisterServer(UA_Server *server, UA_Session *session,
     }
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if (!registeredServer_entry) {
+    if(!registeredServer_entry) {
         // server not yet registered, register it by adding it to the list
         UA_LOG_DEBUG_SESSION(server->config.logger, session, "Registering new server: %.*s",
                              (int)requestServer->serverUri.length, requestServer->serverUri.data);
@@ -532,7 +532,7 @@ void UA_Discovery_cleanupTimedOut(UA_Server *server, UA_DateTime nowMonotonic) {
             size_t fpSize = sizeof(char)*current->registeredServer.semaphoreFilePath.length+1;
             // todo: malloc may fail: return a statuscode
             char* filePath = (char *)UA_malloc(fpSize);
-            if (filePath) {
+            if(filePath) {
                 memcpy(filePath, current->registeredServer.semaphoreFilePath.data,
                        current->registeredServer.semaphoreFilePath.length );
                 filePath[current->registeredServer.semaphoreFilePath.length] = '\0';
@@ -676,7 +676,7 @@ UA_Server_addPeriodicServerRegisterCallback(UA_Server *server,
     {
         periodicServerRegisterCallback_entry *rs, *rs_tmp;
         LIST_FOREACH_SAFE(rs, &server->periodicServerRegisterCallbacks, pointers, rs_tmp) {
-            if (strcmp(rs->callback->discovery_server_url, discoveryServerUrl) == 0) {
+            if(strcmp(rs->callback->discovery_server_url, discoveryServerUrl) == 0) {
                 UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
                             "There is already a register callback for '%s' in place. Removing the older one.", discoveryServerUrl);
                 UA_Server_removeRepeatedCallback(server, rs->callback->id);

+ 8 - 8
src/server/ua_services_discovery_multicast.c

@@ -61,7 +61,7 @@ multicastWorkerLoop(UA_Server *server) {
         unsigned short retVal =
             mdnsd_step(server->mdnsDaemon, server->mdnsSocket,
                        FD_ISSET(server->mdnsSocket, &fds), true, &next_sleep);
-        if (retVal == 1) {
+        if(retVal == 1) {
             UA_LOG_SOCKET_ERRNO_WRAP(
                 UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                           "Multicast error: Can not read from socket. %s", errno_str));
@@ -307,10 +307,10 @@ discovery_createMulticastSocket(void) {
     in.sin_addr.s_addr = 0;
 
 #ifdef _WIN32
-    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
+    if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
         return INVALID_SOCKET;
 #else
-    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+    if((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
         return -1;
 #endif
 
@@ -318,7 +318,7 @@ discovery_createMulticastSocket(void) {
     setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&flag, sizeof(flag));
 #endif
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag));
-    if (bind(s, (struct sockaddr *)&in, sizeof(in))) {
+    if(bind(s, (struct sockaddr *)&in, sizeof(in))) {
         CLOSESOCKET(s);
 #ifdef _WIN32
         return INVALID_SOCKET;
@@ -366,9 +366,9 @@ void destroyMulticastDiscoveryServer(UA_Server* server) {
     mdnsd_shutdown(server->mdnsDaemon);
     mdnsd_free(server->mdnsDaemon);
 #ifdef _WIN32
-    if (server->mdnsSocket != INVALID_SOCKET) {
+    if(server->mdnsSocket != INVALID_SOCKET) {
 #else
-    if (server->mdnsSocket >= 0) {
+    if(server->mdnsSocket >= 0) {
 #endif
         CLOSESOCKET(server->mdnsSocket);
 #ifdef _WIN32
@@ -429,9 +429,9 @@ UA_Discovery_recordExists(UA_Server* server, const char* fullServiceDomain,
                           unsigned short port, const UA_DiscoveryProtocol protocol) {
     // [servername]-[hostname]._opcua-tcp._tcp.local. 86400 IN SRV 0 5 port [hostname].
     mdns_record_t *r  = mdnsd_get_published(server->mdnsDaemon, fullServiceDomain);
-    while (r) {
+    while(r) {
         const mdns_answer_t *data = mdnsd_record_data(r);
-        if (data->type == QTYPE_SRV && (port == 0 || data->srv.port == port))
+        if(data->type == QTYPE_SRV && (port == 0 || data->srv.port == port))
             return UA_TRUE;
         r = mdnsd_record_next(r);
     }

+ 92 - 107
src/server/ua_services_nodemanagement.c

@@ -76,17 +76,16 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
-    UA_NodeClass referenceTypeNodeClass = referenceType->nodeClass;
-    UA_Boolean referenceTypeIsAbstract = referenceType->isAbstract;
-    UA_Nodestore_release(server, (const UA_Node*)referenceType);
-
     /* Check if the referencetype is a reference type node */
-    if(referenceTypeNodeClass != UA_NODECLASS_REFERENCETYPE) {
+    if(referenceType->nodeClass != UA_NODECLASS_REFERENCETYPE) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: Reference type to the parent invalid");
+        UA_Nodestore_release(server, (const UA_Node*)referenceType);
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
 
+    UA_Boolean referenceTypeIsAbstract = referenceType->isAbstract;
+    UA_Nodestore_release(server, (const UA_Node*)referenceType);
     /* Check that the reference type is not abstract */
     if(referenceTypeIsAbstract == true) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -162,13 +161,12 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
     /* If variable node is created below BaseObjectType and has its default valueRank of -2,
      * skip the test */
     const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-    const UA_NodeId refs[2] = {
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
-    };
+    UA_NodeId refs[2];
+    refs[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+    refs[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
     if(node->valueRank != vt->valueRank &&
        node->valueRank != UA_VariableAttributes_default.valueRank &&
-       !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
+       !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs, 2)) {
         /* Check valueRank against the vt */
         if(!compatibleValueRanks(node->valueRank, vt->valueRank))
             return UA_STATUSCODE_BADTYPEMISMATCH;
@@ -221,8 +219,10 @@ useVariableTypeAttributes(UA_Server *server, UA_Session *session,
 
     const UA_VariableTypeNode *vt = (const UA_VariableTypeNode*)
         UA_Nodestore_get(server, typeDefinition);
-    if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE)
+    if(!vt || vt->nodeClass != UA_NODECLASS_VARIABLETYPE) {
+        UA_Nodestore_release(server, (const UA_Node*)vt);
         return UA_STATUSCODE_BADTYPEMISMATCH;
+    }
         
     /* If no value is set, see if the vt provides one and copy it. This needs to
      * be done before copying the datatype from the vt, as setting the datatype
@@ -276,7 +276,8 @@ findChildByBrowsename(UA_Server *server, UA_Session *session,
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
-    Service_Browse_single(server, session, NULL, &bd, 0, &br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, session, &maxrefs, &bd, &br);
     if(br.statusCode != UA_STATUSCODE_GOOD)
         return br.statusCode;
 
@@ -332,8 +333,8 @@ copyChildNodes(UA_Server *server, UA_Session *session,
                const UA_NodeId *destinationNodeId);
 
 static void
-addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item, UA_StatusCode *retval);
+Operation_addReference(UA_Server *server, UA_Session *session, void *context,
+                       const UA_AddReferencesItem *item, UA_StatusCode *retval);
 
 
 /*
@@ -341,7 +342,7 @@ addReference(UA_Server *server, UA_Session *session,
  * Could be used to e.g. delete all the references, except 'HASMODELINGRULE'
  */
 static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_NodeId* referencesSkip) {
-    if (referencesSkipSize == 0) {
+    if(referencesSkipSize == 0) {
         UA_Node_deleteReferences(node);
         return;
     }
@@ -350,13 +351,13 @@ static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_
      * It's faster */
     size_t newSize = 0;
     for(size_t i = 0; i < node->referencesSize; ++i) {
-        for (size_t j = 0; j < referencesSkipSize; j++) {
-            if (UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j])) {
+        for(size_t j = 0; j < referencesSkipSize; j++) {
+            if(UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j])) {
                 newSize++;
             }
         }
     }
-    if (newSize == 0){
+    if(newSize == 0) {
         UA_Node_deleteReferences(node);
         return;
     }
@@ -366,8 +367,8 @@ static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_
     size_t curr = 0;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; ++i) {
-        for (size_t j = 0; j < referencesSkipSize; j++) {
-            if (!UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j]))
+        for(size_t j = 0; j < referencesSkipSize; j++) {
+            if(!UA_NodeId_equal(&node->references[i].referenceTypeId, &referencesSkip[j]))
                 continue;
 
             // copy the reference
@@ -385,8 +386,8 @@ static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_
             drefs->targetIdsSize = srefs->targetIdsSize;
             break;
         }
-        if (retval != UA_STATUSCODE_GOOD) {
-            for (size_t k=0; k<i; k++) {
+        if(retval != UA_STATUSCODE_GOOD) {
+            for(size_t k=0; k<i; k++) {
                 UA_NodeReferenceKind *refs = &newReferences[i];
                 for(size_t j = 0; j < refs->targetIdsSize; ++j)
                     UA_ExpandedNodeId_deleteMembers(&refs->targetIds[j]);
@@ -397,7 +398,7 @@ static void deleteReferencesSubset(UA_Node *node, size_t referencesSkipSize, UA_
     }
 
     UA_Node_deleteReferences(node);
-    if (retval == UA_STATUSCODE_GOOD) {
+    if(retval == UA_STATUSCODE_GOOD) {
         node->references = newReferences;
         node->referencesSize = newSize;
     } else {
@@ -440,7 +441,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         newItem.isForward = true;
         newItem.targetNodeId = rd->nodeId;
         newItem.targetNodeClass = UA_NODECLASS_METHOD;
-        addReference(server, session, &newItem, &retval);
+        Operation_addReference(server, session, NULL, &newItem, &retval);
         return retval;
     }
 
@@ -515,7 +516,8 @@ copyChildNodes(UA_Server *server, UA_Session *session,
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
-    Service_Browse_single(server, session, NULL, &bd, 0, &br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, session, &maxrefs, &bd, &br);
     if(br.statusCode != UA_STATUSCODE_GOOD)
         return br.statusCode;
 
@@ -600,7 +602,7 @@ addTypeDefRef(UA_Server *server, UA_Session *session,
     addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
     addref.isForward = true;
     addref.targetNodeId.nodeId = type->nodeId;
-    addReference(server, session, &addref, &retval);
+    Operation_addReference(server, session, NULL, &addref, &retval);
     return retval;
 }
 
@@ -616,7 +618,7 @@ addParentRef(UA_Server *server, UA_Session *session,
     ref_item.referenceTypeId = *referenceTypeId;
     ref_item.isForward = false;
     ref_item.targetNodeId.nodeId = *parentNodeId;
-    addReference(server, session, &ref_item, &retval);
+    Operation_addReference(server, session, NULL, &ref_item, &retval);
     return retval;
 }
 
@@ -626,9 +628,8 @@ addParentRef(UA_Server *server, UA_Session *session,
 
 /* Prepare the node, then add it to the nodestore */
 UA_StatusCode
-Operation_addNode_begin(UA_Server *server, UA_Session *session,
-                        const UA_AddNodesItem *item, void *nodeContext,
-                        UA_NodeId *outNewNodeId) {
+Operation_addNode_begin(UA_Server *server, UA_Session *session, void *nodeContext,
+                        const UA_AddNodesItem *item, UA_NodeId *outNewNodeId) {
     /* Check the namespaceindex */
     if(item->requestedNewNodeId.nodeId.namespaceIndex >= server->namespacesSize) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -724,11 +725,11 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
        node->nodeClass == UA_NODECLASS_OBJECTTYPE ||
        node->nodeClass == UA_NODECLASS_REFERENCETYPE ||
        node->nodeClass == UA_NODECLASS_DATATYPE) {
-        if (UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL))
+        if(UA_NodeId_equal(referenceTypeId, &UA_NODEID_NULL))
             referenceTypeId = &hasSubtype;
         const UA_Node *parentNode = UA_Nodestore_get(server, parentNodeId);
-        if (parentNode) {
-            if (parentNode->nodeClass == node->nodeClass)
+        if(parentNode) {
+            if(parentNode->nodeClass == node->nodeClass)
                 typeDefinitionId = parentNodeId;
             UA_Nodestore_release(server, parentNode);
         }
@@ -775,7 +776,7 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
         }
 
         UA_Boolean  typeOk = UA_FALSE;
-        switch (node->nodeClass) {
+        switch(node->nodeClass) {
             case UA_NODECLASS_DATATYPE:
                 typeOk = type->nodeClass == UA_NODECLASS_DATATYPE;
                 break;
@@ -803,7 +804,7 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
             default:
                 typeOk = UA_FALSE;
         }
-        if (!typeOk) {
+        if(!typeOk) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Type does not match node class");
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
@@ -818,12 +819,11 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
                 const UA_NodeId variableTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
                 /* A variable may be of an object type which again is below BaseObjectType */
                 const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-                const UA_NodeId refs[2] = {
-                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
-                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
-                };
-                if(!isNodeInTree(&server->config.nodestore, parentNodeId, &variableTypes, refs , 2) &&
-                   !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
+                UA_NodeId refs[2];
+                refs[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+                refs[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
+                if(!isNodeInTree(&server->config.nodestore, parentNodeId, &variableTypes, refs, 2) &&
+                   !isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs, 2)) {
                     UA_LOG_INFO_SESSION(server->config.logger, session,
                                         "AddNodes: Type of variable node must "
                                         "be VariableType and not cannot be abstract");
@@ -837,11 +837,10 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
             if(((const UA_ObjectTypeNode*)type)->isAbstract) {
                 /* Object node created of an abstract ObjectType. Only allowed if within BaseObjectType folder */
                 const UA_NodeId objectTypes = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);
-                const UA_NodeId refs[2] = {
-                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
-                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT)
-                };
-                if(!isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs , 2)) {
+                UA_NodeId refs[2];
+                refs[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+                refs[1] = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
+                if(!isNodeInTree(&server->config.nodestore, parentNodeId, &objectTypes, refs, 2)) {
                     UA_LOG_INFO_SESSION(server->config.logger, session,
                                         "AddNodes: Type of object node must "
                                                 "be ObjectType and not be abstract");
@@ -873,7 +872,7 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
        node->nodeClass == UA_NODECLASS_OBJECT) {
         UA_assert(type != NULL); /* see above */
         /* Add (mandatory) child nodes from the type definition */
-        if (!server->bootstrapNS0) {
+        if(!server->bootstrapNS0) {
             retval = addChildren(server, session, node, type);
             if(retval != UA_STATUSCODE_GOOD) {
                 UA_LOG_INFO_SESSION(server->config.logger, session,
@@ -896,7 +895,7 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
 
     /* Add reference to the parent */
     if(!UA_NodeId_isNull(parentNodeId)) {
-        if (UA_NodeId_isNull(referenceTypeId)) {
+        if(UA_NodeId_isNull(referenceTypeId)) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Reference to parent cannot be null");
             retval = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
@@ -929,8 +928,8 @@ Operation_addNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId
 }
 
 static void
-Operation_addNode(UA_Server *server, UA_Session *session, const UA_AddNodesItem *item,
-                  void *nodeContext, UA_AddNodesResult *result) {
+Operation_addNode(UA_Server *server, UA_Session *session, void *nodeContext,
+                  const UA_AddNodesItem *item, UA_AddNodesResult *result) {
     /* Do not check access for server */
     if(session != &adminSession && server->config.accessControl.allowAddNode &&
        !server->config.accessControl.allowAddNode(&session->sessionId, session->sessionHandle, item)) {
@@ -938,8 +937,8 @@ Operation_addNode(UA_Server *server, UA_Session *session, const UA_AddNodesItem
         return;
     }
 
-    result->statusCode = Operation_addNode_begin(server, session, item, nodeContext,
-                                                 &result->addedNodeId);
+    result->statusCode = Operation_addNode_begin(server, session, nodeContext,
+                                                 item, &result->addedNodeId);
     if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
 
@@ -954,19 +953,11 @@ Operation_addNode(UA_Server *server, UA_Session *session, const UA_AddNodesItem
         UA_NodeId_deleteMembers(&result->addedNodeId);
 }
 
-static void
-Service_AddNode(UA_Server *server, UA_Session *session,
-                const UA_AddNodesItem *item,
-                UA_AddNodesResult *result) {
-    Operation_addNode(server, session, item, NULL, result);
-}
-
 void
 Service_AddNodes(UA_Server *server, UA_Session *session,
                       const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing AddNodesRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing AddNodesRequest");
 
     if(server->config.maxNodesPerNodeManagement != 0 &&
        request->nodesToAddSize > server->config.maxNodesPerNodeManagement) {
@@ -975,10 +966,9 @@ Service_AddNodes(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Service_AddNode,
-                  &request->nodesToAddSize, &UA_TYPES[UA_TYPES_ADDNODESITEM],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addNode, NULL,
+                                           &request->nodesToAddSize, &UA_TYPES[UA_TYPES_ADDNODESITEM],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_ADDNODESRESULT]);
 }
 
 UA_StatusCode
@@ -1007,7 +997,7 @@ __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
     /* Call the normal addnodes service */
     UA_AddNodesResult result;
     UA_AddNodesResult_init(&result);
-    Operation_addNode(server, &adminSession, &item, nodeContext, &result);
+    Operation_addNode(server, &adminSession, nodeContext, &item, &result);
     if(outNewNodeId)
         *outNewNodeId = result.addedNodeId;
     else
@@ -1031,8 +1021,8 @@ UA_Server_addNode_begin(UA_Server *server, const UA_NodeClass nodeClass,
     item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
     item.nodeAttributes.content.decoded.type = attributeType;
     item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr;
-    return Operation_addNode_begin(server, &adminSession, &item,
-                                   nodeContext, outNewNodeId);
+    return Operation_addNode_begin(server, &adminSession, nodeContext,
+                                   &item, outNewNodeId);
 }
 
 UA_StatusCode
@@ -1049,9 +1039,8 @@ UA_Server_addNode_finish(UA_Server *server, const UA_NodeId nodeId,
 /****************/
 
 static void
-deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item,
-                UA_StatusCode *retval);
+Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
+                          const UA_DeleteReferencesItem *item, UA_StatusCode *retval);
 
 /* Remove references to this node (in the other nodes) */
 static void
@@ -1068,7 +1057,7 @@ removeIncomingReferences(UA_Server *server, UA_Session *session,
         item.referenceTypeId = refs->referenceTypeId;
         for(size_t j = 0; j < refs->targetIdsSize; ++j) {
             item.sourceNodeId = refs->targetIds[j].nodeId;
-            deleteReference(server, session, &item, &dummy);
+            Operation_deleteReference(server, session, NULL, &item, &dummy);
         }
     }
 }
@@ -1104,7 +1093,7 @@ deconstructNode(UA_Server *server, UA_Session *session,
 }
 
 static void
-deleteNodeOperation(UA_Server *server, UA_Session *session,
+deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
                     const UA_DeleteNodesItem *item, UA_StatusCode *result);
 
 static void
@@ -1122,7 +1111,8 @@ removeChildren(UA_Server *server, UA_Session *session,
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
-    Service_Browse_single(server, session, NULL, &bd, 0, &br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, session, &maxrefs, &bd, &br);
     if(br.statusCode != UA_STATUSCODE_GOOD)
         return;
 
@@ -1132,9 +1122,12 @@ removeChildren(UA_Server *server, UA_Session *session,
     /* Remove every child */
     for(size_t i = 0; i < br.referencesSize; ++i) {
         UA_ReferenceDescription *rd = &br.references[i];
+        // check for self-reference to avoid endless loop
+        if(UA_NodeId_equal(&node->nodeId, &rd->nodeId.nodeId))
+            continue;
         item.nodeId = rd->nodeId.nodeId;
         UA_StatusCode retval;
-        deleteNodeOperation(server, session, &item, &retval);
+        deleteNodeOperation(server, session, NULL, &item, &retval);
     }
 
     UA_BrowseResult_deleteMembers(&br);
@@ -1145,7 +1138,7 @@ removeDeconstructedNode(UA_Server *server, UA_Session *session,
                         const UA_Node *node, UA_Boolean removeTargetRefs) {
     /* Remove all children of the node */
     removeChildren(server, session, node);
-    
+
     /* Remove references to the node (not the references going out, as the node
      * will be deleted anyway) */
     if(removeTargetRefs)
@@ -1156,7 +1149,7 @@ removeDeconstructedNode(UA_Server *server, UA_Session *session,
 }
 
 static void
-deleteNodeOperation(UA_Server *server, UA_Session *session,
+deleteNodeOperation(UA_Server *server, UA_Session *session, void *context,
                     const UA_DeleteNodesItem *item, UA_StatusCode *result) {
     /* Do not check access for server */
     if(session != &adminSession && server->config.accessControl.allowDeleteNode &&
@@ -1201,12 +1194,9 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session,
-                                           (UA_ServiceOperation)deleteNodeOperation,
-                                           &request->nodesToDeleteSize,
-                                           &UA_TYPES[UA_TYPES_DELETENODESITEM],
-                                           &response->resultsSize,
-                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)deleteNodeOperation, NULL,
+                                           &request->nodesToDeleteSize, &UA_TYPES[UA_TYPES_DELETENODESITEM],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1216,7 +1206,7 @@ UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
     item.deleteTargetReferences = deleteReferences;
     item.nodeId = nodeId;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    deleteNodeOperation(server, &adminSession, &item, &retval);
+    deleteNodeOperation(server, &adminSession, NULL, &item, &retval);
     return retval;
 }
 
@@ -1237,8 +1227,8 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
 }
 
 static void
-addReference(UA_Server *server, UA_Session *session,
-             const UA_AddReferencesItem *item, UA_StatusCode *retval) {
+Operation_addReference(UA_Server *server, UA_Session *session, void *context,
+                       const UA_AddReferencesItem *item, UA_StatusCode *retval) {
     /* Do not check access for server */
     if(session != &adminSession && server->config.accessControl.allowAddReference &&
        !server->config.accessControl. allowAddReference(&session->sessionId, session->sessionHandle, item)) {
@@ -1296,12 +1286,9 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session,
-                                           (UA_ServiceOperation) addReference,
-                                           &request->referencesToAddSize,
-                                           &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
-                                           &response->resultsSize,
-                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_addReference, NULL,
+                                           &request->referencesToAddSize, &UA_TYPES[UA_TYPES_ADDREFERENCESITEM],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1317,7 +1304,7 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
     item.targetNodeId = targetId;
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    addReference(server, &adminSession, &item, &retval);
+    Operation_addReference(server, &adminSession, NULL, &item, &retval);
     return retval;
 }
 
@@ -1326,8 +1313,8 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
 /*********************/
 
 static void
-deleteReference(UA_Server *server, UA_Session *session,
-                const UA_DeleteReferencesItem *item, UA_StatusCode *retval) {
+Operation_deleteReference(UA_Server *server, UA_Session *session, void *context,
+                          const UA_DeleteReferencesItem *item, UA_StatusCode *retval) {
     /* Do not check access for server */
     if(session != &adminSession && server->config.accessControl.allowDeleteReference &&
        !server->config.accessControl.allowDeleteReference(&session->sessionId, session->sessionHandle, item)) {
@@ -1369,12 +1356,9 @@ Service_DeleteReferences(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult =
-        UA_Server_processServiceOperations(server, session,
-                                           (UA_ServiceOperation) deleteReference,
-                                           &request->referencesToDeleteSize,
-                                           &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
-                                           &response->resultsSize,
-                                           &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_deleteReference, NULL,
+                                           &request->referencesToDeleteSize, &UA_TYPES[UA_TYPES_DELETEREFERENCESITEM],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 UA_StatusCode
@@ -1390,7 +1374,7 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
     item.deleteBidirectional = deleteBidirectional;
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    deleteReference(server, &adminSession, &item, &retval);
+    Operation_deleteReference(server, &adminSession, NULL, &item, &retval);
     return retval;
 }
 
@@ -1440,8 +1424,8 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
         outNewNodeId = &newNodeId;
         deleteNodeId = UA_TRUE;
     }
-    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, &item,
-                                                   nodeContext, outNewNodeId);
+    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, nodeContext,
+                                                   &item, outNewNodeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     retval = UA_Server_setVariableNode_dataSource(server, *outNewNodeId, dataSource);
@@ -1500,7 +1484,8 @@ UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
 
     UA_BrowseResult br;
     UA_BrowseResult_init(&br);
-    Service_Browse_single(server, &adminSession, NULL, &bd, 0, &br);
+    UA_UInt32 maxrefs = 0;
+    Operation_Browse(server, &adminSession, &maxrefs, &bd, &br);
 
     UA_StatusCode retval = br.statusCode;
     if(retval != UA_STATUSCODE_GOOD) {
@@ -1595,8 +1580,8 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
         outNewNodeId = &newId;
     }
 
-    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, &item,
-                                                   nodeContext, outNewNodeId);
+    UA_StatusCode retval = Operation_addNode_begin(server, &adminSession, nodeContext,
+                                                   &item, outNewNodeId);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 

+ 11 - 0
src/server/ua_services_session.c

@@ -260,8 +260,19 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "ActivateSession: Detach from old channel");
         UA_Session_detachFromSecureChannel(session);
+        session->activated = false;
     }
 
+    if (session->activated) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "ActivateSession: SecureChannel %i wants "
+                                    "to activate, but the session is already activated",
+                            channel->securityToken.channelId);
+        response->responseHeader.serviceResult =
+                UA_STATUSCODE_BADSESSIONIDINVALID;
+        return;
+
+    }
     /* Attach to the SecureChannel and activate */
     UA_Session_attachToSecureChannel(session, channel);
     session->activated = true;

+ 106 - 119
src/server/ua_services_subscription.c

@@ -25,7 +25,7 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_DEBUG_SESSION(server->config.logger, subscription->session, "Subscription %u | "
                              "Could not unregister publish callback with error code %s",
-                             subscription->subscriptionID, UA_StatusCode_name(retval));
+                             subscription->subscriptionId, UA_StatusCode_name(retval));
 
     /* re-parameterize the subscription */
     subscription->publishingInterval = requestedPublishingInterval;
@@ -50,19 +50,20 @@ setSubscriptionSettings(UA_Server *server, UA_Subscription *subscription,
     if(retval != UA_STATUSCODE_GOOD)
         UA_LOG_DEBUG_SESSION(server->config.logger, subscription->session, "Subscription %u | "
                              "Could not register publish callback with error code %s",
-                             subscription->subscriptionID, UA_StatusCode_name(retval));
+                             subscription->subscriptionId, UA_StatusCode_name(retval));
 }
 
 void
 Service_CreateSubscription(UA_Server *server, UA_Session *session,
                            const UA_CreateSubscriptionRequest *request,
                            UA_CreateSubscriptionResponse *response) {
-
+    /* Check limits for the number of subscriptions */
     if((server->config.maxSubscriptionsPerSession != 0) &&
        (UA_Session_getNumSubscriptions(session) >= server->config.maxSubscriptionsPerSession)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS;
         return;
-   }
+    }
+
     /* Create the subscription */
     UA_Subscription *newSubscription = UA_Subscription_new(session, response->subscriptionId);
     if(!newSubscription) {
@@ -71,7 +72,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    newSubscription->subscriptionID = UA_Session_getUniqueSubscriptionID(session);
+    newSubscription->subscriptionId = UA_Session_getUniqueSubscriptionId(session);
     UA_Session_addSubscription(session, newSubscription);
 
     /* Set the subscription parameters */
@@ -82,7 +83,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
     newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount; /* set settings first */
 
     /* Prepare the response */
-    response->subscriptionId = newSubscription->subscriptionID;
+    response->subscriptionId = newSubscription->subscriptionId;
     response->revisedPublishingInterval = newSubscription->publishingInterval;
     response->revisedLifetimeCount = newSubscription->lifeTimeCount;
     response->revisedMaxKeepAliveCount = newSubscription->maxKeepAliveCount;
@@ -97,10 +98,9 @@ void
 Service_ModifySubscription(UA_Server *server, UA_Session *session,
                            const UA_ModifySubscriptionRequest *request,
                            UA_ModifySubscriptionResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing ModifySubscriptionRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ModifySubscriptionRequest");
 
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
@@ -115,39 +115,31 @@ Service_ModifySubscription(UA_Server *server, UA_Session *session,
     response->revisedMaxKeepAliveCount = sub->maxKeepAliveCount;
 }
 
-static UA_THREAD_LOCAL UA_Boolean op_publishingEnabled;
-
 static void
 Operation_SetPublishingMode(UA_Server *Server, UA_Session *session,
-                            UA_UInt32 *subscriptionId,
+                            UA_Boolean *publishingEnabled, UA_UInt32 *subscriptionId,
                             UA_StatusCode *result) {
-    UA_Subscription *sub =
-        UA_Session_getSubscriptionByID(session, *subscriptionId);
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, *subscriptionId);
     if(!sub) {
         *result = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
-    /* Reset the subscription lifetime */
-    sub->currentLifetimeCount = 0; 
-
-    /* Set the publishing mode */
-    sub->publishingEnabled = op_publishingEnabled;
+    sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
+    sub->publishingEnabled = *publishingEnabled; /* Set the publishing mode */
 }
 
 void
 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
                           const UA_SetPublishingModeRequest *request,
                           UA_SetPublishingModeResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing SetPublishingModeRequest");
-
-    op_publishingEnabled = request->publishingEnabled;
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing SetPublishingModeRequest");
+    UA_Boolean publishingEnabled = request->publishingEnabled; /* request is const */
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_SetPublishingMode,
-                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_SetPublishingMode,
+                                           &publishingEnabled,
+                                           &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 static void
@@ -162,7 +154,7 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 
     /* SamplingInterval */
     UA_Double samplingInterval = params->samplingInterval;
-    if(mon->attributeID == UA_ATTRIBUTEID_VALUE) {
+    if(mon->attributeId == UA_ATTRIBUTEID_VALUE) {
         const UA_VariableNode *vn = (const UA_VariableNode*)
             UA_Nodestore_get(server, &mon->monitoredNodeId);
         if(vn) {
@@ -171,7 +163,7 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
                 samplingInterval = vn->minimumSamplingInterval;
             UA_Nodestore_release(server, (const UA_Node*)vn);
         }
-    } else if(mon->attributeID == UA_ATTRIBUTEID_EVENTNOTIFIER) {
+    } else if(mon->attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER) {
         /* TODO: events should not need a samplinginterval */
         samplingInterval = 10000.0f; // 10 seconds to reduce the load
     }
@@ -206,18 +198,27 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
 
 /* Thread-local variables to pass additional arguments into the operation */
-static UA_THREAD_LOCAL UA_Subscription *op_sub;
-static UA_THREAD_LOCAL UA_TimestampsToReturn op_timestampsToReturn2;
+struct createMonContext {
+    UA_Subscription *sub;
+    UA_TimestampsToReturn timestampsToReturn;
+};
 
 static void
-Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session,
+Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session, struct createMonContext *cmc,
                               const UA_MonitoredItemCreateRequest *request,
                               UA_MonitoredItemCreateResult *result) {
+    /* Check available capacity */
+    if(server->config.maxMonitoredItemsPerSubscription != 0 &&
+       UA_Subscription_getNumMonitoredItems(cmc->sub) >= server->config.maxMonitoredItemsPerSubscription) {
+        result->statusCode = UA_STATUSCODE_BADTOOMANYMONITOREDITEMS;
+        return;
+    }
+
     /* Make an example read to get errors in the itemToMonitor. Allow return
      * codes "good" and "uncertain", as well as a list of statuscodes that might
      * be repaired inside the data source. */
     UA_DataValue v = UA_Server_readWithSession(server, session, &request->itemToMonitor,
-                                               op_timestampsToReturn2);
+                                               cmc->timestampsToReturn);
     if(v.hasStatus && (v.status >> 30) > 1 &&
        v.status != UA_STATUSCODE_BADRESOURCEUNAVAILABLE &&
        v.status != UA_STATUSCODE_BADCOMMUNICATIONERROR &&
@@ -259,14 +260,14 @@ Operation_CreateMonitoredItem(UA_Server *server, UA_Session *session,
         MonitoredItem_delete(server, newMon);
         return;
     }
-    newMon->subscription = op_sub;
-    newMon->attributeID = request->itemToMonitor.attributeId;
-    newMon->itemId = ++(op_sub->lastMonitoredItemId);
-    newMon->timestampsToReturn = op_timestampsToReturn2;
+    newMon->subscription = cmc->sub;
+    newMon->attributeId = request->itemToMonitor.attributeId;
+    newMon->itemId = ++(cmc->sub->lastMonitoredItemId);
+    newMon->timestampsToReturn = cmc->timestampsToReturn;
     setMonitoredItemSettings(server, newMon, request->monitoringMode,
                              &request->requestedParameters);
 
-    UA_Subscription_addMonitoredItem(op_sub, newMon);
+    UA_Subscription_addMonitoredItem(cmc->sub, newMon);
 
     /* Create the first sample */
     if(request->monitoringMode == UA_MONITORINGMODE_REPORTING)
@@ -283,8 +284,7 @@ void
 Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
                              const UA_CreateMonitoredItemsRequest *request,
                              UA_CreateMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing CreateMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing CreateMonitoredItemsRequest");
 
     if(server->config.maxMonitoredItemsPerCall != 0 &&
        request->itemsToCreateSize > server->config.maxMonitoredItemsPerCall) {
@@ -293,50 +293,41 @@ Service_CreateMonitoredItems(UA_Server *server, UA_Session *session,
     }
 
     /* Check if the timestampstoreturn is valid */
-    op_timestampsToReturn2 = request->timestampsToReturn;
-    if(op_timestampsToReturn2 > UA_TIMESTAMPSTORETURN_NEITHER) {
+    struct createMonContext cmc;
+    cmc.timestampsToReturn = request->timestampsToReturn;
+    if(cmc.timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }
 
     /* Find the subscription */
-    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!op_sub) {
+    cmc.sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
+    if(!cmc.sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
-    if((server->config.maxMonitoredItemsPerSubscription != 0) &&
-       ((UA_Subscription_getNumMonitoredItems(op_sub) + request->itemsToCreateSize) >
-        server->config.maxMonitoredItemsPerSubscription)) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYMONITOREDITEMS;
-        return;
-    }
-
     /* Reset the subscription lifetime */
-    op_sub->currentLifetimeCount = 0;
+    cmc.sub->currentLifetimeCount = 0;
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_CreateMonitoredItem,
-                  &request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_CreateMonitoredItem, &cmc,
+                                           &request->itemsToCreateSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATEREQUEST],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMCREATERESULT]);
 }
 
 static void
-Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session,
+Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session, UA_Subscription *sub,
                               const UA_MonitoredItemModifyRequest *request,
                               UA_MonitoredItemModifyResult *result) {
     /* Get the MonitoredItem */
-    UA_MonitoredItem *mon =
-        UA_Subscription_getMonitoredItem(op_sub, request->monitoredItemId);
+    UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemId);
     if(!mon) {
         result->statusCode = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
     }
 
-    setMonitoredItemSettings(server, mon, mon->monitoringMode,
-                             &request->requestedParameters);
+    setMonitoredItemSettings(server, mon, mon->monitoringMode, &request->requestedParameters);
     result->revisedSamplingInterval = mon->samplingInterval;
     result->revisedQueueSize = mon->maxQueueSize;
 }
@@ -344,8 +335,7 @@ Operation_ModifyMonitoredItem(UA_Server *server, UA_Session *session,
 void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   UA_ModifyMonitoredItemsResponse *response) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Processing ModifyMonitoredItemsRequest");
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing ModifyMonitoredItemsRequest");
 
     if(server->config.maxMonitoredItemsPerCall != 0 &&
        request->itemsToModifySize > server->config.maxMonitoredItemsPerCall) {
@@ -360,46 +350,47 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
     }
 
     /* Get the subscription */
-    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!op_sub) {
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
+    if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
-    /* Reset the subscription lifetime */
-    op_sub->currentLifetimeCount = 0;
+    sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
 
     response->responseHeader.serviceResult = 
         UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_ModifyMonitoredItem,
-                  &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
+                                           (UA_ServiceOperation)Operation_ModifyMonitoredItem, sub,
+                                           &request->itemsToModifySize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYREQUEST],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_MONITOREDITEMMODIFYRESULT]);
 }
 
-/* Get the additional argument into the operation */
-static UA_THREAD_LOCAL UA_MonitoringMode op_monitoringMode;
+struct setMonitoringContext {
+    UA_Subscription *sub;
+    UA_MonitoringMode monitoringMode;
+};
 
 static void
 Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
-                            UA_UInt32 *monitoredItemId,
-                            UA_StatusCode *result) {
+                            struct setMonitoringContext *smc,
+                            UA_UInt32 *monitoredItemId, UA_StatusCode *result) {
     UA_MonitoredItem *mon =
-        UA_Subscription_getMonitoredItem(op_sub, *monitoredItemId);
+        UA_Subscription_getMonitoredItem(smc->sub, *monitoredItemId);
     if(!mon) {
         *result = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
         return;
     }
-
+  
     /* check monitoringMode is valid or not */
-    if(op_monitoringMode > UA_MONITORINGMODE_REPORTING) {
+    if(smc->monitoringMode > UA_MONITORINGMODE_REPORTING) {
         return;
     }
 
-    if(mon->monitoringMode == op_monitoringMode)
+    if(mon->monitoringMode == smc->monitoringMode)
         return;
 
-    mon->monitoringMode = op_monitoringMode;
-    if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING) {
+    mon->monitoringMode = smc->monitoringMode;
+    if(mon->monitoringMode == UA_MONITORINGMODE_REPORTING)
         MonitoredItem_registerSampleCallback(server, mon);
     } else if (mon->monitoringMode == UA_MONITORINGMODE_DISABLED) {
         /*  Setting the mode to DISABLED causes all queued Notifications to be delete */
@@ -430,21 +421,20 @@ void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
     }
 
     /* Get the subscription */
-    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!op_sub) {
+    struct setMonitoringContext smc;
+    smc.sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
+    if(!smc.sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
-    /* Reset the subscription lifetime */
-    op_sub->currentLifetimeCount = 0;
+    smc.sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
 
-    op_monitoringMode = request->monitoringMode;
+    smc.monitoringMode = request->monitoringMode;
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_SetMonitoringMode,
-                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_SetMonitoringMode, &smc,
+                                           &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 /* TODO: Unify with senderror in ua_server_binary.c */
@@ -477,7 +467,7 @@ Service_Publish(UA_Server *server, UA_Session *session,
      * resources for the new publish request. If the limit has been reached the
      * oldest publish request shall be responded */
     if((server->config.maxPublishReqPerSession != 0 ) &&
-       (UA_Session_getNumPublishReq(session) >= server->config.maxPublishReqPerSession)){
+       (UA_Session_getNumPublishReq(session) >= server->config.maxPublishReqPerSession)) {
         if(!UA_Subscription_reachedPublishReqLimit(server,session)) {
             subscriptionSendError(session->header.channel, requestId,
                                   request->requestHeader.requestHandle,
@@ -517,7 +507,7 @@ Service_Publish(UA_Server *server, UA_Session *session,
     /* Delete Acknowledged Subscription Messages */
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; ++i) {
         UA_SubscriptionAcknowledgement *ack = &request->subscriptionAcknowledgements[i];
-        UA_Subscription *sub = UA_Session_getSubscriptionByID(session, ack->subscriptionId);
+        UA_Subscription *sub = UA_Session_getSubscriptionById(session, ack->subscriptionId);
         if(!sub) {
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             UA_LOG_DEBUG_SESSION(server->config.logger, session,
@@ -539,27 +529,27 @@ Service_Publish(UA_Server *server, UA_Session *session,
     UA_Boolean found = true; 
     int loopCount = 1;
 
-    if (session->lastSeenSubscriptionID > 0){
+    if(session->lastSeenSubscriptionId > 0) {
         /* If we found anything one the first loop or if there are LATE 
-         * in the list before lastSeenSubscriptionID and not LATE after 
-         * lastSeenSubscriptionID we need a second loop.
+         * in the list before lastSeenSubscriptionId and not LATE after 
+         * lastSeenSubscriptionId we need a second loop.
          */
         loopCount = 2;
         /* We must find the last seen subscription id  */
         found = false;
     }
 
-    for(int i=0; i<loopCount; i++){
+    for(int i=0; i<loopCount; i++) {
        LIST_FOREACH(immediate, &session->serverSubscriptions, listEntry) {
-            if (!found){
-                if (session->lastSeenSubscriptionID == immediate->subscriptionID){
+            if(!found) {
+                if(session->lastSeenSubscriptionId == immediate->subscriptionId) {
                     found = true; 
                 }     
-            }else{
+            } else {
                 if(immediate->state == UA_SUBSCRIPTIONSTATE_LATE) {
-                    session->lastSeenSubscriptionID = immediate->subscriptionID;
+                    session->lastSeenSubscriptionId = immediate->subscriptionId;
                     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Subscription %u | "
-                                         "Response on a late subscription", immediate->subscriptionID);
+                                         "Response on a late subscription", immediate->subscriptionId);
                     UA_Subscription_publishCallback(server, immediate);
                     return;
                 }     
@@ -568,11 +558,11 @@ Service_Publish(UA_Server *server, UA_Session *session,
         /* after the first loop, we can publish the first subscription with UA_SUBSCRIPTIONSTATE_LATE */
         found = true;
     }
-    session->lastSeenSubscriptionID = 0;
+    session->lastSeenSubscriptionId = 0;
 }
 
 static void
-Operation_DeleteSubscription(UA_Server *server, UA_Session *session,
+Operation_DeleteSubscription(UA_Server *server, UA_Session *session, void *_,
                              UA_UInt32 *subscriptionId, UA_StatusCode *result) {
     *result = UA_Session_deleteSubscription(server, session, *subscriptionId);
     if(*result == UA_STATUSCODE_GOOD) {
@@ -594,10 +584,9 @@ Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                          "Processing DeleteSubscriptionsRequest");
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_DeleteSubscription,
-                  &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteSubscription, NULL,
+                                           &request->subscriptionIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 
     /* The session has at least one subscription */
     if(LIST_FIRST(&session->serverSubscriptions))
@@ -608,10 +597,9 @@ Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
 }
 
 static void
-Operation_DeleteMonitoredItem(UA_Server *server, UA_Session *session,
-                              UA_UInt32 *monitoredItemId,
-                              UA_StatusCode *result) {
-    *result = UA_Subscription_deleteMonitoredItem(server, op_sub, *monitoredItemId);
+Operation_DeleteMonitoredItem(UA_Server *server, UA_Session *session, UA_Subscription *sub,
+                              UA_UInt32 *monitoredItemId, UA_StatusCode *result) {
+    *result = UA_Subscription_deleteMonitoredItem(server, sub, *monitoredItemId);
 }
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
@@ -627,20 +615,19 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
     }
 
     /* Get the subscription */
-    op_sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if(!op_sub) {
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
+    if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }
 
     /* Reset the subscription lifetime */
-    op_sub->currentLifetimeCount = 0;
+    sub->currentLifetimeCount = 0;
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_DeleteMonitoredItem,
-                  &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_DeleteMonitoredItem, sub,
+                                           &request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_UINT32],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
 }
 
 void Service_Republish(UA_Server *server, UA_Session *session,
@@ -650,8 +637,8 @@ void Service_Republish(UA_Server *server, UA_Session *session,
                          "Processing RepublishRequest");
 
     /* Get the subscription */
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
-    if (!sub) {
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, request->subscriptionId);
+    if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
         return;
     }

+ 115 - 126
src/server/ua_services_view.c

@@ -57,9 +57,9 @@ relevantReference(UA_Server *server, UA_Boolean includeSubtypes,
 /* Returns whether the node / continuationpoint is done */
 static UA_Boolean
 browseReferences(UA_Server *server, const UA_Node *node,
-                 const UA_BrowseDescription *descr,
-                 UA_BrowseResult *result, ContinuationPointEntry *cp) {
+                 ContinuationPointEntry *cp, UA_BrowseResult *result) {
     UA_assert(cp != NULL);
+    const UA_BrowseDescription *descr = &cp->browseDescription;
 
     /* If the node has no references, just return */
     if(node->referencesSize == 0) {
@@ -186,39 +186,20 @@ browseReferences(UA_Server *server, const UA_Node *node,
 }
 
 /* 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 descr If no cp is set, we take the browsedescription from there
- * @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,
-                      ContinuationPointEntry *cp,
-                      const UA_BrowseDescription *descr,
-                      UA_UInt32 maxrefs, UA_BrowseResult *result) {
-    ContinuationPointEntry *internal_cp = cp;
-    if(!internal_cp) {
-        /* If there is no continuation point, stack-allocate one. It gets copied
-         * on the heap when this is required at a later point. */
-        internal_cp = (ContinuationPointEntry*)UA_alloca(sizeof(ContinuationPointEntry));
-        memset(internal_cp, 0, sizeof(ContinuationPointEntry));
-        internal_cp->maxReferences = maxrefs;
-    } else {
-        /* Set the browsedescription if a cp is given */
-        descr = &cp->browseDescription;
-    }
+ * Browse and BrowseNext. The ContinuationPoint contains all the data used.
+ * Including the BrowseDescription. Returns whether there are remaining
+ * references. */
+static UA_Boolean
+browseWithContinuation(UA_Server *server, UA_Session *session,
+                       ContinuationPointEntry *cp, UA_BrowseResult *result) {
+    const UA_BrowseDescription *descr = &cp->browseDescription;
 
     /* Is the browsedirection valid? */
     if(descr->browseDirection != UA_BROWSEDIRECTION_BOTH &&
        descr->browseDirection != UA_BROWSEDIRECTION_FORWARD &&
        descr->browseDirection != UA_BROWSEDIRECTION_INVERSE) {
         result->statusCode = UA_STATUSCODE_BADBROWSEDIRECTIONINVALID;
-        return;
+        return true;
     }
 
     /* Is the reference type valid? */
@@ -226,7 +207,7 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
         const UA_Node *reftype = UA_Nodestore_get(server, &descr->referenceTypeId);
         if(!reftype) {
             result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-            return;
+            return true;
         }
 
         UA_Boolean isRef = (reftype->nodeClass == UA_NODECLASS_REFERENCETYPE);
@@ -234,116 +215,113 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
 
         if(!isRef) {
             result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-            return;
+            return true;
         }
     }
 
     const UA_Node *node = UA_Nodestore_get(server, &descr->nodeId);
     if(!node) {
         result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
-        return;
+        return true;
     }
 
     /* Browse the references */
-    UA_Boolean done = browseReferences(server, node, descr, result, internal_cp);
-
+    UA_Boolean done = browseReferences(server, node, cp, result);
     UA_Nodestore_release(server, node);
+    return done;
+}
 
-    /* Exit early if an error occurred */
-    if(result->statusCode != UA_STATUSCODE_GOOD)
-        return;
-
-    /* A continuation point exists already */
-    if(cp) {
-        if(done) {
-            removeCp(cp, session); /* All done, remove a finished continuationPoint */
-        } else {
-            /* Return the cp identifier */
-            UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
-        }
+/* Start to browse with no previous cp */
+void
+Operation_Browse(UA_Server *server, UA_Session *session, UA_UInt32 *maxrefs,
+                 const UA_BrowseDescription *descr, UA_BrowseResult *result) {
+    /* Stack-allocate a temporary cp */
+    ContinuationPointEntry *cp = (ContinuationPointEntry*)UA_alloca(sizeof(ContinuationPointEntry));
+    memset(cp, 0, sizeof(ContinuationPointEntry));
+    cp->maxReferences = *maxrefs;
+    cp->browseDescription = *descr; /* Shallow copy. Deep-copy later if we persist the cp. */
+
+    UA_Boolean done = browseWithContinuation(server, session, cp, result);
+
+    /* Exit early if done or an error occurred */
+    if(done || result->statusCode != UA_STATUSCODE_GOOD)
         return;
-    }
-
-    /* Create a new continuation point */
-    if(!done) {
-        if(session->availableContinuationPoints <= 0 ||
-           !(cp = (ContinuationPointEntry *)UA_malloc(sizeof(ContinuationPointEntry)))) {
-            result->statusCode = UA_STATUSCODE_BADNOCONTINUATIONPOINTS;
-            return;
-        }
-        UA_BrowseDescription_copy(descr, &cp->browseDescription);
-        cp->referenceKindIndex = internal_cp->referenceKindIndex;
-        cp->targetIndex = internal_cp->targetIndex;
-        cp->maxReferences = internal_cp->maxReferences;
-
-        /* Create a random bytestring via a Guid */
-        UA_Guid *ident = UA_Guid_new();
-        *ident = UA_Guid_random();
-        cp->identifier.data = (UA_Byte*)ident;
-        cp->identifier.length = sizeof(UA_Guid);
-
-        /* Return the cp identifier */
-        UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
 
-        /* Attach the cp to the session */
-        LIST_INSERT_HEAD(&session->continuationPoints, cp, pointers);
-        --session->availableContinuationPoints;
-    }
+    /* Persist the new continuation point */
+    ContinuationPointEntry *cp2 = NULL;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(session->availableContinuationPoints <= 0 ||
+       !(cp2 = (ContinuationPointEntry *)UA_malloc(sizeof(ContinuationPointEntry)))) {
+        retval = UA_STATUSCODE_BADNOCONTINUATIONPOINTS;
+        goto cleanup;
+    }
+    memset(cp2, 0, sizeof(ContinuationPointEntry));
+    cp2->referenceKindIndex = cp->referenceKindIndex;
+    cp2->targetIndex = cp->targetIndex;
+    cp2->maxReferences = cp->maxReferences;
+    retval = UA_BrowseDescription_copy(descr, &cp2->browseDescription);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    /* Create a random bytestring via a Guid */
+    UA_Guid *ident = UA_Guid_new();
+    if(!ident) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto cleanup;
+    }
+    *ident = UA_Guid_random();
+    cp2->identifier.data = (UA_Byte*)ident;
+    cp2->identifier.length = sizeof(UA_Guid);
+
+    /* Return the cp identifier */
+    retval = UA_ByteString_copy(&cp2->identifier, &result->continuationPoint);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto cleanup;
+
+    /* Attach the cp to the session */
+    LIST_INSERT_HEAD(&session->continuationPoints, cp2, pointers);
+    --session->availableContinuationPoints;
+    return;
+
+ cleanup:
+    if(cp2) {
+        UA_ByteString_deleteMembers(&cp2->identifier);
+        UA_BrowseDescription_deleteMembers(&cp2->browseDescription);
+        UA_free(cp2);
+    }
+    UA_BrowseResult_deleteMembers(result);
+    result->statusCode = retval;
 }
 
 void Service_Browse(UA_Server *server, UA_Session *session,
-                    const UA_BrowseRequest *request,
-                    UA_BrowseResponse *response) {
+                    const UA_BrowseRequest *request, UA_BrowseResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing BrowseRequest");
 
+    /* No views supported at the moment */
     if(!UA_NodeId_isNull(&request->view.viewId)) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADVIEWIDUNKNOWN;
         return;
     }
 
-    if(request->nodesToBrowseSize <= 0) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-        return;
-    }
-
-    if(server->config.maxNodesPerBrowse != 0 &&
-       request->nodesToBrowseSize > server->config.maxNodesPerBrowse) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYOPERATIONS;
-        return;
-    }
-
-    size_t size = request->nodesToBrowseSize;
-    response->results =
-        (UA_BrowseResult*)UA_Array_new(size, &UA_TYPES[UA_TYPES_BROWSERESULT]);
-    if(!response->results) {
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        return;
-    }
-    response->resultsSize = size;
-
-    for(size_t i = 0; i < size; ++i)
-        Service_Browse_single(server, session, NULL, &request->nodesToBrowse[i],
-                              request->requestedMaxReferencesPerNode,
-                              &response->results[i]);
+    UA_UInt32 requestedMaxReferencesPerNode = request->requestedMaxReferencesPerNode;
+    UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_Browse,
+                                       &requestedMaxReferencesPerNode,
+                                       &request->nodesToBrowseSize, &UA_TYPES[UA_TYPES_BROWSEDESCRIPTION],
+                                       &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]);
 }
 
 UA_BrowseResult
-UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs,
-                 const UA_BrowseDescription *descr) {
+UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
-    Service_Browse_single(server, &adminSession, NULL,
-                          descr, maxrefs, &result);
+    Operation_Browse(server, &adminSession, &maxrefs, descr, &result);
     return result;
 }
 
-/* Thread-local variables to pass additional arguments into the operation */
-static UA_THREAD_LOCAL UA_Boolean op_releaseContinuationPoint;
-
 static void
-Operation_BrowseNext(UA_Server *server, UA_Session *session,
-           const UA_ByteString *continuationPoint, UA_BrowseResult *result) {
+Operation_BrowseNext(UA_Server *server, UA_Session *session, UA_Boolean *releaseContinuationPoints,
+                     const UA_ByteString *continuationPoint, UA_BrowseResult *result) {
     /* Find the continuation point */
     ContinuationPointEntry *cp;
     LIST_FOREACH(cp, &session->continuationPoints, pointers) {
@@ -355,11 +333,26 @@ Operation_BrowseNext(UA_Server *server, UA_Session *session,
         return;
     }
 
-    /* Do the work */
-    if(!op_releaseContinuationPoint)
-        Service_Browse_single(server, session, cp, NULL, 0, result);
-    else
+    /* Remove the cp */
+    if(*releaseContinuationPoints) {
         removeCp(cp, session);
+        return;
+    }
+
+    /* Continue browsing */
+    UA_Boolean done = browseWithContinuation(server, session, cp, result);
+
+    if(done) {
+        /* Remove the cp if there are no references left */
+        removeCp(cp, session);
+    } else {
+        /* Return the cp identifier */
+        UA_StatusCode retval = UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_BrowseResult_deleteMembers(result);
+            result->statusCode = retval;
+        }
+    }
 }
 
 void
@@ -368,14 +361,12 @@ Service_BrowseNext(UA_Server *server, UA_Session *session,
                    UA_BrowseNextResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session,
                          "Processing BrowseNextRequest");
-
-    op_releaseContinuationPoint = request->releaseContinuationPoints;
-
+    UA_Boolean releaseContinuationPoints = request->releaseContinuationPoints; /* request is const */
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_BrowseNext,
-                  &request->continuationPointsSize, &UA_TYPES[UA_TYPES_BYTESTRING],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_BrowseNext,
+                                           &releaseContinuationPoints,
+                                           &request->continuationPointsSize, &UA_TYPES[UA_TYPES_BYTESTRING],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSERESULT]);
 }
 
 UA_BrowseResult
@@ -383,8 +374,7 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
                      const UA_ByteString *continuationPoint) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
-    op_releaseContinuationPoint = releaseContinuationPoint;
-    Operation_BrowseNext(server, &adminSession,
+    Operation_BrowseNext(server, &adminSession, &releaseContinuationPoint,
                          continuationPoint, &result);
     return result;
 }
@@ -601,7 +591,7 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
 
 static void
 Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
-                                       const UA_BrowsePath *path,
+                                       void *context, const UA_BrowsePath *path,
                                        UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
@@ -686,7 +676,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
                                        const UA_BrowsePath *browsePath) {
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
-    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
+    Operation_TranslateBrowsePathToNodeIds(server, &adminSession, NULL, browsePath, &result);
     return result;
 }
 
@@ -704,10 +694,9 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
     }
 
     response->responseHeader.serviceResult = 
-        UA_Server_processServiceOperations(server, session,
-                  (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
-                  &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
-                  &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
+        UA_Server_processServiceOperations(server, session, (UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
+                                           NULL, &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
+                                           &response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session,

+ 10 - 10
src/server/ua_session.c

@@ -28,8 +28,8 @@ UA_Session adminSession = {
     UA_MAXCONTINUATIONPOINTS, /* .availableContinuationPoints */
     {NULL}, /* .continuationPoints */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    0, /* .lastSubscriptionID */
-    0, /* .lastSeenSubscriptionID */
+    0, /* .lastSubscriptionId */
+    0, /* .lastSeenSubscriptionId */
     {NULL}, /* .serverSubscriptions */
     {NULL, NULL}, /* .responseQueue */
     0, /* numSubscriptions */
@@ -101,8 +101,8 @@ void UA_Session_addSubscription(UA_Session *session, UA_Subscription *newSubscri
 
 UA_StatusCode
 UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
-                              UA_UInt32 subscriptionID) {
-    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, subscriptionID);
+                              UA_UInt32 subscriptionId) {
+    UA_Subscription *sub = UA_Session_getSubscriptionById(session, subscriptionId);
     if(!sub)
         return UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
 
@@ -125,17 +125,17 @@ UA_Session_getNumSubscriptions( UA_Session *session ) {
 }
 
 UA_Subscription *
-UA_Session_getSubscriptionByID(UA_Session *session, UA_UInt32 subscriptionID) {
+UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId) {
     UA_Subscription *sub;
     LIST_FOREACH(sub, &session->serverSubscriptions, listEntry) {
-        if(sub->subscriptionID == subscriptionID)
+        if(sub->subscriptionId == subscriptionId)
             break;
     }
     return sub;
 }
 
-UA_UInt32 UA_Session_getUniqueSubscriptionID(UA_Session *session) {
-    return ++(session->lastSubscriptionID);
+UA_UInt32 UA_Session_getUniqueSubscriptionId(UA_Session *session) {
+    return ++(session->lastSubscriptionId);
 }
 
 UA_UInt32
@@ -149,7 +149,7 @@ UA_Session_getPublishReq(UA_Session *session) {
 }
 
 void
-UA_Session_removePublishReq( UA_Session *session, UA_PublishResponseEntry* entry){
+UA_Session_removePublishReq( UA_Session *session, UA_PublishResponseEntry* entry) {
     UA_PublishResponseEntry* firstEntry;
     firstEntry = SIMPLEQ_FIRST(&session->responseQueue);
 
@@ -160,7 +160,7 @@ UA_Session_removePublishReq( UA_Session *session, UA_PublishResponseEntry* entry
     }
 }
 
-void UA_Session_addPublishReq( UA_Session *session, UA_PublishResponseEntry* entry){
+void UA_Session_addPublishReq( UA_Session *session, UA_PublishResponseEntry* entry) {
     SIMPLEQ_INSERT_TAIL(&session->responseQueue, entry, listEntry);
     session->numPublishReq++;
 }

+ 6 - 6
src/server/ua_session.h

@@ -51,8 +51,8 @@ typedef struct {
     UA_UInt16 availableContinuationPoints;
     LIST_HEAD(ContinuationPointList, ContinuationPointEntry) continuationPoints;
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    UA_UInt32 lastSubscriptionID;
-    UA_UInt32 lastSeenSubscriptionID;
+    UA_UInt32 lastSubscriptionId;
+    UA_UInt32 lastSeenSubscriptionId;
     LIST_HEAD(UA_ListOfUASubscriptions, UA_Subscription) serverSubscriptions;
     SIMPLEQ_HEAD(UA_ListOfQueuedPublishResponses, UA_PublishResponseEntry) responseQueue;
     UA_UInt32        numSubscriptions;
@@ -61,7 +61,7 @@ typedef struct {
 } UA_Session;
 
 /* Local access to the services (for startup and maintenance) uses this Session
- * with all possible access rights (Session ID: 1) */
+ * with all possible access rights (Session Id: 1) */
 extern UA_Session adminSession;
 
 /**
@@ -87,14 +87,14 @@ UA_UInt32
 UA_Session_getNumSubscriptions(UA_Session *session );
 
 UA_Subscription *
-UA_Session_getSubscriptionByID(UA_Session *session, UA_UInt32 subscriptionID);
+UA_Session_getSubscriptionById(UA_Session *session, UA_UInt32 subscriptionId);
 
 UA_StatusCode
 UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
-                              UA_UInt32 subscriptionID);
+                              UA_UInt32 subscriptionId);
 
 UA_UInt32
-UA_Session_getUniqueSubscriptionID(UA_Session *session);
+UA_Session_getUniqueSubscriptionId(UA_Session *session);
 
 UA_UInt32
 UA_Session_getNumPublishReq(UA_Session *session);

+ 5 - 1
src/server/ua_session_manager.c

@@ -32,6 +32,9 @@ removeSessionCallback(UA_Server *server, void *entry) {
 
 static UA_StatusCode
 removeSession(UA_SessionManager *sm, session_list_entry *sentry) {
+    /* Detach the Session from the SecureChannel */
+    UA_Session_detachFromSecureChannel(&sentry->session);
+
     /* Deactivate the session */
     sentry->session.activated = false;
 
@@ -45,7 +48,8 @@ removeSession(UA_SessionManager *sm, session_list_entry *sentry) {
         return retval; /* Try again next time */
     }
 
-    /* Detach the session and make the capacity available */
+    /* Detach the session from the session manager and make the capacity
+     * available */
     LIST_REMOVE(sentry, pointers);
     UA_atomic_add(&sm->currentSessionCount, (UA_UInt32)-1);
     return UA_STATUSCODE_GOOD;

+ 17 - 17
src/server/ua_subscription.c

@@ -8,7 +8,7 @@
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
 UA_Subscription *
-UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID) {
+UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionId) {
     /* Allocate the memory */
     UA_Subscription *newItem =
         (UA_Subscription*)UA_calloc(1, sizeof(UA_Subscription));
@@ -17,7 +17,7 @@ UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID) {
 
     /* Remaining members are covered by calloc zeroing out the memory */
     newItem->session = session;
-    newItem->subscriptionID = subscriptionID;
+    newItem->subscriptionId = subscriptionId;
     newItem->numMonitoredItems = 0;
     newItem->state = UA_SUBSCRIPTIONSTATE_NORMAL; /* The first publish response is sent immediately */
     TAILQ_INIT(&newItem->retransmissionQueue);
@@ -49,10 +49,10 @@ UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *server)
 
 UA_MonitoredItem *
 UA_Subscription_getMonitoredItem(UA_Subscription *sub,
-                                 UA_UInt32 monitoredItemID) {
+                                 UA_UInt32 monitoredItemId) {
     UA_MonitoredItem *mon;
     LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->itemId == monitoredItemID)
+        if(mon->itemId == monitoredItemId)
             break;
     }
     return mon;
@@ -60,11 +60,11 @@ UA_Subscription_getMonitoredItem(UA_Subscription *sub,
 
 UA_StatusCode
 UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
-                                    UA_UInt32 monitoredItemID) {
+                                    UA_UInt32 monitoredItemId) {
     /* Find the MonitoredItem */
     UA_MonitoredItem *mon;
     LIST_FOREACH(mon, &sub->monitoredItems, listEntry) {
-        if(mon->itemId == monitoredItemID)
+        if(mon->itemId == monitoredItemId)
             break;
     }
     if(!mon)
@@ -251,7 +251,7 @@ void
 UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Publish Callback",
-                         sub->subscriptionID);
+                         sub->subscriptionId);
 
     /* Count the available notifications */
     UA_Boolean moreNotifications = false;
@@ -264,7 +264,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
             return;
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | Sending a KeepAlive",
-                             sub->subscriptionID);
+                             sub->subscriptionId);
     }
 
     /* Check if the securechannel is valid */
@@ -280,7 +280,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | Cannot send a publish "
                              "response since the publish queue is empty",
-                             sub->subscriptionID);
+                             sub->subscriptionId);
         if(sub->state != UA_SUBSCRIPTIONSTATE_LATE) {
             sub->state = UA_SUBSCRIPTIONSTATE_LATE;
         } else {
@@ -288,9 +288,9 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
             if(sub->currentLifetimeCount > sub->lifeTimeCount) {
                 UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                                      "Subscription %u | End of lifetime "
-                                     "for subscription", sub->subscriptionID);
+                                     "for subscription", sub->subscriptionId);
                 UA_Session_deleteSubscription(server, sub->session,
-                                              sub->subscriptionID);
+                                              sub->subscriptionId);
             }
         }
         return;
@@ -306,7 +306,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         if(!retransmission) {
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | Could not allocate memory "
-                                   "for retransmission", sub->subscriptionID);
+                                   "for retransmission", sub->subscriptionId);
             return;
         }
 
@@ -316,7 +316,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | Could not prepare the "
-                                   "notification message", sub->subscriptionID);
+                                   "notification message", sub->subscriptionId);
             UA_free(retransmission);
             return;
         }
@@ -329,7 +329,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
     /* Set up the response */
     response->responseHeader.timestamp = UA_DateTime_now();
-    response->subscriptionId = sub->subscriptionID;
+    response->subscriptionId = sub->subscriptionId;
     response->moreNotifications = moreNotifications;
     message->publishTime = response->responseHeader.timestamp;
 
@@ -365,7 +365,7 @@ UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
     /* Send the response */
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Sending out a publish response "
-                         "with %u notifications", sub->subscriptionID,
+                         "with %u notifications", sub->subscriptionId,
                          (UA_UInt32)notifications);
     UA_SecureChannel_sendSymmetricMessage(sub->session->header.channel, pre->requestId,
                                           UA_MESSAGETYPE_MSG, response,
@@ -443,7 +443,7 @@ UA_StatusCode
 Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub) {
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Register subscription "
-                         "publishing callback", sub->subscriptionID);
+                         "publishing callback", sub->subscriptionId);
 
     if(sub->publishCallbackIsRegistered)
         return UA_STATUSCODE_GOOD;
@@ -464,7 +464,7 @@ UA_StatusCode
 Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub) {
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Unregister subscription "
-                         "publishing callback", sub->subscriptionID);
+                         "publishing callback", sub->subscriptionId);
 
     if(!sub->publishCallbackIsRegistered)
         return UA_STATUSCODE_GOOD;

+ 5 - 5
src/server/ua_subscription.h

@@ -38,7 +38,7 @@ typedef struct UA_MonitoredItem {
     UA_TimestampsToReturn timestampsToReturn;
     UA_MonitoringMode monitoringMode;
     UA_NodeId monitoredNodeId;
-    UA_UInt32 attributeID;
+    UA_UInt32 attributeId;
     UA_UInt32 clientHandle;
     UA_Double samplingInterval; // [ms]
     UA_UInt32 currentQueueSize;
@@ -91,7 +91,7 @@ struct UA_Subscription {
     UA_UInt32 lifeTimeCount;
     UA_UInt32 maxKeepAliveCount;
     UA_Double publishingInterval; /* in ms */
-    UA_UInt32 subscriptionID;
+    UA_UInt32 subscriptionId;
     UA_UInt32 notificationsPerPublish;
     UA_Boolean publishingEnabled;
     UA_UInt32 priority;
@@ -119,14 +119,14 @@ struct UA_Subscription {
     UA_UInt32 retransmissionQueueSize;
 };
 
-UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionID);
+UA_Subscription * UA_Subscription_new(UA_Session *session, UA_UInt32 subscriptionId);
 void UA_Subscription_deleteMembers(UA_Subscription *subscription, UA_Server *server);
 UA_StatusCode Subscription_registerPublishCallback(UA_Server *server, UA_Subscription *sub);
 UA_StatusCode Subscription_unregisterPublishCallback(UA_Server *server, UA_Subscription *sub);
 
 UA_StatusCode
 UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
-                                    UA_UInt32 monitoredItemID);
+                                    UA_UInt32 monitoredItemId);
 
 void
 UA_Subscription_addMonitoredItem(UA_Subscription *sub,
@@ -135,7 +135,7 @@ UA_UInt32
 UA_Subscription_getNumMonitoredItems(UA_Subscription *sub);
 
 UA_MonitoredItem *
-UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemID);
+UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemId);
 
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
 

+ 7 - 7
src/server/ua_subscription_datachange.c

@@ -67,7 +67,7 @@ ensureSpaceInMonitoredItemQueue(UA_MonitoredItem *mon, MonitoredItem_queuedValue
     UA_free(queueItem);
     --mon->currentQueueSize;
 
-    if(mon->maxQueueSize > 1){
+    if(mon->maxQueueSize > 1) {
         newQueueItem->value.hasStatus = true;
         newQueueItem->value.status = UA_STATUSCODE_INFOTYPE_DATAVALUE | UA_STATUSCODE_INFOBITS_OVERFLOW;
     }
@@ -155,7 +155,7 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
         UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                "Subscription %u | MonitoredItem %i | "
                                "Item for the publishing queue could not be allocated",
-                               sub->subscriptionID, monitoredItem->itemId);
+                               sub->subscriptionId, monitoredItem->itemId);
         return false;
     }
 
@@ -166,7 +166,7 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | MonitoredItem %i | "
                                    "ByteString to compare values could not be created",
-                                   sub->subscriptionID, monitoredItem->itemId);
+                                   sub->subscriptionId, monitoredItem->itemId);
             UA_free(newQueueItem);
             return false;
         }
@@ -181,7 +181,7 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
             UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
                                    "Subscription %u | MonitoredItem %i | "
                                    "Item for the publishing queue could not be prepared",
-                                   sub->subscriptionID, monitoredItem->itemId);
+                                   sub->subscriptionId, monitoredItem->itemId);
             UA_free(newQueueItem);
             return false;
         }
@@ -194,7 +194,7 @@ sampleCallbackWithValue(UA_Server *server, UA_Subscription *sub,
 
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | MonitoredItem %u | Sampled a new value",
-                         sub->subscriptionID, monitoredItem->itemId);
+                         sub->subscriptionId, monitoredItem->itemId);
 
     /* Replace the encoding for comparison */
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
@@ -215,7 +215,7 @@ UA_MoniteredItem_SampleCallback(UA_Server *server,
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Subscription %u | MonitoredItem %i | "
                              "Not a data change notification",
-                             sub->subscriptionID, monitoredItem->itemId);
+                             sub->subscriptionId, monitoredItem->itemId);
         return;
     }
 
@@ -223,7 +223,7 @@ UA_MoniteredItem_SampleCallback(UA_Server *server,
     UA_ReadValueId rvid;
     UA_ReadValueId_init(&rvid);
     rvid.nodeId = monitoredItem->monitoredNodeId;
-    rvid.attributeId = monitoredItem->attributeID;
+    rvid.attributeId = monitoredItem->attributeId;
     rvid.indexRange = monitoredItem->indexRange;
     UA_DataValue value =
         UA_Server_readWithSession(server, sub->session,

+ 1 - 1
src/ua_connection.c

@@ -219,7 +219,7 @@ UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application
             return UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
 
         /* round always to upper value to avoid timeout to be set to 0
-         * if (maxDate - now) < (UA_DATETIME_MSEC/2) */
+         * if(maxDate - now) < (UA_DATETIME_MSEC/2) */
         timeout = (UA_UInt32)(((maxDate - now) + (UA_DATETIME_MSEC - 1)) / UA_DATETIME_MSEC);
     }
     return retval;

+ 1 - 0
src/ua_securechannel.h

@@ -19,6 +19,7 @@ extern "C" {
 #define UA_SECURE_CONVERSATION_MESSAGE_HEADER_LENGTH 12
 #define UA_SECURE_MESSAGE_HEADER_LENGTH 24
 
+/* Thread-local variables to force failure modes during testing */
 #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
 extern UA_THREAD_LOCAL UA_StatusCode decrypt_verifySignatureFailure;
 extern UA_THREAD_LOCAL UA_StatusCode sendAsym_sendFailure;

+ 1 - 1
src/ua_types.c

@@ -565,7 +565,7 @@ copySubString(const UA_String *src, UA_String *dst,
 UA_StatusCode
 UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
                      const UA_NumericRange range) {
-    if (!src->type)
+    if(!src->type)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     bool isScalar = UA_Variant_isScalar(src);
     bool stringLike = isStringLike(src->type);

Fichier diff supprimé car celui-ci est trop grand
+ 508 - 589
src/ua_types_encoding_binary.c


+ 5 - 6
src/ua_util.h

@@ -19,20 +19,19 @@ extern "C" {
 
 /* Thread-Local Storage
  * --------------------
- * Thread-local variables are always enabled. Also when the library is built
- * with ``UA_ENABLE_MULTITHREADING`` disabled. Otherwise, if multiple clients
- * run in separate threads, race conditions may occur via global variables in
- * the encoding layer. */
+ * Thread-local storage is not required by the main library functionality. It is
+ * only used for some testing strategies. ``UA_THREAD_LOCAL`` is empty if the
+ * feature is not available. */
+
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 # define UA_THREAD_LOCAL _Thread_local /* C11 */
 #elif defined(__cplusplus) && __cplusplus > 199711L
 # define UA_THREAD_LOCAL thread_local /* C++11 */
-#elif defined(__GNUC__) && !defined(_WRS_KERNEL) //defining __thread gave error of missing __tls_lookup in VxWorks
+#elif defined(__GNUC__)
 # define UA_THREAD_LOCAL __thread /* GNU extension */
 #elif defined(_MSC_VER)
 # define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
 #else
-# warning The compiler does not support thread-local variables
 # define UA_THREAD_LOCAL
 #endif
 

+ 4 - 5
tests/check_types_builtin.c

@@ -411,7 +411,7 @@ START_TEST(UA_Variant_decodeWithOutArrayFlagSetShallSetVTAndAllocateMemoryForArr
     ck_assert_uint_eq(pos, 5);
     ck_assert_uint_eq(pos, UA_calcSizeBinary(&dst, &UA_TYPES[UA_TYPES_VARIANT]));
     //ck_assert_ptr_eq((const void *)dst.type, (const void *)&UA_TYPES[UA_TYPES_INT32]); //does not compile in gcc 4.6
-    ck_assert_int_eq((uintptr_t)dst.type, (uintptr_t)&UA_TYPES[UA_TYPES_INT32]); 
+    ck_assert_int_eq((uintptr_t)dst.type, (uintptr_t)&UA_TYPES[UA_TYPES_INT32]);
     ck_assert_int_eq(dst.arrayLength, 0);
     ck_assert_int_ne((uintptr_t)dst.data, 0);
     UA_assert(dst.data != NULL); /* repeat the previous argument so that clang-analyzer is happy */
@@ -810,8 +810,8 @@ START_TEST(UA_Float_encodeShallWorkOnExample) {
         {0x00, 0x00, 0x80, 0x7F}, // INF
         {0x00, 0x00, 0x80, 0xFF} // -INF
     };
-#ifdef _WIN32
-    // on WIN32 -NAN is encoded differently
+#if defined(_WIN32) || defined(__TINYC__)
+    // on WIN32 or TinyCC -NAN is encoded differently
     result[4][3] = 127;
 #endif
 
@@ -1176,8 +1176,7 @@ END_TEST
 
 START_TEST(UA_QualifiedName_copyShallWorkOnInputExample) {
     // given
-    UA_String srcName = (UA_String){8, (UA_Byte*)"tEsT123!"};
-    UA_QualifiedName src = {5, srcName};
+    UA_QualifiedName src = UA_QUALIFIEDNAME(5, "tEsT123!");
     UA_QualifiedName dst;
 
     // when

+ 5 - 0
tests/fuzz/fuzz_binary_message.cc

@@ -17,6 +17,11 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
     UA_Connection c = createDummyConnection(65535, NULL);
     UA_ServerConfig *config = UA_ServerConfig_new_default();
     UA_Server *server = UA_Server_new(config);
+    if (server == NULL) {
+        UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
+                     "Could not create server instance using UA_Server_new");
+        return 1;
+    }
 
     // we need to copy the message because it will be freed in the processing function
     UA_ByteString msg = UA_ByteString();

+ 20 - 21
tests/server/check_discovery.c

@@ -372,22 +372,23 @@ GetEndpointsAndCheck(const char* discoveryUrl, const char* filterTransportProfil
 
 // Test if discovery server lists himself as registered server if it is filtered by his uri
 START_TEST(Client_filter_discovery) {
-    const UA_String expectedUris[] = {UA_STRING("urn:open62541.test.local_discovery_server")};
+    UA_String expectedUris[1];
+    expectedUris[0] = UA_STRING("urn:open62541.test.local_discovery_server");
     FindAndCheck(expectedUris, 1, NULL, NULL, "urn:open62541.test.local_discovery_server", NULL);
 }
 END_TEST
 
 // Test if server filters locale
 START_TEST(Client_filter_locale) {
-    const UA_String expectedUris[] = {
-        UA_STRING("urn:open62541.test.local_discovery_server"),
-        UA_STRING("urn:open62541.test.server_register")
-    };
-    const UA_String expectedNames[] = {
-        UA_STRING("LDS Server"),
-        UA_STRING("Anmeldungsserver")
-    };
-    const UA_String expectedLocales[] = {UA_STRING("en"), UA_STRING("de")};
+    UA_String expectedUris[2];
+    expectedUris[0] = UA_STRING("urn:open62541.test.local_discovery_server"),
+    expectedUris[1] = UA_STRING("urn:open62541.test.server_register");
+    UA_String expectedNames[2];
+    expectedNames[0]= UA_STRING("LDS Server");
+    expectedNames[1]= UA_STRING("Anmeldungsserver");
+    UA_String expectedLocales[2];
+    expectedLocales[0] = UA_STRING("en");
+    expectedLocales[1] = UA_STRING("de");
     // even if we request en-US, the server will return de-DE because it only has that name.
     FindAndCheck(expectedUris, 2, expectedLocales, expectedNames, NULL, "en");
 
@@ -425,17 +426,15 @@ END_TEST
 
 // Test if filtering with uris works
 START_TEST(Client_find_filter) {
-    const UA_String expectedUris[] = {
-        UA_STRING("urn:open62541.test.server_register")
-    };
+    UA_String expectedUris[1];
+    expectedUris[0] = UA_STRING("urn:open62541.test.server_register");
     FindAndCheck(expectedUris, 1, NULL, NULL, "urn:open62541.test.server_register", NULL);
 }
 END_TEST
 
 START_TEST(Client_get_endpoints) {
-    const UA_String  expectedEndpoints[] ={
-        UA_STRING("opc.tcp://localhost:4840")
-    };
+    UA_String  expectedEndpoints[1];
+    expectedEndpoints[0] = UA_STRING("opc.tcp://localhost:4840");
 
     // general check if expected endpoints are returned
     GetEndpointsAndCheck("opc.tcp://localhost:4840", NULL,expectedEndpoints, 1);
@@ -455,17 +454,17 @@ END_TEST
 
 // Test if discovery server lists himself as registered server, before any other registration.
 START_TEST(Client_find_discovery) {
-    const UA_String expectedUris[] = {UA_STRING("urn:open62541.test.local_discovery_server")};
+    UA_String expectedUris[1];
+    expectedUris[0] = UA_STRING("urn:open62541.test.local_discovery_server");
     FindAndCheck(expectedUris, 1, NULL, NULL, NULL, NULL);
 }
 END_TEST
 
 // Test if registered server is returned from LDS
 START_TEST(Client_find_registered) {
-    const UA_String expectedUris[2] = {
-        UA_STRING("urn:open62541.test.local_discovery_server"),
-        UA_STRING("urn:open62541.test.server_register")
-    };
+    UA_String expectedUris[2];
+    expectedUris[0] = UA_STRING("urn:open62541.test.local_discovery_server");
+    expectedUris[1] = UA_STRING("urn:open62541.test.server_register");
     FindAndCheck(expectedUris, 2, NULL, NULL, NULL, NULL);
 }
 END_TEST

+ 15 - 18
tests/server/check_server_userspace.c

@@ -107,24 +107,21 @@ START_TEST(Server_forEachChildNodeCall) {
 
     /* List all the children/references of the objects folder
      * The forEachChildNodeCall has to hit all of them */
-    struct nodeIterData objectsFolderChildren[] = {
-        {
-                UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                UA_FALSE,
-                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                UA_FALSE
-        },{
-                UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                UA_TRUE,
-                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                UA_FALSE
-        },{
-                UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE),
-                UA_FALSE,
-                UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-                UA_FALSE
-        }
-    };
+    struct nodeIterData objectsFolderChildren[3];
+    objectsFolderChildren[0].id = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+    objectsFolderChildren[0].isInverse = UA_FALSE;
+    objectsFolderChildren[0].referenceTypeID = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    objectsFolderChildren[0].hit = UA_FALSE;
+
+    objectsFolderChildren[1].id = UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER);
+    objectsFolderChildren[1].isInverse = UA_TRUE;
+    objectsFolderChildren[1].referenceTypeID = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    objectsFolderChildren[1].hit = UA_FALSE;
+
+    objectsFolderChildren[2].id = UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE);
+    objectsFolderChildren[2].isInverse = UA_FALSE;
+    objectsFolderChildren[2].referenceTypeID = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
+    objectsFolderChildren[2].hit = UA_FALSE;
 
     UA_StatusCode retval = UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, &objectsFolderChildren);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);

+ 33 - 34
tools/appveyor/build.ps1

@@ -10,25 +10,24 @@ Copy-Item LICENSE pack
 Copy-Item AUTHORS pack
 Copy-Item README.md pack
 
-Write-Host -ForegroundColor Green "`n###################################################################"
-Write-Host -ForegroundColor Green "`n##### Building Documentation on $env:CC_NAME #####`n"
-New-Item -ItemType directory -Path build
-cd build
-& cmake -DMIKTEX_BINARY_PATH=c:\miktex\texmfs\install\miktex\bin -DCMAKE_BUILD_TYPE=Release -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DUA_BUILD_EXAMPLES:BOOL=OFF -G"$env:CC_NAME" ..
-& cmake --build . --target doc_latex
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make doc_latex. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-& cmake --build . --target doc_pdf
-if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
-	Write-Host -ForegroundColor Red "`n`n*** Make doc_pdf. Exiting ... ***"
-	exit $LASTEXITCODE
-}
-cd ..
-Move-Item -Path "build\doc_latex\open62541.pdf" -Destination pack\
-Remove-Item -Path build -Recurse
-
+# Write-Host -ForegroundColor Green "`n###################################################################"
+# Write-Host -ForegroundColor Green "`n##### Building Documentation on $env:CC_NAME #####`n"
+# New-Item -ItemType directory -Path build
+# cd build
+# & cmake -DMIKTEX_BINARY_PATH=c:\miktex\texmfs\install\miktex\bin -DCMAKE_BUILD_TYPE=Release -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -DUA_BUILD_EXAMPLES:BOOL=OFF -G"$env:CC_NAME" ..
+# & cmake --build . --target doc_latex
+# if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+# 	Write-Host -ForegroundColor Red "`n`n*** Make doc_latex. Exiting ... ***"
+# 	exit $LASTEXITCODE
+# }
+# & cmake --build . --target doc_pdf
+# if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+# 	Write-Host -ForegroundColor Red "`n`n*** Make doc_pdf. Exiting ... ***"
+# 	exit $LASTEXITCODE
+# }
+# cd ..
+# Move-Item -Path "build\doc_latex\open62541.pdf" -Destination pack\
+# Remove-Item -Path build -Recurse -Force
 
 Write-Host -ForegroundColor Green "`n###################################################################"
 Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME #####`n"
@@ -41,8 +40,7 @@ if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
 	exit $LASTEXITCODE
 }
 cd ..
-Remove-Item -Path build -Recurse
-
+Remove-Item -Path build -Recurse -Force
 
 Write-Host -ForegroundColor Green "`n###################################################################"
 Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
@@ -55,8 +53,7 @@ if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
 	exit $LASTEXITCODE
 }
 cd ..
-Remove-Item -Path build -Recurse
-
+Remove-Item -Path build -Recurse -Force
 
 Write-Host -ForegroundColor Green "`n###################################################################"
 Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation #####`n"
@@ -80,9 +77,8 @@ if ($env:CC_SHORTNAME -eq "mingw") {
 	Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.lib" -Destination pack_tmp\
 }
 & 7z a -tzip open62541-$env:CC_SHORTNAME-static.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
-Remove-Item -Path pack_tmp -Recurse
-Remove-Item -Path build -Recurse
-
+Remove-Item -Path pack_tmp -Recurse -Force
+Remove-Item -Path build -Recurse -Force
 
 Write-Host -ForegroundColor Green "`n###################################################################"
 Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with amalgamation and .dll #####`n"
@@ -108,24 +104,27 @@ if ($env:CC_SHORTNAME -eq "mingw") {
 	Move-Item -Path "build\$env:OUT_DIR_LIB\open62541.pdb" -Destination pack_tmp\
 }
 & 7z a -tzip open62541-$env:CC_SHORTNAME-dynamic.zip "$env:APPVEYOR_BUILD_FOLDER\pack\*" "$env:APPVEYOR_BUILD_FOLDER\pack_tmp\*"
-Remove-Item -Path pack_tmp -Recurse
-Remove-Item -Path build -Recurse
+Remove-Item -Path pack_tmp -Recurse -Force
+Remove-Item -Path build -Recurse -Force
 
+# Only execute unit tests on vs2015 to save compilation time
 if ($env:CC_SHORTNAME -eq "vs2015") {
-	# Only execute unit tests on vs2015 to save compilation time
-	New-Item -ItemType directory -Path "build"
-	cd build
 	Write-Host -ForegroundColor Green "`n###################################################################"
 	Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with unit tests #####`n"
-	& cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=OFF -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON  -DCMAKE_LIBRARY_PATH=c:\check\lib -DCMAKE_INCLUDE_PATH=c:\check\include -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
+	New-Item -ItemType directory -Path "build"
+	cd build
+	& cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=OFF -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON -DCMAKE_LIBRARY_PATH=c:\check\lib -DCMAKE_INCLUDE_PATH=c:\check\include -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
 	Invoke-Expression $make_cmd
     if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
     	Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
     	exit $LASTEXITCODE
     }
 	& cmake --build . --target test-verbose --config debug
+	if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
+		Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"
+		exit $LASTEXITCODE
+	}
 }
 
-
 # do not cache log
-Remove-Item -Path c:\miktex\texmfs\data\miktex\log -Recurse
+Remove-Item -Path c:\miktex\texmfs\data\miktex\log -Recurse -Force

+ 0 - 2
tools/appveyor/install.ps1

@@ -2,7 +2,6 @@ $ErrorActionPreference = "Stop"
 
 & git submodule --quiet update --init --recursive
 
-
 Write-Host -ForegroundColor Green "`n### Installing CMake and python ###`n"
 & cinst --no-progress cmake python2
 
@@ -24,7 +23,6 @@ if (-not (Test-Path "c:\miktex\texmfs\install\miktex\bin\pdflatex.exe")) {
 Write-Host -ForegroundColor Green "`n### Installing graphviz ###`n"
 & cinst --no-progress graphviz.portable
 
-
 if ($env:CC_SHORTNAME -eq "vs2015") {
 	Write-Host -ForegroundColor Green "`n### Installing libcheck ###`n"
 	& appveyor DownloadFile https://github.com/Pro/check/releases/download/0.12.0_win/check.zip

+ 12 - 0
tools/travis/travis_linux_before_install.sh

@@ -18,6 +18,18 @@ if [ -z ${DOCKER+x} ]; then
 		sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang-3.9 clang-tidy-3.9 libfuzzer-3.9-dev
 	fi
 
+	if [ "$CC" = "tcc" ]; then
+		mkdir tcc_install && cd tcc_install
+		wget https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2
+		tar xvf tcc-0.9.27.tar.bz2
+		cd tcc-0.9.27
+		./configure
+		make
+		sudo make install
+		cd ../..
+		rm -rf tcc_install
+	fi
+
 	sudo add-apt-repository -y ppa:lttng/ppa
 	sudo apt-get update -qq
 	sudo apt-get install -y liburcu4 liburcu-dev

+ 38 - 33
tools/travis/travis_linux_script.sh

@@ -162,13 +162,16 @@ else
     make -j
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
-    echo -en 'travis_fold:end:script.build.shared_libs\\r'echo -e "\r\n==Compile multithreaded version==" && echo -en 'travis_fold:start:script.build.multithread\\r'
-    mkdir -p build && cd build
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLES=ON ..
-    make -j
-    if [ $? -ne 0 ] ; then exit 1 ; fi
-    cd .. && rm build -rf
-    echo -en 'travis_fold:end:script.build.multithread\\r'
+
+	if [ "$CC" != "tcc" ]; then
+		echo -en 'travis_fold:end:script.build.shared_libs\\r'echo -e "\r\n==Compile multithreaded version==" && echo -en 'travis_fold:start:script.build.multithread\\r'
+		mkdir -p build && cd build
+		cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLES=ON ..
+		make -j
+		if [ $? -ne 0 ] ; then exit 1 ; fi
+		cd .. && rm build -rf
+		echo -en 'travis_fold:end:script.build.multithread\\r'
+	fi
 
     echo -e "\r\n== Compile without discovery version ==" && echo -en 'travis_fold:start:script.build.unit_test_valgrind\\r'
     mkdir -p build && cd build
@@ -184,14 +187,15 @@ else
     if [ $? -ne 0 ] ; then exit 1 ; fi
     cd .. && rm build -rf
 
-
-    echo -e "\r\n== Compile multithreaded version with discovery =="
-    mkdir -p build && cd build
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_EXAMPLES=ON ..
-    make -j
-    if [ $? -ne 0 ] ; then exit 1 ; fi
-    cd .. && rm build -rf
-    echo -en 'travis_fold:end:script.build.multithread\\r'
+	if [ "$CC" != "tcc" ]; then
+		echo -e "\r\n== Compile multithreaded version with discovery =="
+		mkdir -p build && cd build
+		cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_MULTITHREADING=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_EXAMPLES=ON ..
+		make -j
+		if [ $? -ne 0 ] ; then exit 1 ; fi
+		cd .. && rm build -rf
+		echo -en 'travis_fold:end:script.build.multithread\\r'
+	fi
 
     echo -e "\r\n== Unit tests (full NS0) ==" && echo -en 'travis_fold:start:script.build.unit_test_ns0_full\\r'
     mkdir -p build && cd build
@@ -204,22 +208,23 @@ else
     cd .. && rm build -rf
     echo -en 'travis_fold:end:script.build.unit_test_ns0\\r'
 
-    echo -e "\r\n== Unit tests (minimal NS0) ==" && echo -en 'travis_fold:start:script.build.unit_test_ns0_minimal\\r'
-    mkdir -p build && cd build
-    cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON \
-    -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
-    -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON ..
-    make -j && make test ARGS="-V"
-    if [ $? -ne 0 ] ; then exit 1 ; fi
-    echo -en 'travis_fold:end:script.build.unit_test_ns0_minimal\\r'
-
-    # only run coveralls on main repo, otherwise it fails uploading the files
-    echo -e "\r\n== -> Current repo: ${TRAVIS_REPO_SLUG} =="
-    if [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
-        echo -en "\r\n==   Building coveralls for ${TRAVIS_REPO_SLUG} ==" && echo -en 'travis_fold:start:script.build.coveralls\\r'
-        coveralls -E '.*/build/CMakeFiles/.*' -E '.*/examples/.*' -E '.*/tests/.*' -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../ || true # ignore result since coveralls is unreachable from time to time
-        echo -en 'travis_fold:end:script.build.coveralls\\r'
-    fi
-    cd .. && rm build -rf
-
+	if [ "$CC" != "tcc" ]; then
+        echo -e "\r\n== Unit tests (minimal NS0) ==" && echo -en 'travis_fold:start:script.build.unit_test_ns0_minimal\\r'
+        mkdir -p build && cd build
+        cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON \
+              -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_DISCOVERY=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
+              -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=ON -DUA_ENABLE_UNIT_TESTS_MEMCHECK=ON ..
+        make -j && make test ARGS="-V"
+        if [ $? -ne 0 ] ; then exit 1 ; fi
+        echo -en 'travis_fold:end:script.build.unit_test_ns0_minimal\\r'
+
+        # only run coveralls on main repo, otherwise it fails uploading the files
+        echo -e "\r\n== -> Current repo: ${TRAVIS_REPO_SLUG} =="
+        if [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
+            echo -en "\r\n==   Building coveralls for ${TRAVIS_REPO_SLUG} ==" && echo -en 'travis_fold:start:script.build.coveralls\\r'
+            coveralls -E '.*/build/CMakeFiles/.*' -E '.*/examples/.*' -E '.*/tests/.*' -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../ || true # ignore result since coveralls is unreachable from time to time
+            echo -en 'travis_fold:end:script.build.coveralls\\r'
+        fi
+        cd .. && rm build -rf
+	fi
 fi