Browse Source

Merge branch 'master' of https://github.com/open62541/open62541

FlorianPalm 8 years ago
parent
commit
7b273f046a

+ 1 - 1
.travis.yml

@@ -93,7 +93,7 @@ script:
 - if [ ${TRAVIS_OS_NAME} == "osx" ]; then sh ./tools/travis/travis_osx_script.sh; fi
 
 after_success:
-- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master" && ${TRAVIS_PULL_REQUEST} == "false" ) ]]; then sh ./tools/travis/travis_push_doc.sh; fi
+- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "0.2" && ${TRAVIS_PULL_REQUEST} == "false" ) ]]; then sh ./tools/travis/travis_push_doc.sh; fi
 - if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master" && ${TRAVIS_PULL_REQUEST} == "false" ) ]]; then sh ./tools/travis/travis_push_coverity.sh; fi
 - if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" ) ]]; then sh ./tools/travis/travis_push_release.sh; fi
 

+ 0 - 1
CMakeLists.txt

@@ -26,7 +26,6 @@ else()
     set(GIT_COMMIT_ID "unknown--no-git-found")
     message(STATUS "Git not found. Build will not contain git revision info." )
 endif()
-add_definitions("-DVERSION=${GIT_COMMIT_ID}")
 
 message(STATUS "Git version: "  ${GIT_COMMIT_ID})
 

+ 2 - 2
doc/conf.py

@@ -47,7 +47,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'open62541'
-copyright = u'2015, The open62541 authors'
+copyright = u'2016, The open62541 authors'
 author = u'The open62541 authors'
 
 # The version info for the project you're documenting, acts as replacement for
@@ -55,7 +55,7 @@ author = u'The open62541 authors'
 # built documents.
 #
 # The short X.Y version.
-version = '0'
+version = '0.2'
 # The full version, including alpha/beta/rc tags.
 release = '1'
 

+ 55 - 51
doc/tutorial_client_firstSteps.rst

@@ -22,12 +22,12 @@ As a recap, your directory structure should now look like this::
   │   ├── ua_config.h
   │   ├── ua_config.h.in
   │   ├── ua_connection.h
+  │   ├── ua_constants.h
   │   ├── ua_job.h
   │   ├── ua_log.h
   │   ├── ua_nodeids.h
   │   ├── ua_server_external_ns.h
   │   ├── ua_server.h
-  │   ├── ua_constants.h
   │   ├── ua_transport_generated_encoding_binary.h
   │   ├── ua_transport_generated.h
   │   ├── ua_types_generated_encoding_binary.h
@@ -49,10 +49,11 @@ To create a really basic client, navigate back into the myApp folder from the pr
     #include "ua_server.h"
     #include "logger_stdout.h"
     #include "networklayer_tcp.h"
+    #include "ua_config_standard.h"
     
     int main(void) {
-      UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout);
-      UA_StatusCode retval = UA_Client_connect(client, UA_ClientConnectionTCP,"opc.tcp://localhost:16664");
+      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+      UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
       if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         return retval;
@@ -131,46 +132,47 @@ Let us extend the client with with an action reading node's value:
 .. code-block:: c
 
     #include <stdio.h>
-    #include <stdio.h>
     
     #include "ua_types.h"
     #include "ua_server.h"
-    #include "logger_stdout.h"
+    //#include "logger_stdout.h"
     #include "networklayer_tcp.h"
+    #include "ua_config_standard.h"
+    #include <inttypes.h>
     
     int main(void) {
-      UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout);
-      UA_StatusCode retval = UA_Client_connect(client, UA_ClientConnectionTCP,"opc.tcp://localhost:16664");
+      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+      UA_StatusCode retval = UA_Client_connect(client,"opc.tcp://localhost:16664");
       if(retval != UA_STATUSCODE_GOOD) {
         UA_Client_delete(client);
         return retval;
       }
-    
+      
       //variable to store data
       UA_DateTime raw_date = 0;
-    
+      
       UA_ReadRequest rReq;
       UA_ReadRequest_init(&rReq);
       rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
       rReq.nodesToReadSize = 1;
       rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
       rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
-    
+      
       UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
       if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
-         rResp.resultsSize > 0 && rResp.results[0].hasValue &&
-         UA_Variant_isScalar(&rResp.results[0].value) &&
-         rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
-             raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-             printf("raw date is: %" PRId64 "\n", raw_date);
+        rResp.resultsSize > 0 && rResp.results[0].hasValue &&
+        UA_Variant_isScalar(&rResp.results[0].value) &&
+        rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
+         raw_date = *(UA_DateTime*)rResp.results[0].value.data;
+         printf("raw date is: %" PRId64 "\n", raw_date);
       }
-    
+      
       UA_ReadRequest_deleteMembers(&rReq);
       UA_ReadResponse_deleteMembers(&rResp);
-    
+      
       UA_Client_disconnect(client);
       UA_Client_delete(client);
-      return 0;
+      return UA_STATUSCODE_GOOD;
     }
 
 You should see raw time in milliseconds since January 1, 1601 UTC midnight::
@@ -188,46 +190,48 @@ As the last step for this tutorial, we are going to convert the raw date value i
     
     #include "ua_types.h"
     #include "ua_server.h"
-    #include "logger_stdout.h"
+    //#include "logger_stdout.h"
     #include "networklayer_tcp.h"
+    #include "ua_config_standard.h"
+    #include <inttypes.h>
     
     int main(void) {
-        UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout);
-        UA_StatusCode retval = UA_Client_connect(client, UA_ClientConnectionTCP, "opc.tcp://localhost:16664");
-        if(retval != UA_STATUSCODE_GOOD) {
-          UA_Client_delete(client);
-          return retval;
-        }
-      
-        //variables to store data
-        UA_DateTime raw_date = 0;
-        UA_String string_date;
+      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+      UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
+      if(retval != UA_STATUSCODE_GOOD) {
+        UA_Client_delete(client);
+        return retval;
+      }
       
-        UA_ReadRequest rReq;
-        UA_ReadRequest_init(&rReq);
-        rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
-        rReq.nodesToReadSize = 1;
-        rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
-        rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+      //variables to store data
+      UA_DateTime raw_date = 0;
+      UA_String string_date;
+    
+      UA_ReadRequest rReq;
+      UA_ReadRequest_init(&rReq);
+      rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
+      rReq.nodesToReadSize = 1;
+      rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, 2258);
+      rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
       
-        UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
-        if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
-            rResp.resultsSize > 0 && rResp.results[0].hasValue &&
-            UA_Variant_isScalar(&rResp.results[0].value) &&
-            rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
-          raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-          printf("raw date is: %" PRId64 "\n", raw_date);
-          string_date = UA_DateTime_toString(raw_date);
-          printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
-        }
+      UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
+      if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD &&
+          rResp.resultsSize > 0 && rResp.results[0].hasValue &&
+          UA_Variant_isScalar(&rResp.results[0].value) &&
+          rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
+        raw_date = *(UA_DateTime*)rResp.results[0].value.data;
+        printf("raw date is: %" PRId64 "\n", raw_date);
+        string_date = UA_DateTime_toString(raw_date);
+        printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
+      }
       
-        UA_ReadRequest_deleteMembers(&rReq);
-        UA_ReadResponse_deleteMembers(&rResp);
-        UA_String_deleteMembers(&string_date);
+      UA_ReadRequest_deleteMembers(&rReq);
+      UA_ReadResponse_deleteMembers(&rResp);
+      UA_String_deleteMembers(&string_date);
       
-        UA_Client_disconnect(client);
-        UA_Client_delete(client);
-        return UA_STATUSCODE_GOOD;
+      UA_Client_disconnect(client);
+      UA_Client_delete(client);
+      return UA_STATUSCODE_GOOD;
     }
 
 Note that this file can be found as "examples/client_firstSteps.c" in the repository.

+ 13 - 13
doc/tutorial_server_firstSteps.rst

@@ -186,24 +186,24 @@ We will also make a slight change to our server: We want it to exit cleanly when
 
     UA_Boolean running = true;
     static void stopHandler(int signal) {
-        running = false;
+      running = false;
     }
     
     int main(void) {
-        signal(SIGINT,  stopHandler);
-        signal(SIGTERM, stopHandler);
+      signal(SIGINT,  stopHandler);
+      signal(SIGTERM, stopHandler);
     
-        UA_ServerConfig config = UA_ServerConfig_standard;
-        UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664, Logger_Stdout);
-        config.logger = Logger_Stdout;
-        config.networkLayers = &nl;
-        config.networkLayersSize = 1;
-        UA_Server *server = UA_Server_new(config);
+      UA_ServerConfig config = UA_ServerConfig_standard;
+      UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664, Logger_Stdout);
+      config.logger = Logger_Stdout;
+      config.networkLayers = &nl;
+      config.networkLayersSize = 1;
+      UA_Server *server = UA_Server_new(config);
     
-        UA_StatusCode retval = UA_Server_run(server, &running);
-        UA_Server_delete(server);
-        nl.deleteMembers(&nl);
-        return retval;
+      UA_StatusCode retval = UA_Server_run(server, &running);
+      UA_Server_delete(server);
+      nl.deleteMembers(&nl);
+      return retval;
     }
 
 Note that this file can be found as "examples/server_firstSteps.c" in the repository.

+ 2 - 2
doc/tutorial_server_variables.rst

@@ -32,9 +32,9 @@ The members of the struct are
 
 * storageType:  used to declare who the owner of data is and to which lifecycle it belongs. Three different cases are possible:
 
- * UA_VARIANT_DATA: this is the simplest case. The data belongs to the variant, which means if the variant is deleted so does the data which is kept inside
+ * UA_VARIANT_DATA: this is the simplest case. The data belongs to the variant, which means if the variant is deleted so does the data which is kept inside,
  
- * UA_VARIANT_DATASOURCE: in this case user-defined functions are called to access the data. The signature of the functions is defined by UA_VariantDataSource structure. A use-case could be to access a sensor only when the data is asked by some client
+ * UA_VARIANT_NODELETE: in this case user-defined data is inside of the variant, meaning that the data will not be automatically freed by the stack.
 
 * arrayLength: length of the array (-1 if a scalar is saved)
 

+ 1 - 0
include/ua_config.h.in

@@ -28,6 +28,7 @@ extern "C" {
 #endif
 
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
+#define UA_GIT_COMMIT_ID "${GIT_COMMIT_ID}"
 #cmakedefine UA_ENABLE_MULTITHREADING
 #cmakedefine UA_ENABLE_METHODCALLS
 #cmakedefine UA_ENABLE_SUBSCRIPTIONS

+ 9 - 0
include/ua_server_external_ns.h

@@ -42,6 +42,9 @@ typedef UA_Int32 (*UA_ExternalNodeStore_addReferences)
 (void *ensHandle, const UA_RequestHeader *requestHeader, UA_AddReferencesItem* referencesToAdd,
  UA_UInt32 *indices,UA_UInt32 indicesSize, UA_StatusCode *addReferencesResults,
  UA_DiagnosticInfo *diagnosticInfos);
+ 
+ typedef UA_Int32 (*UA_ExternalNodeStore_addOneWayReference)
+(void *ensHandle, const UA_AddReferencesItem *item);
 
 typedef UA_Int32 (*UA_ExternalNodeStore_deleteNodes)
 (void *ensHandle, const UA_RequestHeader *requestHeader, UA_DeleteNodesItem *nodesToDelete, UA_UInt32 *indices,
@@ -70,6 +73,10 @@ typedef UA_Int32 (*UA_ExternalNodeStore_translateBrowsePathsToNodeIds)
 (void *ensHandle, const UA_RequestHeader *requestHeader, UA_BrowsePath *browsePath, UA_UInt32 *indices,
  UA_UInt32 indicesSize, UA_BrowsePathResult *browsePathResults, UA_DiagnosticInfo *diagnosticInfos);
 
+typedef UA_Int32 (*UA_ExternalNodeStore_call)
+(void *ensHandle, const UA_RequestHeader *requestHeader, UA_CallMethodRequest *request, UA_UInt32 *indices,
+ UA_UInt32 indicesSize,UA_CallMethodResult *results);
+ 
 typedef UA_Int32 (*UA_ExternalNodeStore_delete)(void *ensHandle);
 
 typedef struct UA_ExternalNodeStore {
@@ -82,6 +89,8 @@ typedef struct UA_ExternalNodeStore {
 	UA_ExternalNodeStore_translateBrowsePathsToNodeIds translateBrowsePathsToNodeIds;
 	UA_ExternalNodeStore_addReferences addReferences;
 	UA_ExternalNodeStore_deleteReferences deleteReferences;
+	UA_ExternalNodeStore_call call;
+	UA_ExternalNodeStore_addOneWayReference addOneWayReference;
 	UA_ExternalNodeStore_delete destroy;
 } UA_ExternalNodeStore;
 

+ 14 - 19
include/ua_types.h

@@ -21,9 +21,6 @@ extern "C" {
 #endif
 
 #include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <inttypes.h>
 #include <stdbool.h>
 #include "ua_config.h"
 #include "ua_constants.h"
@@ -219,11 +216,9 @@ UA_EXPORT extern const UA_String UA_STRING_NULL;
  * ``UA_STRING_ALLOC`` is shorthand for ``UA_String_fromChars`` and makes a copy
  * of the char-array. */
 static UA_INLINE UA_String
-UA_STRING(const char *chars) {
+UA_STRING(char *chars) {
     UA_String str; str.length = strlen(chars);
-    str.length = strlen(chars);
-    str.data = (UA_Byte *) malloc(str.length ); memcpy(str.data, chars, str.length ); 
-    return str;
+    str.data = (UA_Byte*)chars; return str;
 }
 
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
@@ -295,7 +290,7 @@ UA_StatusCode UA_EXPORT UA_ByteString_allocBuffer(UA_ByteString *bs, size_t leng
 UA_EXPORT extern const UA_ByteString UA_BYTESTRING_NULL;
 
 static UA_INLINE UA_ByteString
-UA_BYTESTRING( char *chars) {
+UA_BYTESTRING(char *chars) {
     UA_ByteString str; str.length = strlen(chars);
     str.data = (UA_Byte*)chars; return str;
 }
@@ -337,12 +332,7 @@ typedef struct {
 
 UA_EXPORT extern const UA_NodeId UA_NODEID_NULL;
 
-static UA_INLINE UA_Boolean
-UA_NodeId_isNull(const UA_NodeId *p) {
-    return (p->namespaceIndex == 0 &&
-            p->identifierType == UA_NODEIDTYPE_NUMERIC &&
-            p->identifier.numeric == 0);
-}
+UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
 
 UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 
@@ -425,7 +415,7 @@ UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
 }
 
 static UA_INLINE UA_ExpandedNodeId
-UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex,  char *chars) {
+UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
     UA_ExpandedNodeId id; id.nodeId = UA_NODEID_BYTESTRING(nsIndex, chars);
     id.serverIndex = 0; id.namespaceUri = UA_STRING_NULL; return id;
 }
@@ -445,11 +435,16 @@ typedef struct {
     UA_String name;
 } UA_QualifiedName;
 
+static UA_INLINE UA_Boolean
+UA_QualifiedName_isNull(const UA_QualifiedName *q) {
+    return (q->namespaceIndex == 0 &&
+            q->name.length == 0);
+}
+
 static UA_INLINE UA_QualifiedName
-UA_QUALIFIEDNAME(UA_UInt16 nsIndex, const char *chars) {
+UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars) {
     UA_QualifiedName qn; qn.namespaceIndex = nsIndex;
-    qn.name.length = strlen(chars);
-    qn.name.data = (UA_Byte *) malloc(qn.name.length ); memcpy(qn.name.data, chars, qn.name.length ); return qn;
+    qn.name = UA_STRING(chars); return qn;
 }
 
 static UA_INLINE UA_QualifiedName
@@ -468,7 +463,7 @@ typedef struct {
 } UA_LocalizedText;
 
 static UA_INLINE UA_LocalizedText
-UA_LOCALIZEDTEXT( const char *locale,  const char *text) {
+UA_LOCALIZEDTEXT(char *locale, char *text) {
     UA_LocalizedText lt; lt.locale = UA_STRING(locale);
     lt.text = UA_STRING(text); return lt;
 }

+ 24 - 13
plugins/networklayer_tcp.c

@@ -72,8 +72,13 @@ socket_write(UA_Connection *connection, UA_ByteString *buf) {
     do {
         ssize_t n = 0;
         do {
+        /*
+         * If the OS throws EMSGSIZE, force a smaller packet size:
+         *  size_t bytes_to_send = buf->length - nWritten >  1024 ? 1024 : buf->length - nWritten;
+         */
+            size_t bytes_to_send = buf->length - nWritten;
 #ifdef _WIN32
-            n = send((SOCKET)connection->sockfd, (const char*)buf->data, buf->length, 0);
+            n = send((SOCKET)connection->sockfd, (const char*)buf->data + nWritten, bytes_to_send, 0);
             const int last_error = WSAGetLastError();
             if(n < 0 && last_error != WSAEINTR && last_error != WSAEWOULDBLOCK) {
                 connection->close(connection);
@@ -82,7 +87,7 @@ socket_write(UA_Connection *connection, UA_ByteString *buf) {
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
             }
 #else
-            n = send(connection->sockfd, (const char*)buf->data, buf->length, MSG_NOSIGNAL);
+            n = send(connection->sockfd, (const char*)buf->data + nWritten, bytes_to_send, MSG_NOSIGNAL);
             if(n == -1L && errno != EINTR && errno != EAGAIN) {
                 connection->close(connection);
                 socket_close(connection);
@@ -111,9 +116,9 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
         UA_UInt32 timeout_usec = timeout * 1000;
     #ifdef __APPLE__
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
-	#else
+    #else
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), (long int)(timeout_usec % 1000000)};
-	#endif
+    #endif
         int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tmptv, sizeof(struct timeval));
 #else
         DWORD timeout_dw = timeout;
@@ -135,9 +140,9 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
         UA_UInt32 timeout_usec = timeout * 1000;
     #ifdef __APPLE__
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
-	#else
+    #else
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), (long int)(timeout_usec % 1000000)};
-	#endif
+    #endif
         UA_Int32 retval;
 
         FD_ZERO(&fdset);
@@ -289,10 +294,12 @@ ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
         return;
     connection->state = UA_CONNECTION_CLOSED;
 #endif
-    //cppcheck-suppress unreadVariable
+#if UA_LOGLEVEL <= 300
+   //cppcheck-suppress unreadVariable
     ServerNetworkLayerTCP *layer = connection->handle;
     UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Force closing the connection",
                 connection->sockfd);
+#endif
     /* only "shutdown" here. this triggers the select, where the socket is
        "closed" in the mainloop */
     shutdown(connection->sockfd, 2);
@@ -307,9 +314,14 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
 
     struct sockaddr_in addr;
     socklen_t addrlen = sizeof(struct sockaddr_in);
-    getpeername(newsockfd, (struct sockaddr*)&addr, &addrlen);
-    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP from %s:%d",
-                newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+    int res = getpeername(newsockfd, (struct sockaddr*)&addr, &addrlen);
+    if(res == 0) {
+        UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP from %s:%d",
+                    newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+    } else {
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP, getpeername failed with errno %i",
+                       newsockfd, errno);
+    }
     UA_Connection_init(c);
     c->sockfd = newsockfd;
     c->handle = layer;
@@ -397,8 +409,7 @@ ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt1
     UA_Int32 highestfd = setFDSet(layer, &fdset);
     setFDSet(layer, &errset);
     struct timeval tmptv = {0, timeout * 1000};
-    UA_Int32 resultsize;
-    resultsize = select(highestfd+1, &fdset, NULL, &errset, &tmptv);
+    UA_Int32 resultsize = select(highestfd+1, &fdset, NULL, &errset, &tmptv);
     if(resultsize < 0) {
         *jobs = NULL;
         return 0;
@@ -411,8 +422,8 @@ ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt1
         socklen_t cli_len = sizeof(cli_addr);
         int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
         int i = 1;
-        setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
         if(newsockfd >= 0) {
+            setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
             socket_set_nonblocking(newsockfd);
             ServerNetworkLayerTCP_add(layer, newsockfd);
         }

+ 5 - 5
plugins/ua_config_standard.c

@@ -1,8 +1,8 @@
 #include "ua_config_standard.h"
 
-#define MANUFACTURER_NAME "open62541.org"
+#define MANUFACTURER_NAME "open62541"
 #define PRODUCT_NAME "open62541 OPC UA Server"
-#define PRODUCT_URI "urn:unconfigured:open62541"
+#define PRODUCT_URI "http://open62541.org"
 #define APPLICATION_NAME "open62541-based OPC UA Application"
 #define APPLICATION_URI "urn:unconfigured:application"
 
@@ -22,8 +22,8 @@ const UA_ServerConfig UA_ServerConfig_standard = {
         .productUri = UA_STRING_STATIC(PRODUCT_URI),
         .manufacturerName = UA_STRING_STATIC(MANUFACTURER_NAME),
         .productName = UA_STRING_STATIC(PRODUCT_NAME),
-        .softwareVersion = UA_STRING_STATIC("0"),
-        .buildNumber = UA_STRING_STATIC("0"),
+        .softwareVersion = UA_STRING_STATIC(UA_GIT_COMMIT_ID),
+        .buildNumber = UA_STRING_STATIC(__DATE__ " " __TIME__),
         .buildDate = 0 },
     .applicationDescription = {
         .applicationUri = UA_STRING_STATIC(APPLICATION_URI),
@@ -48,7 +48,7 @@ const UA_ServerConfig UA_ServerConfig_standard = {
     .usernamePasswordLoginsSize = 2,
 
     /* Limits for SecureChannels */
-    .maxSecureChannels = 100,
+    .maxSecureChannels = 40,
     .maxSecurityTokenLifetime = 10 * 60 * 1000, /* 10 minutes */
 
     /* Limits for Sessions */

+ 27 - 19
src/client/ua_client_highlevel_subscriptions.c

@@ -199,7 +199,7 @@ UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscri
 }
 
 static void
-UA_Client_processPublishResponse(UA_Client *client, UA_PublishResponse *response) {
+UA_Client_processPublishResponse(UA_Client *client, UA_PublishRequest *request, UA_PublishResponse *response) {
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
         return;
 
@@ -216,29 +216,37 @@ UA_Client_processPublishResponse(UA_Client *client, UA_PublishResponse *response
                  "Processing a publish response on subscription %u with %u notifications",
                  sub->SubscriptionID, response->notificationMessage.notificationDataSize);
 
-    /* Check if the server has acknowledged any of our ACKS */
-    // TODO: The acks should be attached to the subscription
-    UA_Client_NotificationsAckNumber *ack, *tmpAck;
-    size_t i = 0;
-    LIST_FOREACH_SAFE(ack, &client->pendingNotificationsAcks, listEntry, tmpAck) {
-        if(response->results[i] == UA_STATUSCODE_GOOD ||
-           response->results[i] == UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN) {
-            LIST_REMOVE(ack, listEntry);
-            UA_free(ack);
+    /* Check if the server has acknowledged any of the sent ACKs */
+    for(size_t i = 0; i < response->resultsSize && i < request->subscriptionAcknowledgementsSize; i++) {
+        /* remove also acks that are unknown to the server */
+        if(response->results[i] != UA_STATUSCODE_GOOD &&
+           response->results[i] != UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN)
+            continue;
+
+        /* Remove the ack from the list */
+        UA_SubscriptionAcknowledgement *orig_ack = &request->subscriptionAcknowledgements[i];
+        UA_Client_NotificationsAckNumber *ack;
+        LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) {
+            if(ack->subAck.subscriptionId == orig_ack->subscriptionId &&
+               ack->subAck.sequenceNumber == orig_ack->sequenceNumber) {
+                LIST_REMOVE(ack, listEntry);
+                UA_free(ack);
+                UA_assert(ack != LIST_FIRST(&client->pendingNotificationsAcks));
+                break;
+            }
         }
-        i++;
     }
-    
+
     /* Process the notification messages */
     UA_NotificationMessage *msg = &response->notificationMessage;
     for(size_t k = 0; k < msg->notificationDataSize; k++) {
         if(msg->notificationData[k].encoding != UA_EXTENSIONOBJECT_DECODED)
             continue;
-        
+
         /* Currently only dataChangeNotifications are supported */
         if(msg->notificationData[k].content.decoded.type != &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION])
             continue;
-        
+
         UA_DataChangeNotification *dataChangeNotification = msg->notificationData[k].content.decoded.data;
         for(size_t j = 0; j < dataChangeNotification->monitoredItemsSize; j++) {
             UA_MonitoredItemNotification *mitemNot = &dataChangeNotification->monitoredItems[j];
@@ -255,9 +263,9 @@ UA_Client_processPublishResponse(UA_Client *client, UA_PublishResponse *response
                              mitemNot->clientHandle, sub->SubscriptionID);
         }
     }
-    
+
     /* Add to the list of pending acks */
-    tmpAck = UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
+    UA_Client_NotificationsAckNumber *tmpAck = UA_malloc(sizeof(UA_Client_NotificationsAckNumber));
     tmpAck->subAck.sequenceNumber = msg->sequenceNumber;
     tmpAck->subAck.subscriptionId = sub->SubscriptionID;
     LIST_INSERT_HEAD(&client->pendingNotificationsAcks, tmpAck, listEntry);
@@ -268,7 +276,7 @@ UA_StatusCode UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *clie
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 
     UA_Boolean moreNotifications = true;
-    while(moreNotifications == true) {
+    while(moreNotifications) {
         UA_PublishRequest request;
         UA_PublishRequest_init(&request);
         request.subscriptionAcknowledgementsSize = 0;
@@ -283,7 +291,7 @@ UA_StatusCode UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *clie
                 return UA_STATUSCODE_GOOD;
         }
         
-        int index = 0 ;
+        int index = 0;
         LIST_FOREACH(ack, &client->pendingNotificationsAcks, listEntry) {
             request.subscriptionAcknowledgements[index].sequenceNumber = ack->subAck.sequenceNumber;
             request.subscriptionAcknowledgements[index].subscriptionId = ack->subAck.subscriptionId;
@@ -291,7 +299,7 @@ UA_StatusCode UA_Client_Subscriptions_manuallySendPublishRequest(UA_Client *clie
         }
         
         UA_PublishResponse response = UA_Client_Service_publish(client, request);
-        UA_Client_processPublishResponse(client, &response);
+        UA_Client_processPublishResponse(client, &request, &response);
         moreNotifications = response.moreNotifications;
         
         UA_PublishResponse_deleteMembers(&response);

+ 1 - 1
src/client/ua_client_internal.h

@@ -11,8 +11,8 @@
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 
 typedef struct UA_Client_NotificationsAckNumber_s {
-    UA_SubscriptionAcknowledgement subAck;
     LIST_ENTRY(UA_Client_NotificationsAckNumber_s) listEntry;
+    UA_SubscriptionAcknowledgement subAck;
 } UA_Client_NotificationsAckNumber;
 
 typedef struct UA_Client_MonitoredItem_s {

+ 30 - 10
src/server/ua_securechannel_manager.c

@@ -24,6 +24,17 @@ void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
     }
 }
 
+static void removeSecureChannel(UA_SecureChannelManager *cm, channel_list_entry *entry){
+    LIST_REMOVE(entry, pointers);
+    UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+#ifndef UA_ENABLE_MULTITHREADING
+    cm->currentChannelCount--;
+    UA_free(entry);
+#else
+    cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, -1);
+    UA_Server_delayedFree(cm->server, entry);
+#endif
+}
 /* remove channels that were not renewed or who have no connection attached */
 void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime now) {
     channel_list_entry *entry, *temp;
@@ -33,29 +44,38 @@ void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_Dat
             (UA_DateTime)(entry->channel.securityToken.revisedLifetime * UA_MSEC_TO_DATETIME);
         if(timeout < now || !entry->channel.connection) {
             UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, (&entry->channel), "SecureChannel has timed out");
-            LIST_REMOVE(entry, pointers);
-            UA_SecureChannel_deleteMembersCleanup(&entry->channel);
-#ifndef UA_ENABLE_MULTITHREADING
-            cm->currentChannelCount--;
-            UA_free(entry);
-#else
-            cm->currentChannelCount = uatomic_add_return(&cm->currentChannelCount, -1);
-            UA_Server_delayedFree(cm->server, entry);
-#endif
+            removeSecureChannel(cm, entry);
         } else if(entry->channel.nextSecurityToken.tokenId > 0) {
             UA_SecureChannel_revolveTokens(&entry->channel);
         }
     }
 }
 
+/* remove the first channel that has no session attached */
+static UA_Boolean purgeFirstChannelWithoutSession(UA_SecureChannelManager *cm) {
+    channel_list_entry *entry;
+    LIST_FOREACH(entry, &cm->channels, pointers) {
+        if(LIST_EMPTY(&(entry->channel.sessions))){
+            UA_LOG_DEBUG_CHANNEL(cm->server->config.logger, (&entry->channel), "Channel was purged since maxSecureChannels was reached and channel had no session attached");
+            removeSecureChannel(cm, entry);
+            UA_assert(entry != LIST_FIRST(&cm->channels));
+            return true;
+        }
+    }
+    return false;
+}
+
 UA_StatusCode
 UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
                              const UA_OpenSecureChannelRequest *request,
                              UA_OpenSecureChannelResponse *response) {
     if(request->securityMode != UA_MESSAGESECURITYMODE_NONE)
         return UA_STATUSCODE_BADSECURITYMODEREJECTED;
-    if(cm->currentChannelCount >= cm->server->config.maxSecureChannels)
+    //check if there exists a free SC, otherwise try to purge one SC without a session
+    //the purge has been introduced to pass CTT, it is not clear what strategy is expected here
+    if(cm->currentChannelCount >= cm->server->config.maxSecureChannels && !purgeFirstChannelWithoutSession(cm)){
         return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
     channel_list_entry *entry = UA_malloc(sizeof(channel_list_entry));
     if(!entry)
         return UA_STATUSCODE_BADOUTOFMEMORY;

+ 38 - 38
src/server/ua_server.c

@@ -29,6 +29,9 @@ static const UA_NodeId nodeIdHasProperty = {
 static const UA_NodeId nodeIdOrganizes = {
     .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
     .identifier.numeric = UA_NS0ID_ORGANIZES};
+static const UA_NodeId nodeIdFolderType = {
+    .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
+    .identifier.numeric = UA_NS0ID_FOLDERTYPE};
 
 static const UA_ExpandedNodeId expandedNodeIdBaseDataVariabletype = {
     .nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
@@ -189,6 +192,18 @@ addNodeInternal(UA_Server *server, UA_Node *node, const UA_NodeId parentNodeId,
     return res;
 }
 
+static UA_AddNodesResult
+addNodeInternalWithType(UA_Server *server, UA_Node *node, const UA_NodeId parentNodeId,
+                        const UA_NodeId referenceTypeId, const UA_NodeId typeIdentifier) {
+    UA_AddNodesResult res;
+    UA_AddNodesResult_init(&res);
+    UA_RCU_LOCK();
+    Service_AddNodes_existing(server, &adminSession, node, &parentNodeId,
+                              &referenceTypeId, &typeIdentifier, NULL, &res);
+    UA_RCU_UNLOCK();
+    return res;
+}
+
 UA_StatusCode
 __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
                     const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
@@ -723,29 +738,34 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_NodeStore_insert(server->nodestore, (UA_Node*)baseobjtype);
     UA_RCU_UNLOCK();
 
+    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER), nodeIdHasTypeDefinition,
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+
+    addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
+
     UA_ObjectNode *objects = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)objects, "Objects");
     objects->nodeId.identifier.numeric = UA_NS0ID_OBJECTSFOLDER;
-    addNodeInternal(server, (UA_Node*)objects, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                    nodeIdOrganizes);
+    addNodeInternalWithType(server, (UA_Node*)objects, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 
     UA_ObjectNode *types = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)types, "Types");
     types->nodeId.identifier.numeric = UA_NS0ID_TYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)types, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                    nodeIdOrganizes);
+    addNodeInternalWithType(server, (UA_Node*)types, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 
     UA_ObjectNode *views = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)views, "Views");
     views->nodeId.identifier.numeric = UA_NS0ID_VIEWSFOLDER;
-    addNodeInternal(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-                    nodeIdOrganizes);
+    addNodeInternalWithType(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 
     UA_ObjectNode *referencetypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)referencetypes, "ReferenceTypes");
     referencetypes->nodeId.identifier.numeric = UA_NS0ID_REFERENCETYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)referencetypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                    nodeIdOrganizes);
+    addNodeInternalWithType(server, (UA_Node*)referencetypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCETYPESFOLDER), nodeIdOrganizes,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES), true);
@@ -757,26 +777,12 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *objecttypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)objecttypes, "ObjectTypes");
     objecttypes->nodeId.identifier.numeric = UA_NS0ID_OBJECTTYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)objecttypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                    nodeIdOrganizes);
+    addNodeInternalWithType(server, (UA_Node*)objecttypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
     /* Link in the bootstrapped baseobjecttype */
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTTYPESFOLDER), nodeIdOrganizes,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), true);
 
-    addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTTYPESFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCETYPESFOLDER),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
-
     addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE,
                       UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
@@ -794,9 +800,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *datatypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)datatypes, "DataTypes");
     datatypes->nodeId.identifier.numeric = UA_NS0ID_DATATYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)datatypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER), nodeIdOrganizes);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_DATATYPESFOLDER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)datatypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 
     addDataTypeNode(server, "BaseDataType", UA_NS0ID_BASEDATATYPE, UA_NS0ID_DATATYPESFOLDER);
     addDataTypeNode(server, "Boolean", UA_NS0ID_BOOLEAN, UA_NS0ID_BASEDATATYPE);
@@ -834,10 +839,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *variabletypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)variabletypes, "VariableTypes");
     variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                    nodeIdOrganizes);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VARIABLETYPESFOLDER),
-                         nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
     addVariableTypeNode_organized(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE,
                                   UA_NS0ID_VARIABLETYPESFOLDER, true);
     addVariableTypeNode_subtype(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
@@ -852,9 +855,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *eventtypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)eventtypes, "EventTypes");
     eventtypes->nodeId.identifier.numeric = UA_NS0ID_EVENTTYPESFOLDER;
-    addNodeInternal(server, (UA_Node*)eventtypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER), nodeIdOrganizes);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_EVENTTYPESFOLDER), nodeIdHasTypeDefinition,
-            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)eventtypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                            nodeIdOrganizes, nodeIdFolderType);
 #endif
 
 #ifdef UA_ENABLE_GENERATE_NAMESPACE0
@@ -869,10 +871,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_ObjectNode *servernode = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)servernode, "Server");
     servernode->nodeId.identifier.numeric = UA_NS0ID_SERVER;
-    addNodeInternal(server, (UA_Node*)servernode, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                    nodeIdOrganizes);
-    addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasTypeDefinition,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE), true);
+    addNodeInternalWithType(server, (UA_Node*)servernode, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            nodeIdOrganizes, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE));
 
     UA_VariableNode *namespaceArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)namespaceArray, "NamespaceArray");

+ 6 - 1
src/server/ua_server_binary.c

@@ -173,6 +173,11 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
         *requestType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSREQUEST];
         *responseType = &UA_TYPES[UA_TYPES_MODIFYMONITOREDITEMSRESPONSE];
         break;
+    case UA_NS0ID_SETMONITORINGMODEREQUEST:
+        *service = (UA_Service)Service_SetMonitoringMode;
+        *requestType = &UA_TYPES[UA_TYPES_SETMONITORINGMODEREQUEST];
+        *responseType = &UA_TYPES[UA_TYPES_SETMONITORINGMODERESPONSE];
+        break;
 #endif
 
 #ifdef UA_ENABLE_METHODCALLS
@@ -447,7 +452,7 @@ processRequest(UA_SecureChannel *channel, UA_Server *server, UA_UInt32 requestId
     /* Set an anonymous, inactive session for services that need no session */
     UA_Session anonymousSession;
     if(!session) {
-        if(sessionRequired || !UA_NodeId_isNull(&requestHeader->authenticationToken)) {
+        if(sessionRequired) {
             UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Service request %i without a valid session",
                                 requestTypeId.identifier.numeric - UA_ENCODINGOFFSET_BINARY);
             sendError(channel, msg, requestPos, responseType, requestId, UA_STATUSCODE_BADSESSIONIDINVALID);

+ 4 - 0
src/server/ua_server_worker.c

@@ -614,6 +614,10 @@ static void completeMessages(UA_Server *server, UA_Job *job) {
     }
     if(realloced)
         job->type = UA_JOBTYPE_BINARYMESSAGE_ALLOCATED;
+
+    /* discard the job if message is empty - also no leak is possible here */
+    if(job->job.binaryMessage.message.length == 0)
+        job->type = UA_JOBTYPE_NOTHING;
 }
 
 UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {

+ 9 - 4
src/server/ua_services.h

@@ -279,11 +279,16 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
 void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_ModifyMonitoredItemsRequest *request,
                                   UA_ModifyMonitoredItemsResponse *response);
-/* Not Implemented: Service_SetMonitoringMode */
+
+/* Used to set the monitoring mode for one or more MonitoredItems of a Subscription. */
+void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
+                               const UA_SetMonitoringModeRequest *request,
+                               UA_SetMonitoringModeResponse *response);
+
 /* Not Implemented: Service_SetTriggering */
 
 #endif
-                                      
+
 /**
  * Subscription Service Set
  * ------------------------
@@ -304,8 +309,8 @@ void Service_ModifySubscription(UA_Server *server, UA_Session *session,
 
 /* Used to enable sending of Notifications on one or more Subscriptions. */
 void Service_SetPublishingMode(UA_Server *server, UA_Session *session,
-	                           const UA_SetPublishingModeRequest *request,
-	                           UA_SetPublishingModeResponse *response);
+                               const UA_SetPublishingModeRequest *request,
+                               UA_SetPublishingModeResponse *response);
 
 /* Used for two purposes. First, it is used to acknowledge the receipt of
  * NotificationMessages for one or more Subscriptions. Second, it is used to

+ 28 - 9
src/server/ua_services_attribute.c

@@ -26,14 +26,20 @@ readDimension(UA_Byte *buf, size_t buflen, struct UA_NumericRangeDimension *dim)
     size_t progress = readNumber(buf, buflen, &dim->min);
     if(progress == 0)
         return 0;
-    if(buflen <= progress || buf[progress] != ':') {
+    if(buflen <= progress + 1 || buf[progress] != ':') {
         dim->max = dim->min;
         return progress;
     }
+
     progress++;
     size_t progress2 = readNumber(&buf[progress], buflen - progress, &dim->max);
     if(progress2 == 0)
         return 0;
+
+    /* invalid range */
+    if(dim->min >= dim->max)
+        return 0;
+    
     return progress + progress2;
 }
 
@@ -46,7 +52,7 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
     struct UA_NumericRangeDimension *dimensions = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     size_t pos = 0;
-    do {
+    while(true) {
         /* alloc dimensions */
         if(idx >= dimensionsMax) {
             struct UA_NumericRangeDimension *newds;
@@ -71,7 +77,13 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
         /* loop into the next dimension */
         if(pos >= str->length)
             break;
-    } while(str->data[pos] == ',' && pos++);
+
+        if(str->data[pos] != ',') {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
+            break;
+        }
+        pos++;
+    }
 
     if(retval == UA_STATUSCODE_GOOD && idx > 0) {
         range->dimensions = dimensions;
@@ -156,14 +168,19 @@ getVariableNodeDataType(UA_Server *server, UA_Session *session,
                         const UA_VariableNode *vn, UA_DataValue *v) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-        forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId,
-                              &UA_TYPES[UA_TYPES_NODEID]);
+        if(vn->value.variant.value.type) {
+            forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+        } else {
+            UA_NodeId nullid;
+            UA_NodeId_init(&nullid);
+            UA_Variant_setScalarCopy(&v->value, &nullid, &UA_TYPES[UA_TYPES_NODEID]);
+        }
     } else {
+        /* Read from the datasource to see the data type */
         if(!vn->value.dataSource.read) {
             UA_LOG_DEBUG_SESSION(server->config.logger, session, "DataSource cannot be read in ReadRequest");
             return UA_STATUSCODE_BADINTERNALERROR;
         }
-        /* Read from the datasource to see the data type */
         UA_DataValue val;
         UA_DataValue_init(&val);
         val.hasValue = false; // always assume we are not given a value by userspace
@@ -207,10 +224,11 @@ static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"Defa
 /* clang complains about unused variables */
 // static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"};
 
-/** Reads a single attribute from a node in the nodestore. */
+/* Reads a single attribute from a node in the nodestore */
 void Service_Read_single(UA_Server *server, UA_Session *session,
                          const UA_TimestampsToReturn timestamps,
                          const UA_ReadValueId *id, UA_DataValue *v) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Read the attribute %i", id->attributeId);
     if(id->dataEncoding.name.length > 0 &&
        !UA_String_equal(&binEncoding, &id->dataEncoding.name)) {
            v->hasStatus = true;
@@ -218,7 +236,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
            return;
     }
 
-    //index range for a non-value
+    /* index range for a non-value */
     if(id->indexRange.length > 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){
         v->hasStatus = true;
         v->status = UA_STATUSCODE_BADINDEXRANGENODATA;
@@ -353,7 +371,8 @@ void Service_Read(UA_Server *server, UA_Session *session,
         return;
     }
 
-    if(request->timestampsToReturn > 3){
+    /* check if the timestampstoreturn is valid */
+    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
         return;
     }

+ 27 - 2
src/server/ua_services_call.c

@@ -215,8 +215,33 @@ void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *
     }
     response->resultsSize = request->methodsToCallSize;
 
-    for(size_t i = 0; i < request->methodsToCallSize;i++)
-        Service_Call_single(server, session, &request->methodsToCall[i], &response->results[i]);
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+    UA_Boolean isExternal[request->methodsToCallSize];
+    UA_UInt32 indices[request->methodsToCallSize];
+    memset(isExternal, false, sizeof(UA_Boolean) * request->methodsToCallSize);
+    for(size_t j = 0;j<server->externalNamespacesSize;j++) {
+        size_t indexSize = 0;
+        for(size_t i = 0;i < request->methodsToCallSize;i++) {
+            if(request->methodsToCall[i].methodId.namespaceIndex != server->externalNamespaces[j].index)
+                continue;
+            isExternal[i] = true;
+            indices[indexSize] = (UA_UInt32)i;
+            indexSize++;
+        }
+        if(indexSize == 0)
+            continue;
+        UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+        ens->call(ens->ensHandle, &request->requestHeader, request->methodsToCall,
+                       indices, (UA_UInt32)indexSize, response->results);
+    }
+#endif
+	
+    for(size_t i = 0; i < request->methodsToCallSize;i++){
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+        if(!isExternal[i])
+#endif    
+			Service_Call_single(server, session, &request->methodsToCall[i], &response->results[i]);
+	}
 }
 
 #endif /* UA_ENABLE_METHODCALLS */

+ 48 - 7
src/server/ua_services_nodemanagement.c

@@ -941,23 +941,64 @@ addOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node, const
 
 UA_StatusCode
 Service_AddReferences_single(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item) {
-    if(item->targetServerUri.length > 0)
+UA_StatusCode retval = UA_STATUSCODE_GOOD;
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+	UA_Boolean handledExternally = UA_FALSE;
+#endif  
+	if(item->targetServerUri.length > 0)
         return UA_STATUSCODE_BADNOTIMPLEMENTED; // currently no expandednodeids are allowed
 
-    /* cast away the const to loop the call through UA_Server_editNode */
-    UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+    for(size_t j = 0; j <server->externalNamespacesSize; j++) {
+        if(item->sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
+                continue;
+		} else {
+			UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+			retval = ens->addOneWayReference(ens->ensHandle, item);
+			handledExternally = UA_TRUE;
+			break;
+        }
+        
+    }
+	if(handledExternally == UA_FALSE) {
+#endif
+    /* cast away the const to loop the call through UA_Server_editNode 
+		beware the "if" above for external nodestores	*/
+    retval = UA_Server_editNode(server, session, &item->sourceNodeId,
                                               (UA_EditNodeCallback)addOneWayReference, item);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+	}
+#endif
 
+    if(retval != UA_STATUSCODE_GOOD){
+		return retval;
+	}
+	
     UA_AddReferencesItem secondItem;
     secondItem = *item;
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
     secondItem.isForward = !item->isForward;
-    retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+    handledExternally = UA_FALSE;
+	for(size_t j = 0; j <server->externalNamespacesSize; j++) {
+        if(secondItem.sourceNodeId.namespaceIndex != server->externalNamespaces[j].index) {
+                continue;
+		} else {
+			UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
+			retval = ens->addOneWayReference(ens->ensHandle, &secondItem);
+			handledExternally = UA_TRUE;
+			break;
+        }
+        
+    }
+	if(handledExternally == UA_FALSE) {
+#endif
+	retval = UA_Server_editNode(server, session, &secondItem.sourceNodeId,
                                 (UA_EditNodeCallback)addOneWayReference, &secondItem);
-
+#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
+	}
+#endif
     // todo: remove reference if the second direction failed
     return retval;
 }

+ 72 - 9
src/server/ua_services_subscription.c

@@ -125,7 +125,8 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
     UA_BOUNDEDVALUE_SETWBOUNDS(server->config.queueSizeLimits,
                                queueSize, mon->maxQueueSize);
     mon->discardOldest = discardOldest;
-    MonitoredItem_registerSampleJob(server, mon);
+    if(monitoringMode == UA_MONITORINGMODE_REPORTING)
+        MonitoredItem_registerSampleJob(server, mon);
 }
 
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
@@ -134,13 +135,16 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
                                     const UA_TimestampsToReturn timestampsToReturn,
                                     const UA_MonitoredItemCreateRequest *request,
                                     UA_MonitoredItemCreateResult *result) {
-    /* Check if the target exists */
-    const UA_Node *target = UA_NodeStore_get(server->nodestore, &request->itemToMonitor.nodeId);
-    if(!target) {
-        result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
+    /* Make an example read to get errors in the itemToMonitor */
+    UA_DataValue v;
+    UA_DataValue_init(&v);
+    Service_Read_single(server, session, timestampsToReturn, &request->itemToMonitor, &v);
+    if(v.hasStatus != UA_STATUSCODE_GOOD) {
+        result->statusCode = v.status;
+        UA_DataValue_deleteMembers(&v);
         return;
     }
-    // TODO: Check if the target node type has the requested attribute
+    UA_DataValue_deleteMembers(&v);
 
     /* Check if the encoding is supported */
     if(request->itemToMonitor.dataEncoding.name.length > 0 &&
@@ -162,7 +166,7 @@ Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session, UA_S
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    UA_StatusCode retval = UA_NodeId_copy(&target->nodeId, &newMon->monitoredNodeId);
+    UA_StatusCode retval = UA_NodeId_copy(&request->itemToMonitor.nodeId, &newMon->monitoredNodeId);
     if(retval != UA_STATUSCODE_GOOD) {
         result->statusCode = retval;
         MonitoredItem_delete(server, newMon);
@@ -191,6 +195,13 @@ 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");
+
+    /* check if the timestampstoreturn is valid */
+    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
+        return;
+    }
+
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -240,6 +251,13 @@ 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");
+
+    /* check if the timestampstoreturn is valid */
+    if(request->timestampsToReturn > UA_TIMESTAMPSTORETURN_NEITHER) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADTIMESTAMPSTORETURNINVALID;
+        return;
+    }
+
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
@@ -262,11 +280,43 @@ void Service_ModifyMonitoredItems(UA_Server *server, UA_Session *session,
     response->resultsSize = request->itemsToModifySize;
 
     for(size_t i = 0; i < request->itemsToModifySize; i++)
-        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i],
-                                            &response->results[i]);
+        Service_ModifyMonitoredItems_single(server, session, sub, &request->itemsToModify[i], &response->results[i]);
+
+}
+
+void Service_SetMonitoringMode(UA_Server *server, UA_Session *session,
+                               const UA_SetMonitoringModeRequest *request,
+                               UA_SetMonitoringModeResponse *response) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing SetMonitoringMode");
+    UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
+    if(!sub) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
+        return;
+    }
+
+    if(request->monitoredItemIdsSize == 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
+
+    response->results = UA_Array_new(request->monitoredItemIdsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
+    if(!response->results) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+        return;
+    }
+    response->resultsSize = request->monitoredItemIdsSize;
 
+    for(size_t i = 0; i < response->resultsSize; i++) {
+        UA_MonitoredItem *mon = UA_Subscription_getMonitoredItem(sub, request->monitoredItemIds[i]);
+        if(mon)
+            setMonitoredItemSettings(server, mon, request->monitoringMode, mon->clientHandle,
+                                     mon->samplingInterval, mon->maxQueueSize, mon->discardOldest);
+        else
+            response->results[i] = UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
+    }
 }
 
+
 void
 Service_Publish(UA_Server *server, UA_Session *session,
                 const UA_PublishRequest *request, UA_UInt32 requestId) {
@@ -340,6 +390,12 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                                  const UA_DeleteSubscriptionsRequest *request,
                                  UA_DeleteSubscriptionsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteSubscriptionsRequest");
+
+    if(request->subscriptionIdsSize == 0){
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
+
     response->results = UA_malloc(sizeof(UA_StatusCode) * request->subscriptionIdsSize);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -355,6 +411,13 @@ void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
                                   const UA_DeleteMonitoredItemsRequest *request,
                                   UA_DeleteMonitoredItemsResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing DeleteMonitoredItemsRequest");
+
+    if(request->monitoredItemIdsSize == 0) {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+        return;
+    }
+
+    /* Get the subscription */
     UA_Subscription *sub = UA_Session_getSubscriptionByID(session, request->subscriptionId);
     if(!sub) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;

+ 80 - 4
src/server/ua_services_view.c

@@ -125,6 +125,70 @@ returnRelevantNode(UA_Server *server, const UA_BrowseDescription *descr, UA_Bool
     return node;
 }
 
+/**
+ * We find all subtypes by a single iteration over the array. We start with an array with a single
+ * root nodeid at the beginning. When we find relevant references, we add the nodeids to the back of
+ * the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the
+ * array to process the newly found referencetype nodeids (emulated recursion).
+ */
+static UA_StatusCode
+findSubTypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) {
+    const UA_Node *node = UA_NodeStore_get(ns, root);
+    if(!node)
+        return UA_STATUSCODE_BADNOMATCH;
+    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
+        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+
+    size_t results_size = 20; // probably too big, but saves mallocs
+    UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
+    if(!results)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_free(results);
+        return retval;
+    }
+        
+    size_t idx = 0; // where are we currently in the array?
+    size_t last = 0; // where is the last element in the array?
+    do {
+        node = UA_NodeStore_get(ns, &results[idx]);
+        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
+            continue;
+        for(size_t i = 0; i < node->referencesSize; i++) {
+            if(node->references[i].referenceTypeId.identifier.numeric != UA_NS0ID_HASSUBTYPE ||
+               node->references[i].isInverse == true)
+                continue;
+
+            if(++last >= results_size) { // is the array big enough?
+                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
+                if(!new_results) {
+                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                    break;
+                }
+                results = new_results;
+                results_size *= 2;
+            }
+
+            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
+            if(retval != UA_STATUSCODE_GOOD) {
+                last--; // for array_delete
+                break;
+            }
+        }
+    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
+        return retval;
+    }
+
+    *reftypes = results;
+    *reftypes_count = last + 1;
+    return UA_STATUSCODE_GOOD;
+}
+
 static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     LIST_REMOVE(cp, pointers);
     UA_ByteString_deleteMembers(&cp->identifier);
@@ -170,8 +234,8 @@ Service_Browse_single(UA_Server *server, UA_Session *session, struct Continuatio
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
     if(!all_refs) {
         if(descr->includeSubtypes) {
-            result->statusCode = getTypeHierarchy(server->nodestore, &descr->referenceTypeId,
-                                                     &relevant_refs, &relevant_refs_size);
+            result->statusCode = findSubTypes(server->nodestore, &descr->referenceTypeId,
+                                              &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
@@ -400,7 +464,7 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     } else if(!elem->includeSubtypes) {
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
     } else {
-        retval = getTypeHierarchy(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        retval = findSubTypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -465,6 +529,15 @@ void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session
         return;
     }
         
+    //relativePath elements should not have an empty targetName
+    for(size_t i=0;i<path->relativePath.elementsSize;i++){
+        UA_QualifiedName *qname = &(path->relativePath.elements[i].targetName);
+        if(UA_QualifiedName_isNull(qname)){
+            result->statusCode = UA_STATUSCODE_BADBROWSENAMEINVALID;
+            return;
+        }
+    }
+
     size_t arraySize = 10;
     result->targets = UA_malloc(sizeof(UA_BrowsePathTarget) * arraySize);
     if(!result->targets) {
@@ -479,10 +552,12 @@ void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session
         result->targets = NULL;
         return;
     }
+
     result->statusCode = walkBrowsePath(server, session, firstNode, &path->relativePath, 0,
                                         &result->targets, &arraySize, &result->targetsSize);
     if(result->targetsSize == 0 && result->statusCode == UA_STATUSCODE_GOOD)
         result->statusCode = UA_STATUSCODE_BADNOMATCH;
+
     if(result->statusCode != UA_STATUSCODE_GOOD) {
         UA_Array_delete(result->targets, result->targetsSize, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET]);
         result->targets = NULL;
@@ -528,7 +603,8 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
             continue;
         UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
         ens->translateBrowsePathsToNodeIds(ens->ensHandle, &request->requestHeader, request->browsePaths,
-                                           indices, (UA_UInt32)indexSize, response->results, response->diagnosticInfos);
+                                           indices, (UA_UInt32)indexSize, response->results,
+                                           response->diagnosticInfos);
     }
 #endif
 

+ 22 - 20
src/server/ua_subscription.c

@@ -44,18 +44,18 @@ void MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem) {
 }
 
 static void SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
+    UA_Subscription *sub = monitoredItem->subscription;
     if(monitoredItem->monitoredItemType != UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
-        UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "Session " PRINTF_GUID_FORMAT " | MonitoredItem %i | "
+        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "MonitoredItem %i | "
                      "Cannot process a monitoreditem that is not a data change notification",
-                     PRINTF_GUID_DATA(monitoredItem->subscription->session->sessionId), monitoredItem->itemId);
+                     monitoredItem->itemId);
         return;
     }
 
     MonitoredItem_queuedValue *newvalue = UA_malloc(sizeof(MonitoredItem_queuedValue));
     if(!newvalue) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
-                    "Session " PRINTF_GUID_FORMAT " | MonitoredItem %i | Skipped a sample due to lack of memory",
-                    PRINTF_GUID_DATA(monitoredItem->subscription->session->sessionId), monitoredItem->itemId);
+        UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "MonitoredItem %i | "
+                            "Skipped a sample due to lack of memory", monitoredItem->itemId);
         return;
     }
     UA_DataValue_init(&newvalue->value);
@@ -67,7 +67,6 @@ static void SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
     rvid.nodeId = monitoredItem->monitoredNodeId;
     rvid.attributeId = monitoredItem->attributeID;
     rvid.indexRange = monitoredItem->indexRange;
-    UA_Subscription *sub = monitoredItem->subscription;
     Service_Read_single(server, sub->session, monitoredItem->timestampsToReturn, &rvid, &newvalue->value);
 
     /* encode to see if the data has changed */
@@ -90,16 +89,14 @@ static void SampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) {
         UA_ByteString_deleteMembers(&newValueAsByteString);
         UA_DataValue_deleteMembers(&newvalue->value);
         UA_free(newvalue);
-        UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "Session " PRINTF_GUID_FORMAT " | Subscription %u | "
-                     "MonitoredItem %u | Do not sample an unchanged value",
-                     PRINTF_GUID_DATA(monitoredItem->subscription->session->sessionId), monitoredItem->subscription->subscriptionID, monitoredItem->itemId);
+        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                             "MonitoredItem %u | Do not sample an unchanged value",
+                             sub->subscriptionID, monitoredItem->itemId);
         return;
     }
 
-    UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "Session " PRINTF_GUID_FORMAT " | Subscription %u | "
-                 "MonitoredItem %u | Sampling the value",
-                 PRINTF_GUID_DATA(monitoredItem->subscription->session->sessionId),
-                 monitoredItem->subscription->subscriptionID, monitoredItem->itemId);
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | MonitoredItem %u | Sampling the value",
+                         sub->subscriptionID, monitoredItem->itemId);
 
     /* do we have space in the queue? */
     if(monitoredItem->currentQueueSize >= monitoredItem->maxQueueSize) {
@@ -250,15 +247,15 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
             sub->state = UA_SUBSCRIPTIONSTATE_LATE;
         } else {
             sub->currentLifetimeCount++;
-            if(sub->currentLifetimeCount >= sub->lifeTimeCount) {
-                UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
-                    "Session %u | Subscription %u | End of lifetime for subscription",
-                            sub->session->authenticationToken.identifier.numeric, sub->subscriptionID);
+            if(sub->currentLifetimeCount > sub->lifeTimeCount) {
+                UA_LOG_INFO_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                                    "End of lifetime for subscription", sub->subscriptionID);
                 UA_Session_deleteSubscription(server, sub->session, sub->subscriptionID);
             }
         }
         return;
     }
+
     SIMPLEQ_REMOVE_HEAD(&sub->session->responseQueue, listEntry);
     UA_PublishResponse *response = &pre->response;
     UA_UInt32 requestId = pre->requestId;
@@ -310,8 +307,13 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
         /* Put the notification message into the retransmission queue */
         UA_NotificationMessageEntry *retransmission = malloc(sizeof(UA_NotificationMessageEntry));
-        retransmission->message = response->notificationMessage;
-        LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
+        if(retransmission) {
+            UA_NotificationMessage_copy(&response->notificationMessage, &retransmission->message);
+            LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
+        } else {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                                   "Could not allocate memory for retransmission", sub->subscriptionID);
+        }
     }
 
     /* Get the available sequence numbers from the retransmission queue */
@@ -331,7 +333,7 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
     /* Send the response */
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                   "Sending out a publish response with %u notifications", (UA_UInt32)notifications);
+                         "Sending out a publish response with %u notifications", (UA_UInt32)notifications);
     UA_SecureChannel_sendBinaryMessage(sub->session->channel, requestId, response,
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 

+ 26 - 2
src/ua_types.c

@@ -91,7 +91,7 @@ UA_DateTime UA_DateTime_nowMonotonic(void) {
     clock_get_time(cclock, &mts);
     mach_port_deallocate(mach_task_self(), cclock);
     return (mts.tv_sec * UA_SEC_TO_DATETIME) + (mts.tv_nsec / 100);
-#elif defined(__CYGWIN__)
+#elif defined(__CYGWIN__) || !defined(CLOCK_MONOTONIC_RAW)
     struct timespec ts;
     clock_gettime(CLOCK_MONOTONIC, &ts);
     return (ts.tv_sec * UA_SEC_TO_DATETIME) + (ts.tv_nsec / 100);
@@ -234,6 +234,30 @@ static UA_StatusCode NodeId_copy(UA_NodeId const *src, UA_NodeId *dst, const UA_
     return retval;
 }
 
+UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
+    if(p->namespaceIndex != 0)
+        return false;
+    switch(p->identifierType) {
+    case UA_NODEIDTYPE_NUMERIC:
+        return (p->identifier.numeric == 0);
+    case UA_NODEIDTYPE_GUID:
+        return (p->identifier.guid.data1 == 0 &&
+                p->identifier.guid.data2 == 0 &&
+                p->identifier.guid.data3 == 0 &&
+                p->identifier.guid.data4[0] == 0 &&
+                p->identifier.guid.data4[1] == 0 &&
+                p->identifier.guid.data4[2] == 0 &&
+                p->identifier.guid.data4[3] == 0 &&
+                p->identifier.guid.data4[4] == 0 &&
+                p->identifier.guid.data4[5] == 0 &&
+                p->identifier.guid.data4[6] == 0 &&
+                p->identifier.guid.data4[7] == 0);
+    default:
+        break;
+    }
+    return (p->identifier.string.length == 0);
+}
+
 UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
     if(n1->namespaceIndex != n2->namespaceIndex || n1->identifierType!=n2->identifierType)
         return false;
@@ -393,7 +417,7 @@ processRangeDefinition(const UA_Variant *v, const UA_NumericRange range, size_t
     /* Test the integrity of the range */
     size_t count = 1;
     if(range.dimensionsSize != dims_count)
-        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_BADINDEXRANGENODATA;
     for(size_t i = 0; i < dims_count; i++) {
         if(range.dimensions[i].min > range.dimensions[i].max)
             return UA_STATUSCODE_BADINDEXRANGEINVALID;

+ 14 - 7
src/ua_types_encoding_binary.c

@@ -262,8 +262,8 @@ static uint64_t pack754(long double f, unsigned bits, unsigned expbits) {
     while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
     fnorm = fnorm - 1.0;
     long long significand = (long long)(fnorm * ((float)(1LL<<significandbits) + 0.5f));
-    long long exp = shift + ((1<<(expbits-1)) - 1);
-    return (uint64_t)((sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand);
+    long long exponent = shift + ((1<<(expbits-1)) - 1);
+    return (uint64_t)((sign<<(bits-1)) | (exponent<<(bits-expbits-1)) | significand);
 }
 
 static long double unpack754(uint64_t i, unsigned bits, unsigned expbits) {
@@ -917,11 +917,18 @@ Variant_decodeBinary(bufpos pos, bufend end, UA_Variant *dst, const UA_DataType
 
         /* search for the datatype. use extensionobject if nothing is found */
         dst->type = &UA_TYPES[UA_TYPES_EXTENSIONOBJECT];
-        if(typeId.namespaceIndex != 0 || eo_encoding != UA_EXTENSIONOBJECT_ENCODED_BYTESTRING ||
-           findDataType(&typeId, &dst->type) != UA_STATUSCODE_GOOD)
-                *pos = old_pos; /* the datatype is unknown. roll back the
-                                   position and decode as an extensionobject */
-        UA_NodeId_deleteMembers(&typeId);
+        if(typeId.namespaceIndex == 0 && typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
+           eo_encoding == UA_EXTENSIONOBJECT_ENCODED_BYTESTRING) {
+            UA_assert(typeId.identifier.byteString.data == NULL); /* for clang analyzer <= 3.7 */
+            typeId.identifier.numeric -= UA_ENCODINGOFFSET_BINARY;
+            if(findDataType(&typeId, &dst->type) == UA_STATUSCODE_GOOD)
+                (*pos) += 4; /* jump over the length (todo: check if length matches) */
+            else
+                *pos = old_pos; /* jump back and decode as extensionobject */
+        } else {
+            *pos = old_pos; /* jump back and decode as extensionobject */
+            UA_NodeId_deleteMembers(&typeId);
+        }
 
         /* decode the type */
         dst->data = UA_calloc(1, dst->type->memSize);

+ 2 - 2
tests/CMakeLists.txt

@@ -76,7 +76,7 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                           ${CMAKE_CURRENT_BINARY_DIR}/client_CreateActivateSession.bin
                           ${CMAKE_CURRENT_BINARY_DIR}/client_Browse.bin
                           ${CMAKE_CURRENT_BINARY_DIR}/client_Read.bin
-                          ${CMAKE_CURRENT_BINARY_DIR}/client_Writebin
+                          ${CMAKE_CURRENT_BINARY_DIR}/client_Write.bin
                   PRE_BUILD
                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/hex2bin.py
                                  ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex
@@ -112,7 +112,7 @@ add_test(check_server_binary_messages_read ${CMAKE_CURRENT_BINARY_DIR}/check_ser
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Read.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CLO.bin)
 
-add_test(check_server_binary_messages_read ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
+add_test(check_server_binary_messages_write ${CMAKE_CURRENT_BINARY_DIR}/check_server_binary_messages
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_CreateActivateSession.bin
                                            ${CMAKE_CURRENT_BINARY_DIR}/client_Write.bin

+ 1 - 1
tests/check_server_binary_messages.c

@@ -37,7 +37,7 @@ START_TEST(processMessage) {
         UA_ByteString msg = readFile(filenames[i]);
         UA_Boolean reallocated;
         UA_StatusCode retval = UA_Connection_completeMessages(&c, &msg, &reallocated);
-        if(retval == UA_STATUSCODE_GOOD)
+        if(retval == UA_STATUSCODE_GOOD && msg.length > 0)
             UA_Server_processBinaryMessage(server, &c, &msg);
         UA_ByteString_deleteMembers(&msg);
     }

+ 9 - 9
tools/amalgamate.py

@@ -19,7 +19,6 @@ if pos > 0:
     outname = outname[:pos]
 include_re = re.compile("^#include (\".*\").*$")
 guard_re = re.compile("^#(?:(?:ifndef|define) [A-Z_]+_H_|endif /\* [A-Z_]+_H_ \*/|endif // [A-Z_]+_H_)")
-includes = []
 
 print ("Starting amalgamating file "+ args.outfile)
 
@@ -30,7 +29,7 @@ file.write(u"""/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN6
  */
 
 /*
- * Copyright (C) 2015 the contributors as stated in the AUTHORS file
+ * Copyright (C) 2014-2016 the contributors as stated in the AUTHORS file
  *
  * This file is part of open62541. open62541 is free software: you can
  * redistribute it and/or modify it under the terms of the GNU Lesser General
@@ -44,7 +43,14 @@ file.write(u"""/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN6
  * details.
  */\n\n""" % args.version)
 
-if not is_c:
+if is_c:
+    file.write(u'''#ifndef UA_DYNAMIC_LINKING
+# define UA_DYNAMIC_LINKING
+#endif
+
+#include "%s.h"
+''' % outname)
+else:
     file.write(u'''#ifndef %s
 #define %s
 
@@ -52,12 +58,6 @@ if not is_c:
 extern "C" {
 #endif\n''' % (outname.upper() + u"_H_", outname.upper() + u"_H_") )
 
-if not is_c:
-    for inc in includes:
-        file.write(u"#include " + inc + "\n")
-else:
-    file.write(u"#include \"" + outname + ".h\"\n")
-
 for fname in args.inputs:
     with io.open(fname, encoding="utf8") as infile:
         file.write(u"\n/*********************************** amalgamated original file \"" + fname + u"\" ***********************************/\n\n")

+ 2 - 0
tools/schema/datatypes_minimal.txt

@@ -156,3 +156,5 @@ MonitoredItemModifyRequest
 ModifyMonitoredItemsRequest
 MonitoredItemModifyResult
 ModifyMonitoredItemsResponse
+SetMonitoringModeRequest
+SetMonitoringModeResponse

+ 38 - 40
tools/travis/travis_linux_before_install.sh

@@ -23,51 +23,49 @@ if [ $ANALYZE = "true" ]; then
         cppcheck --version
     fi
 else
-	cd $LOCAL_PKG
-
-	# travis caches the $LOCAL_PKG dir. If it is loaded, we don't need to reinstall the packages
-	if [ ! -f $LOCAL_PKG/.cached ]; then
-
-		# Install newer valgrind
-		mkdir -p $LOCAL_PKG/package && cd $LOCAL_PKG/package
-		wget http://valgrind.org/downloads/valgrind-3.11.0.tar.bz2
-		tar xf valgrind-3.11.0.tar.bz2
-		cd valgrind-3.11.0
-		./configure --prefix=$LOCAL_PKG
-		make -s -j8 install
-		echo "\n### Installed valgrind version: $(valgrind --version)"
-		cd $LOCAL_PKG
+    cd $LOCAL_PKG
 
-		# Install specific check version which is not yet in the apt package
-	 	wget http://ftp.de.debian.org/debian/pool/main/c/check/check_0.10.0-3_amd64.deb
-	 	dpkg -x check_0.10.0-3_amd64.deb $LOCAL_PKG/package
-	 	# change pkg-config file path
-		sed -i "s|prefix=/usr|prefix=${LOCAL_PKG}|g" $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc
-		sed -i 's|libdir=.*|libdir=${prefix}/lib|g' $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc
-		# move files to globally included dirs
-		cp -R $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/* $LOCAL_PKG/lib/
-		cp -R $LOCAL_PKG/package/usr/include/* $LOCAL_PKG/include/
-		cp -R $LOCAL_PKG/package/usr/bin/* $LOCAL_PKG/
+    # travis caches the $LOCAL_PKG dir. If it is loaded, we don't need to reinstall the packages
+    if [ ! -f $LOCAL_PKG/.cached ]; then
 
-		# Install specific liburcu version which is not yet in the apt package
-		wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu2_0.8.5-1ubuntu1_amd64.deb
-		wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu-dev_0.8.5-1ubuntu1_amd64.deb
-		dpkg -x liburcu2_0.8.5-1ubuntu1_amd64.deb $LOCAL_PKG/package
-		dpkg -x liburcu-dev_0.8.5-1ubuntu1_amd64.deb $LOCAL_PKG/package
-		# move files to globally included dirs
-		cp -R $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/* $LOCAL_PKG/lib/
-		cp -R $LOCAL_PKG/package/usr/include/* $LOCAL_PKG/include/
+        # Install newer valgrind
+        mkdir -p $LOCAL_PKG/package && cd $LOCAL_PKG/package
+        wget http://valgrind.org/downloads/valgrind-3.11.0.tar.bz2
+        tar xf valgrind-3.11.0.tar.bz2
+        cd valgrind-3.11.0
+        ./configure --prefix=$LOCAL_PKG
+        make -s -j8 install
+        echo "\n### Installed valgrind version: $(valgrind --version)"
+        cd $LOCAL_PKG
 
-		# create cached flag
-		touch $LOCAL_PKG/.cached
+        # Install specific check version which is not yet in the apt package
+        wget http://ftp.de.debian.org/debian/pool/main/c/check/check_0.10.0-3_amd64.deb
+        dpkg -x check_0.10.0-3_amd64.deb $LOCAL_PKG/package
+        # change pkg-config file path
+        sed -i "s|prefix=/usr|prefix=${LOCAL_PKG}|g" $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc
+        sed -i 's|libdir=.*|libdir=${prefix}/lib|g' $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/pkgconfig/check.pc
+        # move files to globally included dirs
+        cp -R $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/* $LOCAL_PKG/lib/
+        cp -R $LOCAL_PKG/package/usr/include/* $LOCAL_PKG/include/
+        cp -R $LOCAL_PKG/package/usr/bin/* $LOCAL_PKG/
 
-	else
-		echo "\n## Using local packages from cache\n"
-	fi
+        # Install specific liburcu version which is not yet in the apt package
+        wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu2_0.8.5-1ubuntu1_amd64.deb
+        wget https://launchpad.net/ubuntu/+source/liburcu/0.8.5-1ubuntu1/+build/6513813/+files/liburcu-dev_0.8.5-1ubuntu1_amd64.deb
+        dpkg -x liburcu2_0.8.5-1ubuntu1_amd64.deb $LOCAL_PKG/package
+        dpkg -x liburcu-dev_0.8.5-1ubuntu1_amd64.deb $LOCAL_PKG/package
+        # move files to globally included dirs
+        cp -R $LOCAL_PKG/package/usr/lib/x86_64-linux-gnu/* $LOCAL_PKG/lib/
+        cp -R $LOCAL_PKG/package/usr/include/* $LOCAL_PKG/include/
 
+        # create cached flag
+        touch $LOCAL_PKG/.cached
 
+    else
+        echo "\n## Using local packages from cache\n"
+    fi
 
-	pip install --user cpp-coveralls
-	pip install --user sphinx
-	pip install --user sphinx_rtd_theme
+    pip install --user cpp-coveralls
+    pip install --user sphinx
+    pip install --user sphinx_rtd_theme
 fi

+ 84 - 84
tools/travis/travis_linux_script.sh

@@ -2,25 +2,25 @@
 set -ev
 
 if [ $ANALYZE = "true" ]; then
-	echo "\n=== Running static code analysis ==="
-	if [ "$CC" = "clang" ]; then
-		mkdir -p build
-		cd build
+    echo "\n=== Running static code analysis ==="
+    if [ "$CC" = "clang" ]; then
+        mkdir -p build
+        cd build
         scan-build cmake -G "Unix Makefiles" ..
         scan-build -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           make -j 8
-		cd .. && rm build -rf
+        cd .. && rm build -rf
 
-		mkdir -p build
-		cd build
+        mkdir -p build
+        cd build
         scan-build cmake -G "Unix Makefiles" -DUA_ENABLE_AMALGAMATION=ON ..
         scan-build -enable-checker security.FloatLoopCounter \
           -enable-checker security.insecureAPI.UncheckedReturn \
           --status-bugs -v \
           make -j 8
-		cd .. && rm build -rf
+        cd .. && rm build -rf
     else
         cppcheck --template "{file}({line}): {severity} ({id}): {message}" \
             --enable=style --force --std=c++11 -j 8 \
@@ -28,93 +28,93 @@ if [ $ANALYZE = "true" ]; then
             --suppress=invalidscanf --inline-suppr \
             -I include src plugins 2> cppcheck.txt
         if [ -s cppcheck.txt ]; then
-        	echo "\n\n====== CPPCHECK Static Analysis Errors ======"
+            echo "\n\n====== CPPCHECK Static Analysis Errors ======"
             cat cppcheck.txt
             exit 1
         fi
     fi
 else
-	echo "\n=== Building ==="
+    echo "\n=== Building ==="
 
-	echo "Documentation and certificate build"
-	mkdir -p build
-	cd build
-	cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
-	make doc
-	make selfsigned
-	cp -r doc ../../
-	cp server_cert.der ../../
-	cd .. && rm build -rf
+    echo "Documentation and certificate build"
+    mkdir -p build
+    cd build
+    cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
+    make doc
+    make selfsigned
+    cp -r doc ../../
+    cp server_cert.der ../../
+    cd .. && rm build -rf
 
-	# cross compilation only with gcc
-	if [ "$CC" = "gcc" ]; then
-		echo "Cross compile release build for MinGW 32 bit"
-		mkdir -p build && cd build
-		cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON ..
-		make -j8
-		zip -r open62541-win32.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
-		cp open62541-win32.zip ..
-		cd .. && rm build -rf
+    # cross compilation only with gcc
+    if [ "$CC" = "gcc" ]; then
+        echo "Cross compile release build for MinGW 32 bit"
+        mkdir -p build && cd build
+        cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON ..
+        make -j8
+        zip -r open62541-win32.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+        cp open62541-win32.zip ..
+        cd .. && rm build -rf
 
-		echo "Cross compile release build for MinGW 64 bit"
-		mkdir -p build && cd build
-		cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw64.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON ..
-		make -j8
-		zip -r open62541-win64.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
-		cp open62541-win64.zip ..
-		cd .. && rm build -rf
+        echo "Cross compile release build for MinGW 64 bit"
+        mkdir -p build && cd build
+        cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw64.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_EXAMPLES=ON ..
+        make -j8
+        zip -r open62541-win64.zip ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static.exe server.exe client.exe client_static.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+        cp open62541-win64.zip ..
+        cd .. && rm build -rf
 
-		echo "Cross compile release build for 32-bit linux"
-		mkdir -p build && cd build
-		cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-gcc-m32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON ..
-		make -j8
-		tar -pczf open62541-linux32.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
-		cp open62541-linux32.tar.gz ..
-		cd .. && rm build -rf
-	fi
+        echo "Cross compile release build for 32-bit linux"
+        mkdir -p build && cd build
+        cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-gcc-m32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON ..
+        make -j8
+        tar -pczf open62541-linux32.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
+        cp open62541-linux32.tar.gz ..
+        cd .. && rm build -rf
+    fi
+
+    echo "Compile release build for 64-bit linux"
+    mkdir -p build && cd build
+    cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON ..
+    make -j8
+    tar -pczf open62541-linux64.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
+    cp open62541-linux64.tar.gz ..
+    cp open62541.h .. #copy single file-release
+    cp open62541.c .. #copy single file-release
+    cd .. && rm build -rf
 
-	echo "Compile release build for 64-bit linux"
-	mkdir -p build && cd build
-	cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON ..
-	make -j8
-	tar -pczf open62541-linux64.tar.gz ../../doc ../../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
-	cp open62541-linux64.tar.gz ..
-	cp open62541.h .. #copy single file-release
-	cp open62541.c .. #copy single file-release
-	cd .. && rm build -rf
+    if [ "$CC" = "gcc" ]; then
+        echo "Upgrade to gcc 4.8"
+        export CXX="g++-4.8" CC="gcc-4.8"
+    fi
 
-	if [ "$CC" = "gcc" ]; then
-		echo "Upgrade to gcc 4.8"
-		export CXX="g++-4.8" CC="gcc-4.8"
-	fi
-	
-	echo "Building the C++ example"
-	mkdir -p build && cd build
-	cp ../open62541.* .
-	gcc-4.8 -std=c99 -c open62541.c
-	g++-4.8 ../examples/server.cpp -I./ open62541.o -lrt -o cpp-server
-	cd .. && rm build -rf
+    echo "Building the C++ example"
+    mkdir -p build && cd build
+    cp ../open62541.* .
+    gcc-4.8 -std=c99 -c open62541.c
+    g++-4.8 ../examples/server.cpp -I./ open62541.o -lrt -o cpp-server
+    cd .. && rm build -rf
 
-	echo "Compile multithreaded version"
-	mkdir -p build && cd build
-	cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLESERVER=ON ..
-	make -j8
-	cd .. && rm build -rf
+    echo "Compile multithreaded version"
+    mkdir -p build && cd build
+    cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLESERVER=ON ..
+    make -j8
+    cd .. && rm build -rf
 
-	#this run inclides full examples and methodcalls
-	echo "Debug build and unit tests (64 bit)"
-	mkdir -p build && cd build
-	cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_METHODCALLS=ON -DUA_BUILD_DEMO_NODESET=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
-	make -j8 && make test ARGS="-V"
-	echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
-	(valgrind --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
-	# only run coveralls on main repo, otherwise it fails uploading the files
-	echo "-> Current repo: ${TRAVIS_REPO_SLUG}"
-	if ([ "$CC" = "gcc-4.8" ] || [ "$CC" = "gcc" ]) && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
-		echo "	Building coveralls for ${TRAVIS_REPO_SLUG}"
-		coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../
-	else
-		echo "	Skipping coveralls since not gcc and/or ${TRAVIS_REPO_SLUG} is not the main repo"
-	fi
-	cd .. && rm build -rf
+    #this run inclides full examples and methodcalls
+    echo "Debug build and unit tests (64 bit)"
+    mkdir -p build && cd build
+    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_METHODCALLS=ON -DUA_BUILD_DEMO_NODESET=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
+    make -j8 && make test ARGS="-V"
+    echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
+    (valgrind --leak-check=yes --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
+    # only run coveralls on main repo, otherwise it fails uploading the files
+    echo "-> Current repo: ${TRAVIS_REPO_SLUG}"
+    if ([ "$CC" = "gcc-4.8" ] || [ "$CC" = "gcc" ]) && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
+        echo "  Building coveralls for ${TRAVIS_REPO_SLUG}"
+        coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../
+    else
+        echo "  Skipping coveralls since not gcc and/or ${TRAVIS_REPO_SLUG} is not the main repo"
+    fi
+    cd .. && rm build -rf
 fi

+ 30 - 31
tools/travis/travis_osx_script.sh

@@ -2,40 +2,39 @@
 set -ev
 
 if [ $ANALYZE = "true" ]; then
-	echo "\n=== Skipping static analysis on OS X ==="
-	exit 0
+    echo "\n=== Skipping static analysis on OS X ==="
+    exit 0
 else
-	echo "\n=== Building ==="
+    echo "\n=== Building ==="
 
-	echo "Create certificate for OS X build"
-	mkdir -p build && cd build
-	cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=OFF -DUA_BUILD_EXAMPLECLIENT=OFF -DUA_BUILD_EXAMPLES=OFF -DUA_BUILD_DOCUMENTATION=OFF -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
-	make selfsigned
-	cp server_cert.der ../
-	cd .. && rm -rf build
+    echo "Create certificate for OS X build"
+    mkdir -p build && cd build
+    cmake -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLESERVER=OFF -DUA_BUILD_EXAMPLECLIENT=OFF -DUA_BUILD_EXAMPLES=OFF -DUA_BUILD_DOCUMENTATION=OFF -DUA_BUILD_SELFSIGNED_CERTIFICATE=ON ..
+    make selfsigned
+    cp server_cert.der ../
+    cd .. && rm -rf build
 
-	echo "Compile release build for OS X"
-	mkdir -p build && cd build
-	cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_GENERATE_SELFSIGNED=ON ..
-	make -j8
-	tar -pczf open62541-osx.tar.gz ../doc ../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.dylib open62541.h open62541.c
-	cp open62541-osx.tar.gz ..
-	cp open62541.h .. #copy single file-release
-	cp open62541.c .. #copy single file-release
-	cd .. && rm -rf build
+    echo "Compile release build for OS X"
+    mkdir -p build && cd build
+    cmake -DCMAKE_BUILD_TYPE=Release -DUA_ENABLE_AMALGAMATION=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_BUILD_EXAMPLECLIENT=ON -DUA_BUILD_DOCUMENTATION=ON -DUA_GENERATE_SELFSIGNED=ON ..
+    make -j8
+    tar -pczf open62541-osx.tar.gz ../doc ../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.dylib open62541.h open62541.c
+    cp open62541-osx.tar.gz ..
+    cp open62541.h .. #copy single file-release
+    cp open62541.c .. #copy single file-release
+    cd .. && rm -rf build
 
-	echo "Compile multithreaded version"
-	mkdir -p build && cd build
-	cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLESERVER=ON ..
-	make -j8
-	cd .. && rm -rf build
-
-	echo "Debug build and unit tests (64 bit)"
-	mkdir -p build && cd build
-	cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_DEMO_NODESET=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
-	make -j8 && make test
-	echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
-	(valgrind --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
-	cd .. && rm -rf build
+    echo "Compile multithreaded version"
+    mkdir -p build && cd build
+    cmake -DUA_ENABLE_MULTITHREADING=ON -DUA_BUILD_EXAMPLESERVER=ON ..
+    make -j8
+    cd .. && rm -rf build
 
+    echo "Debug build and unit tests (64 bit)"
+    mkdir -p build && cd build
+    cmake -DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_DEMO_NODESET=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_BUILD_EXAMPLESERVER=ON -DUA_ENABLE_COVERAGE=ON ..
+    make -j8 && make test
+    echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
+    (valgrind --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);
+    cd .. && rm -rf build
 fi

+ 1 - 3
tools/travis/travis_push_coverity.sh

@@ -1,9 +1,7 @@
 #!/bin/bash
 
-#
-# This sciprt is run by travis-ci and pushes the first commit
+# This script is run by travis-ci and pushes the first commit
 # of the day to the coverity scan service
-#
 
 git fetch origin coverity_scan
 COMMITS=`git log --oneline --since=today.midnight | wc -l`