Browse Source

Merge branch '0.2'

Julius Pfrommer 7 years ago
parent
commit
4f23e1d3bd

+ 10 - 0
.travis.yml

@@ -22,6 +22,7 @@ env:
     # Do both, compile with static code analysis and without
     - ANALYZE=false
     - ANALYZE=true
+    - DOCKER=true
 
 matrix:
   exclude:
@@ -30,6 +31,15 @@ matrix:
       compiler: gcc
     - os: osx
       env: ANALYZE=true
+    - os: osx
+      env: DOCKER=true
+    - os: linux
+      compiler: clang
+      env: DOCKER=true
+
+# Required for docker build test
+services:
+  - docker
 
 addons:
   apt:

+ 1 - 1
CMakeLists.txt

@@ -39,7 +39,7 @@ if(GIT_FOUND)
         string(REPLACE "\n" "" OPEN62541_VER_COMMIT ${GIT_COM_ID} )
     endif()
 endif()
-if("${OPEN62541_VER_COMMIT}" STREQUAL "")
+if(NOT ${OPEN62541_VER_COMMIT} OR ${OPEN62541_VER_COMMIT} STREQUAL "")
     set(OPEN62541_VER_COMMIT "undefined")
 endif()
 

+ 6 - 4
Dockerfile

@@ -1,8 +1,10 @@
-FROM alpine:3.3
+FROM alpine:3.5
 RUN apk add --no-cache cmake gcc g++ musl-dev python make && rm -rf /var/cache/apk/*
 ADD . /tmp/open62541
 WORKDIR /tmp/open62541/build
-RUN cmake -D UA_ENABLE_AMALGAMATION=true /tmp/open62541 && make
+RUN cmake -DUA_ENABLE_AMALGAMATION=true  \
+          -DBUILD_SHARED_LIBS=true \
+          /tmp/open62541 
+RUN make -j
 RUN cp *.h /usr/include/ && \
-    cp *.so /usr/lib && \
-    cp *.a /usr/lib
+    cp *.so /usr/lib

+ 1 - 1
README.md

@@ -57,7 +57,7 @@ As an open source project, we invite new contributors to help improve open62541.
 
 ### Example Server Implementation
 Compile the examples with the single-file distribution `open62541.h/.c` header and source file.
-Using the GCC compiler, just run ```gcc -std=c99 <server.c> open62541.c -o server```.
+Using the GCC compiler, just run ```gcc -std=c99 <server.c> open62541.c -o server``` (under Windows you may need to add ``` -lws2_32```).
 ```c
 #include <signal.h>
 #include "open62541.h"

+ 4 - 4
TinyDockerfile

@@ -1,11 +1,11 @@
-FROM alpine:3.3
+FROM alpine:3.5
 ADD . /tmp/open62541
 WORKDIR /tmp/open62541/build
 RUN apk add --no-cache cmake gcc g++ musl-dev python make && rm -rf /var/cache/apk/* && \
-    cmake -D UA_ENABLE_AMALGAMATION=true /tmp/open62541 && \
+    cmake -DUA_ENABLE_AMALGAMATION=true \
+          -DBUILD_SHARED_LIBS=true /tmp/open62541 && \
     make && \
     cp *.h /usr/include/ && \
     cp *.so /usr/lib && \
-    cp *.a /usr/lib && \
     make clean && \
-    apk del cmake gcc musl-dev python make
+    apk del cmake gcc g++ musl-dev python make

+ 1 - 1
examples/server_readspeed.c

@@ -41,7 +41,7 @@ int main(int argc, char** argv) {
     rvi.nodeId = myIntegerNodeId;
     rvi.attributeId = UA_ATTRIBUTEID_VALUE;
     rvi.indexRange = UA_STRING_NULL;
-    rvi.dataEncoding = UA_QUALIFIEDNAME(0, "DefaultBinary");
+    rvi.dataEncoding = UA_QUALIFIEDNAME(0, "Default Binary");
     request.timestampsToReturn = UA_TIMESTAMPSTORETURN_NEITHER;
     request.nodesToReadSize = 1;
     request.nodesToRead = &rvi;

+ 4 - 4
include/ua_client.h

@@ -249,8 +249,8 @@ static UA_INLINE UA_AddReferencesResponse
 UA_Client_Service_addReferences(UA_Client *client,
                                 const UA_AddReferencesRequest request) {
     UA_AddReferencesResponse response;
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDNODESREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_ADDNODESRESPONSE]);
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_ADDREFERENCESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_ADDREFERENCESRESPONSE]);
     return response;
 }
 
@@ -267,8 +267,8 @@ static UA_INLINE UA_DeleteReferencesResponse
 UA_Client_Service_deleteReferences(UA_Client *client,
                                    const UA_DeleteReferencesRequest request) {
     UA_DeleteReferencesResponse response;
-    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETENODESREQUEST],
-                        &response, &UA_TYPES[UA_TYPES_DELETENODESRESPONSE]);
+    __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_DELETEREFERENCESREQUEST],
+                        &response, &UA_TYPES[UA_TYPES_DELETEREFERENCESRESPONSE]);
     return response;
 }
 

+ 46 - 28
include/ua_config.h.in

@@ -67,7 +67,13 @@ extern "C" {
 #endif
 
 #include <stddef.h>
-#include "ms_stdint.h" /* Includes stdint.h or workaround for older Visual Studios */
+
+/* Include stdint.h or workaround for older Visual Studios */
+#if !defined(_MSC_VER) || _MSC_VER >= 1600
+# include <stdint.h>
+#else
+# include "ms_stdint.h"
+#endif
 
 #ifndef __cplusplus
 # include <stdbool.h> /* C99 Boolean */
@@ -77,22 +83,15 @@ typedef uint8_t bool;
 #define false 0 */
 #endif
 
-/* Manually define some function on libc */
-#ifndef UA_ENABLE_EMBEDDED_LIBC
-# include <string.h>
-#else
-  void *memcpy(void *UA_RESTRICT dest, const void *UA_RESTRICT src, size_t n);
-  void *memset(void *dest, int c, size_t n);
-  size_t strlen(const char *s);
-  int memcmp(const void *vl, const void *vr, size_t n);
-#endif
-
-/* Memory Management */
 #include <stdlib.h>
-#ifdef _WIN32
-# ifndef __clang__
-#  include <malloc.h>
-# endif
+
+/**
+ * Memory Management
+ * ---------------
+ * The default malloc implementation from ``stdlib.h`` is used internally.
+ * Override if required. */
+#if defined(_WIN32) && !defined(__clang__)
+# include <malloc.h>
 #endif
 
 #define UA_free(ptr) free(ptr)
@@ -100,15 +99,13 @@ typedef uint8_t bool;
 #define UA_calloc(num, size) calloc(num, size)
 #define UA_realloc(ptr, size) realloc(ptr, size)
 
-#ifndef NO_ALLOCA
-# if defined(__GNUC__) || defined(__clang__)
-#  define UA_alloca(size) __builtin_alloca (size)
-# elif defined(_WIN32)
-#  define UA_alloca(SIZE) _alloca(SIZE)
-# else
-#  include <alloca.h>
-#  define UA_alloca(SIZE) alloca(SIZE)
-# endif
+#if defined(__GNUC__) || defined(__clang__)
+# define UA_alloca(size) __builtin_alloca (size)
+#elif defined(_WIN32)
+# define UA_alloca(SIZE) _alloca(SIZE)
+#else
+# include <alloca.h>
+# define UA_alloca(SIZE) alloca(SIZE)
 #endif
 
 /**
@@ -176,16 +173,33 @@ typedef uint8_t bool;
 # define UA_FUNC_ATTR_WARN_UNUSED_RESULT
 #endif
 
+/**
+ * String Manipulation
+ * -------------------
+ * The header ``string.h`` is defined in the C-standard. If no libc is provided
+ * (e.g. on some embedded target), use the following definitions and the
+ * implementation in ``/deps/libc_string.c`` */
+#ifndef UA_ENABLE_EMBEDDED_LIBC
+# include <string.h>
+#else
+  void *memcpy(void *UA_RESTRICT dest, const void *UA_RESTRICT src, size_t n);
+  void *memset(void *dest, int c, size_t n);
+  size_t strlen(const char *s);
+  int memcmp(const void *vl, const void *vr, size_t n);
+#endif
+
 /**
  * Binary Encoding Overlays
  * ------------------------
- * Integers and floating point numbers are transmitted in little-endian (IEE 754
+ * Integers and floating point numbers are transmitted in little-endian (IEEE 754
  * for floating point) encoding. If the target architecture uses the same
  * format, numeral datatypes can be memcpy'd (overlayed) on the binary stream.
  * This speeds up encoding.
  *
  * Integer Endianness
- * ^^^^^^^^^^^^^^^^^^ */
+ * ^^^^^^^^^^^^^^^^^^
+ * The definition ``UA_BINARY_OVERLAYABLE_INTEGER`` is true when the integer
+ * representation of the target architecture is little-endian. */
 #if defined(_WIN32) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
                         (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
 # define UA_BINARY_OVERLAYABLE_INTEGER 1
@@ -230,7 +244,11 @@ typedef uint8_t bool;
 
 /**
  * Float Endianness
- * ^^^^^^^^^^^^^^^^ */
+ * ^^^^^^^^^^^^^^^^
+ * The definition ``UA_BINARY_OVERLAYABLE_FLOAT`` is true when the floating
+ * point number representation of the target architecture is IEEE 754. Note that
+ * this cannot be reliable detected with macros for the clang compiler
+ * (beginning of 2017). Just override if necessary. */
 #if defined(_WIN32)
 # define UA_BINARY_OVERLAYABLE_FLOAT 1
 #elif defined(__FLOAT_WORD_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \

+ 21 - 25
include/ua_types.h

@@ -445,6 +445,27 @@ UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
     lt.text = UA_STRING_ALLOC(text); return lt;
 }
 
+/**
+ * .. _numericrange:
+ *
+ * NumericRange
+ * ^^^^^^^^^^^^
+ *
+ * NumericRanges are used to indicate subsets of a (multidimensional) array.
+ * They no official data type in the OPC UA standard and are transmitted only
+ * with a string encoding, such as "1:2,0:3,5". The colon separates min/max
+ * index and the comma separates dimensions. A single value indicates a range
+ * with a single element (min==max). */
+typedef struct {
+    UA_UInt32 min;
+    UA_UInt32 max;
+} UA_NumericRangeDimension;
+
+typedef struct  {
+    size_t dimensionsSize;
+    UA_NumericRangeDimension *dimensions;
+} UA_NumericRange;
+
 /**
  * .. _variant:
  *
@@ -483,10 +504,6 @@ UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
 struct UA_DataType;
 typedef struct UA_DataType UA_DataType;
 
-/* Forward declaration. See the section on Array Handling */
-struct UA_NumericRange;
-typedef struct UA_NumericRange UA_NumericRange;
-
 #define UA_EMPTY_ARRAY_SENTINEL ((void*)0x01)
 
 typedef enum {
@@ -858,27 +875,6 @@ UA_Array_copy(const void *src, size_t size, void **dst,
  * @param type The datatype of the array members */
 void UA_EXPORT UA_Array_delete(void *p, size_t size, const UA_DataType *type);
 
-/**
- * .. _numericrange:
- *
- * NumericRange
- * ^^^^^^^^^^^^
- *
- * NumericRanges are used to indicate subsets of a (multidimensional) variant
- * array. NumericRange has no official type structure in the standard. On the
- * wire, it only exists as an encoded string, such as "1:2,0:3,5". The colon
- * separates min/max index and the comma separates dimensions. A single value
- * indicates a range with a single element (min==max). */
-typedef struct {
-    UA_UInt32 min;
-    UA_UInt32 max;
-} UA_NumericRangeDimension;
-
-struct UA_NumericRange {
-    size_t dimensionsSize;
-    UA_NumericRangeDimension *dimensions;
-};
-
 /**
  * Random Number Generator
  * -----------------------

+ 77 - 39
plugins/ua_network_tcp.c

@@ -57,9 +57,11 @@
 #endif
 
 /* unsigned int for windows and workaround to a glibc bug */
+/* Additionally if GNU_LIBRARY is not defined, it may be using musl libc (e.g. Docker Alpine) */
 #if defined(_WIN32) || defined(__OpenBSD__) || \
     (defined(__GNU_LIBRARY__) && (__GNU_LIBRARY__ <= 6) && \
-     (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16))
+     (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16) || \
+    !defined(__GNU_LIBRARY__))
 # define UA_fd_set(fd, fds) FD_SET((unsigned int)fd, fds)
 # define UA_fd_isset(fd, fds) FD_ISSET((unsigned int)fd, fds)
 #else
@@ -357,7 +359,8 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
     ConnectionMapping *nm;
     nm  = (ConnectionMapping *)realloc(layer->mappings, sizeof(ConnectionMapping)*(layer->mappingsSize+1));
     if(!nm) {
-        UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK, "No memory for a new Connection");
+        UA_LOG_ERROR(layer->logger, UA_LOGCATEGORY_NETWORK,
+                     "No memory for a new Connection");
         free(c);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
@@ -442,19 +445,52 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_Logger logger) {
 }
 
 static size_t
-ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
-    ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP *)nl->handle;
+removeClosedConnections(ServerNetworkLayerTCP *layer, UA_Job *js) {
+    size_t c = 0;
+    for(size_t i = 0; i < layer->mappingsSize; ++i) {
+        if(layer->mappings[i].connection &&
+           layer->mappings[i].connection->state != UA_CONNECTION_CLOSED)
+            continue;
+        /* the socket was closed from remote */
+        UA_Connection *conn = layer->mappings[i].connection;
+        js[c].type = UA_JOBTYPE_DETACHCONNECTION;
+        js[c].job.closeConnection = conn;
+        layer->mappings[i] = layer->mappings[layer->mappingsSize-1];
+        --layer->mappingsSize;
+        ++c;
+        js[c].type = UA_JOBTYPE_METHODCALL_DELAYED;
+        js[c].job.methodCall.method = FreeConnectionCallback;
+        js[c].job.methodCall.data = conn;
+        ++c;
+    }
+    return c;
+}
+
+static size_t
+ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs,
+                              UA_UInt16 timeout) {
+    /* Every open socket can generate two jobs */
+    ServerNetworkLayerTCP *layer = nl->handle;
+    UA_Job *js = malloc(sizeof(UA_Job) * (size_t)((layer->mappingsSize * 2)));
+    if(!js)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    /* Remove closed sockets */
+    size_t totalJobs = removeClosedConnections(layer, js);
+
+    /* Listen on open sockets (including the server) */
     fd_set fdset, errset;
     UA_Int32 highestfd = setFDSet(layer, &fdset);
     setFDSet(layer, &errset);
     struct timeval tmptv = {0, timeout * 1000};
     UA_Int32 resultsize = select(highestfd+1, &fdset, NULL, &errset, &tmptv);
-    if(resultsize <= 0) {
+    if(totalJobs == 0 && resultsize <= 0) {
+        free(js);
         *jobs = NULL;
         return 0;
     }
 
-    /* accept new connections (can only be a single one) */
+    /* Accept new connection via the server socket (can only be a single one) */
     if(UA_fd_isset(layer->serversockfd, &fdset)) {
         --resultsize;
         SOCKET newsockfd = accept((SOCKET)layer->serversockfd, NULL, NULL);
@@ -465,23 +501,14 @@ ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt1
 #endif
         {
             socket_set_nonblocking(newsockfd);
-            /* Send messages directly and do wait to merge packets (disable
-               Nagle's algorithm) */
+            /* Do not merge packets on the socket (disable Nagle's algorithm) */
             int i = 1;
             setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (const char *)&i, sizeof(i));
             ServerNetworkLayerTCP_add(layer, (UA_Int32)newsockfd);
         }
     }
 
-    /* alloc enough space for a cleanup-connection and free-connection job per
-       resulted socket */
-    if(resultsize == 0)
-        return 0;
-    UA_Job *js = (UA_Job*)malloc(sizeof(UA_Job) * (size_t)resultsize * 2);
-    if(!js)
-        return 0;
-
-    /* read from established sockets */
+    /* Read from established sockets */
     size_t j = 0;
     UA_ByteString buf = UA_BYTESTRING_NULL;
     for(size_t i = 0; i < layer->mappingsSize && j < (size_t)resultsize; ++i) {
@@ -491,34 +518,34 @@ ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt1
 
         UA_StatusCode retval = socket_recv(layer->mappings[i].connection, &buf, 0);
         if(retval == UA_STATUSCODE_GOOD) {
-            js[j].job.binaryMessage.connection = layer->mappings[i].connection;
-            js[j].job.binaryMessage.message = buf;
-            js[j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER;
+            js[totalJobs + j].job.binaryMessage.connection = layer->mappings[i].connection;
+            js[totalJobs + j].job.binaryMessage.message = buf;
+            js[totalJobs + j].type = UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER;
             ++j;
         } else if (retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
             UA_Connection *c = layer->mappings[i].connection;
             UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
                         "Connection %i | Connection closed from remote", c->sockfd);
             /* the socket was closed from remote */
-            js[j].type = UA_JOBTYPE_DETACHCONNECTION;
-            js[j].job.closeConnection = layer->mappings[i].connection;
+            js[totalJobs + j].type = UA_JOBTYPE_DETACHCONNECTION;
+            js[totalJobs + j].job.closeConnection = c;
             layer->mappings[i] = layer->mappings[layer->mappingsSize-1];
             --layer->mappingsSize;
-            ++j;
-            js[j].type = UA_JOBTYPE_METHODCALL_DELAYED;
-            js[j].job.methodCall.method = FreeConnectionCallback;
-            js[j].job.methodCall.data = c;
+            ++totalJobs; /* increase j only once */
+            js[totalJobs + j].type = UA_JOBTYPE_METHODCALL_DELAYED;
+            js[totalJobs + j].job.methodCall.method = FreeConnectionCallback;
+            js[totalJobs + j].job.methodCall.data = c;
             ++j;
         }
     }
+    totalJobs += j;
 
-    if(j == 0) {
+    if(totalJobs == 0) {
         free(js);
         js = NULL;
     }
-
     *jobs = js;
-    return j;
+    return totalJobs;
 }
 
 static size_t
@@ -586,7 +613,8 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port) {
 /***************************/
 
 static UA_StatusCode
-ClientNetworkLayerGetBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
+ClientNetworkLayerGetBuffer(UA_Connection *connection, size_t length,
+                            UA_ByteString *buf) {
     if(length > connection->remoteConf.recvBufferSize)
         return UA_STATUSCODE_BADCOMMUNICATIONERROR;
     if(connection->state == UA_CONNECTION_CLOSED)
@@ -675,13 +703,15 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl,
     }
 
     /* Get a socket */
-    SOCKET clientsockfd = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
+    SOCKET clientsockfd = socket(server->ai_family, server->ai_socktype,
+                                 server->ai_protocol);
 #ifdef _WIN32
     if(clientsockfd == INVALID_SOCKET) {
 #else
     if(clientsockfd < 0) {
 #endif
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Could not create client socket");
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                       "Could not create client socket");
         freeaddrinfo(server);
         return connection;
     }
@@ -694,24 +724,32 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl,
         ClientNetworkLayerClose(&connection);
 #ifdef _WIN32
         wchar_t *s = NULL;
-        FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL, WSAGetLastError(),
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPWSTR)&s, 0, NULL);
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %s failed. Error: %d: %S", endpointUrl, WSAGetLastError(), s);
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                       "Connection to %s failed. Error: %d: %S",
+                       endpointUrl, WSAGetLastError(), s);
         LocalFree(s);
 #else
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Connection to %s failed. Error: %d: %s", endpointUrl, errno, strerror(errno));
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                       "Connection to %s failed. Error: %d: %s",
+                       endpointUrl, errno, strerror(errno));
 #endif
         return connection;
     }
 
 #ifdef SO_NOSIGPIPE
     int val = 1;
-    if(setsockopt(connection.sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof(val)) < 0) {
-        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK, "Couldn't set SO_NOSIGPIPE");
-        return connection;
-    }
+    int sso_result = setsockopt(connection.sockfd,
+                                SOL_SOCKET, SO_NOSIGPIPE,
+                                (void*)&val, sizeof(val));
+    if(sso_result < 0)
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                       "Couldn't set SO_NOSIGPIPE");
 #endif
 
     return connection;

+ 54 - 48
src/client/ua_client.c

@@ -65,7 +65,7 @@ void UA_Client_delete(UA_Client* client){
     UA_free(client);
 }
 
-UA_ClientState UA_EXPORT UA_Client_getState(UA_Client *client) {
+UA_ClientState UA_Client_getState(UA_Client *client) {
     if(!client)
         return UA_CLIENTSTATE_ERRORED;
     return client->state;
@@ -77,7 +77,8 @@ UA_ClientState UA_EXPORT UA_Client_getState(UA_Client *client) {
 
 #define UA_MINMESSAGESIZE 8192
 
-static UA_StatusCode HelAckHandshake(UA_Client *client) {
+static UA_StatusCode
+HelAckHandshake(UA_Client *client) {
     /* Get a buffer */
     UA_ByteString message;
     UA_Connection *conn = &client->connection;
@@ -175,32 +176,36 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
     if(conn->state != UA_CONNECTION_ESTABLISHED)
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 
-    UA_SecureConversationMessageHeader messageHeader;
-    messageHeader.messageHeader.messageTypeAndChunkType =
-        UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
-    if(renew)
-        messageHeader.secureChannelId = client->channel.securityToken.channelId;
-    else
-        messageHeader.secureChannelId = 0;
+    UA_ByteString message;
+    UA_StatusCode retval = conn->getSendBuffer(conn, conn->remoteConf.recvBufferSize, &message);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 
-    UA_SequenceHeader seqHeader;
-    seqHeader.sequenceNumber = ++client->channel.sendSequenceNumber;
-    seqHeader.requestId = ++client->requestId;
+    /* Jump over the messageHeader that will be encoded last */
+    size_t offset = 12;
 
+    /* Encode the Asymmetric Security Header */
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
-    asymHeader.securityPolicyUri =
-        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
+    asymHeader.securityPolicyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+    retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
+
+    /* Encode the sequence header */
+    UA_SequenceHeader seqHeader;
+    seqHeader.sequenceNumber = ++client->channel.sendSequenceNumber;
+    seqHeader.requestId = ++client->requestId;
+    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
 
-    /* id of opensecurechannelrequest */
+    /* Encode the NodeId of the OpenSecureChannel Service */
     UA_NodeId requestType =
         UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId);
+    retval |= UA_NodeId_encodeBinary(&requestType, &message, &offset);
 
+    /* Encode the OpenSecureChannelRequest */
     UA_OpenSecureChannelRequest opnSecRq;
     UA_OpenSecureChannelRequest_init(&opnSecRq);
     opnSecRq.requestHeader.timestamp = UA_DateTime_now();
     opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
-    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
     if(renew) {
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
@@ -210,39 +215,35 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
         UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
                      "Requesting to open a SecureChannel");
     }
-
-    UA_ByteString_copy(&client->channel.clientNonce, &opnSecRq.clientNonce);
     opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
-
-    UA_ByteString message;
-    UA_StatusCode retval = conn->getSendBuffer(conn, conn->remoteConf.recvBufferSize, &message);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-        UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
-        return retval;
-    }
-
-    size_t offset = 12;
-    retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
-    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
-    retval |= UA_NodeId_encodeBinary(&requestType, &message, &offset);
+    opnSecRq.clientNonce = client->channel.clientNonce;
+    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
     retval |= UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
+
+    /* Encode the message header at the beginning */
+    UA_SecureConversationMessageHeader messageHeader;
+    messageHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
     messageHeader.messageHeader.messageSize = (UA_UInt32)offset;
+    if(renew)
+        messageHeader.secureChannelId = client->channel.securityToken.channelId;
+    else
+        messageHeader.secureChannelId = 0;
     offset = 0;
     retval |= UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
 
-    UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
-    UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
+    /* Clean up and return if encoding the message failed */
     if(retval != UA_STATUSCODE_GOOD) {
         client->connection.releaseSendBuffer(&client->connection, &message);
         return retval;
     }
 
+    /* Send the message */
     message.length = messageHeader.messageHeader.messageSize;
     retval = conn->send(conn, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
+    /* Receive the response */
     UA_ByteString reply = UA_BYTESTRING_NULL;
     UA_Boolean realloced = false;
     retval = UA_Connection_receiveChunksBlocking(conn, &reply, &realloced,
@@ -255,13 +256,13 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
 
     /* Decode the header */
     offset = 0;
-    UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
-    UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
-    UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
-    UA_NodeId_decodeBinary(&reply, &offset, &requestType);
+    retval = UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
+    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
+    retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
+    retval |= UA_NodeId_decodeBinary(&reply, &offset, &requestType);
     UA_NodeId expectedRequest =
         UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
-    if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
+    if(retval != UA_STATUSCODE_GOOD || !UA_NodeId_equal(&requestType, &expectedRequest)) {
         UA_ByteString_deleteMembers(&reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
@@ -317,15 +318,15 @@ SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
     }
 
     /* Clean up */
-    UA_OpenSecureChannelResponse_deleteMembers(&response);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+    UA_OpenSecureChannelResponse_deleteMembers(&response);
     return retval;
 }
 
-static UA_StatusCode ActivateSession(UA_Client *client) {
+static UA_StatusCode
+ActivateSession(UA_Client *client) {
     UA_ActivateSessionRequest request;
     UA_ActivateSessionRequest_init(&request);
-
     request.requestHeader.requestHandle = ++client->requestHandle;
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 600000;
@@ -355,8 +356,8 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
 
     if(response.responseHeader.serviceResult) {
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "ActivateSession failed with statuscode 0x%08x",
-                     response.responseHeader.serviceResult);
+                     "ActivateSession failed with error code %s",
+                     UA_StatusCode_name(response.responseHeader.serviceResult));
     }
 
     UA_StatusCode retval = response.responseHeader.serviceResult;
@@ -384,7 +385,8 @@ __UA_Client_getEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
     if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_StatusCode retval = response.responseHeader.serviceResult;
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "GetEndpointRequest failed with statuscode 0x%08x", retval);
+                     "GetEndpointRequest failed with error code %s",
+                     UA_StatusCode_name(retval));
         UA_GetEndpointsResponse_deleteMembers(&response);
         return retval;
     }
@@ -396,7 +398,8 @@ __UA_Client_getEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode EndpointsHandshake(UA_Client *client) {
+static UA_StatusCode
+EndpointsHandshake(UA_Client *client) {
     UA_EndpointDescription* endpointArray = NULL;
     size_t endpointArraySize = 0;
     UA_StatusCode retval = __UA_Client_getEndpoints(client, &endpointArraySize, &endpointArray);
@@ -461,7 +464,8 @@ static UA_StatusCode EndpointsHandshake(UA_Client *client) {
     return retval;
 }
 
-static UA_StatusCode SessionHandshake(UA_Client *client) {
+static UA_StatusCode
+SessionHandshake(UA_Client *client) {
     UA_CreateSessionRequest request;
     UA_CreateSessionRequest_init(&request);
 
@@ -485,7 +489,8 @@ static UA_StatusCode SessionHandshake(UA_Client *client) {
     return retval;
 }
 
-static UA_StatusCode CloseSession(UA_Client *client) {
+static UA_StatusCode
+CloseSession(UA_Client *client) {
     UA_CloseSessionRequest request;
     UA_CloseSessionRequest_init(&request);
 
@@ -502,7 +507,8 @@ static UA_StatusCode CloseSession(UA_Client *client) {
     return retval;
 }
 
-static UA_StatusCode CloseSecureChannel(UA_Client *client) {
+static UA_StatusCode
+CloseSecureChannel(UA_Client *client) {
     UA_SecureChannel *channel = &client->channel;
     UA_CloseSecureChannelRequest request;
     UA_CloseSecureChannelRequest_init(&request);

+ 2 - 2
src/client/ua_client_highlevel.c

@@ -82,7 +82,7 @@ UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId,
 /* Node Management */
 /*******************/
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId,
                        const UA_NodeId referenceTypeId, UA_Boolean isForward,
                        const UA_String targetServerUri,
@@ -115,7 +115,7 @@ UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId,
     return retval;
 }
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId,
                           const UA_NodeId referenceTypeId, UA_Boolean isForward,
                           const UA_ExpandedNodeId targetNodeId,

+ 6 - 5
src/client/ua_client_highlevel_subscriptions.c

@@ -83,8 +83,8 @@ UA_Client_Subscriptions_remove(UA_Client *client, UA_UInt32 subscriptionId) {
 
     if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Could not remove subscription %u with statuscode 0x%08x",
-                    sub->subscriptionID, retval);
+                    "Could not remove subscription %u with error code %s",
+                    sub->subscriptionID, UA_StatusCode_name(retval));
         return retval;
     }
 
@@ -169,7 +169,8 @@ UA_Client_Subscriptions_addMonitoredItem(UA_Client *client, UA_UInt32 subscripti
     *newMonitoredItemId = newMon->monitoredItemId;
 
     UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                 "Created a monitored item with client handle %u", client->monitoredItemHandles);
+                 "Created a monitored item with client handle %u",
+                 client->monitoredItemHandles);
 
     UA_CreateMonitoredItemsResponse_deleteMembers(&response);
     return UA_STATUSCODE_GOOD;
@@ -209,8 +210,8 @@ UA_Client_Subscriptions_removeMonitoredItem(UA_Client *client, UA_UInt32 subscri
     if(retval != UA_STATUSCODE_GOOD &&
        retval != UA_STATUSCODE_BADMONITOREDITEMIDINVALID) {
         UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                    "Could not remove monitoreditem %u with statuscode 0x%08x",
-                    monitoredItemId, retval);
+                    "Could not remove monitoreditem %u with error code %s",
+                    monitoredItemId, UA_StatusCode_name(retval));
         return retval;
     }
 

+ 0 - 2
src/server/ua_nodes.c

@@ -4,8 +4,6 @@
 
 #include "ua_server_internal.h"
 #include "ua_nodes.h"
-#include "ua_nodestore.h"
-#include "ua_util.h"
 
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     /* delete standard content */

+ 0 - 1
src/server/ua_nodestore.h

@@ -9,7 +9,6 @@
 extern "C" {
 #endif
 
-#include "ua_types_generated.h"
 #include "ua_nodes.h"
 
 /**

+ 1 - 1
src/server/ua_server.c

@@ -79,7 +79,7 @@ UA_Server_deleteExternalNamespaces(UA_Server *server) {
     }
 }
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Server_addExternalNamespace(UA_Server *server, const UA_String *url,
                                UA_ExternalNodeStore *nodeStore,
                                UA_UInt16 *assignedNamespaceIndex) {

+ 9 - 23
src/server/ua_server_internal.h

@@ -41,13 +41,13 @@ extern "C" {
 #  define UA_ASSERT_RCU_UNLOCKED()
 # else
    extern UA_THREAD_LOCAL bool rcu_locked;
-#   define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
-#   define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
-#   define UA_RCU_LOCK() do {                     \
+#  define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
+#  define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
+#  define UA_RCU_LOCK() do {                      \
         UA_ASSERT_RCU_UNLOCKED();                 \
         rcu_locked = true;                        \
         rcu_read_lock(); } while(0)
-#   define UA_RCU_UNLOCK() do {                   \
+#  define UA_RCU_UNLOCK() do {                    \
         UA_ASSERT_RCU_LOCKED();                   \
         rcu_locked = false;                       \
         rcu_read_unlock(); } while(0)
@@ -205,13 +205,16 @@ struct UA_Server {
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
 UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
 
-typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node*, const void*);
-
 /* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
    the nodestore. */
+typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server*, UA_Session*, UA_Node*, const void*);
 UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
                                  UA_EditNodeCallback callback, const void *data);
 
+/********************/
+/* Event Processing */
+/********************/
+
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
                                     const UA_ByteString *message);
 
@@ -304,28 +307,11 @@ compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank);
 /* Some services take an array of "independent" requests. The single-services
  * are stored here to keep ua_services.h clean for documentation purposes. */
 
-UA_StatusCode
-Service_AddReferences_single(UA_Server *server, UA_Session *session,
-                             const UA_AddReferencesItem *item);
-
-UA_StatusCode
-Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
-                           const UA_NodeId *nodeId, UA_Boolean deleteReferences);
-
-UA_StatusCode
-Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
-                                const UA_DeleteReferencesItem *item);
-
 void Service_Browse_single(UA_Server *server, UA_Session *session,
                            struct ContinuationPointEntry *cp,
                            const UA_BrowseDescription *descr,
                            UA_UInt32 maxrefs, UA_BrowseResult *result);
 
-void
-Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session *session,
-                                             const UA_BrowsePath *path,
-                                             UA_BrowsePathResult *result);
-
 void Service_Read_single(UA_Server *server, UA_Session *session,
                          UA_TimestampsToReturn timestamps,
                          const UA_ReadValueId *id, UA_DataValue *v);

+ 3 - 9
src/server/ua_server_ns0.c

@@ -173,15 +173,9 @@ readMonitoredItems(void *handle, const UA_NodeId *objectId,
 static void
 addReferenceInternal(UA_Server *server, UA_UInt32 sourceId, UA_UInt32 refTypeId,
                      UA_UInt32 targetId, UA_Boolean isForward) {
-    UA_AddReferencesItem item;
-    UA_AddReferencesItem_init(&item);
-    item.sourceNodeId.identifier.numeric = sourceId;
-    item.referenceTypeId.identifier.numeric = refTypeId;
-    item.isForward = isForward;
-    item.targetNodeId.nodeId.identifier.numeric = targetId;
-    UA_RCU_LOCK();
-    Service_AddReferences_single(server, &adminSession, &item);
-    UA_RCU_UNLOCK();
+    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, sourceId),
+                           UA_NODEID_NUMERIC(0, refTypeId),
+                           UA_EXPANDEDNODEID_NUMERIC(0, targetId), isForward);
 }
 
 static void

+ 0 - 1
src/server/ua_services.h

@@ -11,7 +11,6 @@ extern "C" {
 
 #include "ua_server.h"
 #include "ua_session.h"
-#include "ua_nodes.h"
 
 /**
  * .. _services:

+ 2 - 2
src/server/ua_services_attribute.c

@@ -709,8 +709,8 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
 /* Read Service */
 /****************/
 
-static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
-/* static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"}; */
+static const UA_String binEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
+/* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
 #define CHECK_NODECLASS(CLASS)                                  \
     if(!(node->nodeClass & (CLASS))) {                          \

+ 54 - 43
src/server/ua_services_nodemanagement.c

@@ -5,6 +5,22 @@
 #include "ua_server_internal.h"
 #include "ua_services.h"
 
+/************************/
+/* Forward Declarations */
+/************************/
+
+static UA_StatusCode
+addReference(UA_Server *server, UA_Session *session,
+             const UA_AddReferencesItem *item);
+
+static UA_StatusCode
+deleteReference(UA_Server *server, UA_Session *session,
+                const UA_DeleteReferencesItem *item);
+
+static UA_StatusCode
+deleteNode(UA_Server *server, UA_Session *session,
+           const UA_NodeId *nodeId, UA_Boolean deleteReferences);
+
 /**********************/
 /* Consistency Checks */
 /**********************/
@@ -114,8 +130,8 @@ typeCheckVariableNodeWithValue(UA_Server *server, UA_Session *session,
         return retval;
 
     /* Check array dimensions against the vt */
-    retval = compatibleArrayDimensions(node->arrayDimensionsSize, node->arrayDimensions,
-                                       vt->arrayDimensionsSize, vt->arrayDimensions);
+    retval = compatibleArrayDimensions(vt->arrayDimensionsSize, vt->arrayDimensions,
+                                       node->arrayDimensionsSize, node->arrayDimensions);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
@@ -335,7 +351,7 @@ copyChildNode(UA_Server *server, UA_Session *session,
         newItem.isForward = true;
         newItem.targetNodeId = rd->nodeId;
         newItem.targetNodeClass = UA_NODECLASS_METHOD;
-        retval = Service_AddReferences_single(server, session, &newItem);
+        retval = addReference(server, session, &newItem);
     } else if(rd->nodeClass == UA_NODECLASS_VARIABLE ||
               rd->nodeClass == UA_NODECLASS_OBJECT) {
         /* Copy the node */
@@ -476,7 +492,7 @@ instantiateNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
     addref.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION);
     addref.isForward = true;
     addref.targetNodeId.nodeId = *typeId;
-    retval = Service_AddReferences_single(server, session, &addref);
+    retval = addReference(server, session, &addref);
 
     /* Call custom callback */
     if(retval == UA_STATUSCODE_GOOD && instantiationCallback)
@@ -684,7 +700,7 @@ Service_AddNode_begin(UA_Server *server, UA_Session *session,
     if(result->statusCode != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: Could not copy the nodeid");
-        Service_DeleteNodes_single(server, &adminSession, &node->nodeId, true);
+        deleteNode(server, &adminSession, &node->nodeId, true);
     }
 }
 
@@ -727,7 +743,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
     if(retval != UA_STATUSCODE_GOOD) {
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: The parent reference is invalid");
-        Service_DeleteNodes_single(server, &adminSession, nodeId, true);
+        deleteNode(server, &adminSession, nodeId, true);
         return retval;
     }
 
@@ -739,7 +755,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
         UA_LOG_INFO_SESSION(server->config.logger, session,
                             "AddNodes: Node instantiation failed "
                             "with code %s", UA_StatusCode_name(retval));
-        Service_DeleteNodes_single(server, &adminSession, nodeId, true);
+        deleteNode(server, &adminSession, nodeId, true);
         return retval;
     }
     
@@ -751,7 +767,7 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Type checking failed with error code %s",
                                 UA_StatusCode_name(retval));
-            Service_DeleteNodes_single(server, &adminSession, nodeId, true);
+            deleteNode(server, &adminSession, nodeId, true);
             return retval;
         }
     }
@@ -764,11 +780,11 @@ Service_AddNode_finish(UA_Server *server, UA_Session *session, const UA_NodeId *
         ref_item.referenceTypeId = *referenceTypeId;
         ref_item.isForward = false;
         ref_item.targetNodeId.nodeId = *parentNodeId;
-        retval = Service_AddReferences_single(server, session, &ref_item);
+        retval = addReference(server, session, &ref_item);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_LOG_INFO_SESSION(server->config.logger, session,
                                 "AddNodes: Adding reference to parent failed");
-            Service_DeleteNodes_single(server, &adminSession, nodeId, true);
+            deleteNode(server, &adminSession, nodeId, true);
             return retval;
         }
     }
@@ -1035,7 +1051,7 @@ UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
     UA_StatusCode retval = br.statusCode;
     if(retval != UA_STATUSCODE_GOOD) {
         UA_RCU_LOCK();
-        Service_DeleteNodes_single(server, &adminSession, &nodeId, true);
+        deleteNode(server, &adminSession, &nodeId, true);
         UA_RCU_UNLOCK();
         UA_BrowseResult_deleteMembers(&br);
         return retval;
@@ -1097,9 +1113,9 @@ UA_Server_addMethodNode_finish(UA_Server *server, const UA_NodeId nodeId,
     UA_RCU_UNLOCK();
 
     if(retval != UA_STATUSCODE_GOOD) {
-        Service_DeleteNodes_single(server, &adminSession, &nodeId, true);
-        Service_DeleteNodes_single(server, &adminSession, &inputArgsId, true);
-        Service_DeleteNodes_single(server, &adminSession, &outputArgsId, true);
+        deleteNode(server, &adminSession, &nodeId, true);
+        deleteNode(server, &adminSession, &inputArgsId, true);
+        deleteNode(server, &adminSession, &outputArgsId, true);
     }
     UA_BrowseResult_deleteMembers(&br);
     return retval;
@@ -1176,9 +1192,9 @@ addOneWayReference(UA_Server *server, UA_Session *session,
     return retval;
 }
 
-UA_StatusCode
-Service_AddReferences_single(UA_Server *server, UA_Session *session,
-                             const UA_AddReferencesItem *item) {
+static UA_StatusCode
+addReference(UA_Server *server, UA_Session *session,
+             const UA_AddReferencesItem *item) {
     /* Currently no expandednodeids are allowed */
     if(item->targetServerUri.length > 0)
         return UA_STATUSCODE_BADNOTIMPLEMENTED;
@@ -1286,7 +1302,7 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
 #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
     for(size_t i = 0; i < response->resultsSize; ++i)
         response->results[i] =
-            Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
+            addReference(server, session, &request->referencesToAdd[i]);
 #else
     size_t size = request->referencesToAddSize;
 # ifdef NO_ALLOCA
@@ -1317,7 +1333,7 @@ void Service_AddReferences(UA_Server *server, UA_Session *session,
     for(size_t i = 0; i < response->resultsSize; ++i) {
         if(!isExternal[i])
             response->results[i] =
-                Service_AddReferences_single(server, session, &request->referencesToAdd[i]);
+                addReference(server, session, &request->referencesToAdd[i]);
     }
 #endif
 }
@@ -1334,7 +1350,7 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
     item.isForward = isForward;
     item.targetNodeId = targetId;
     UA_RCU_LOCK();
-    UA_StatusCode retval = Service_AddReferences_single(server, &adminSession, &item);
+    UA_StatusCode retval = addReference(server, &adminSession, &item);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1352,14 +1368,13 @@ removeReferences(UA_Server *server, UA_Session *session, const UA_Node *node) {
         item.isForward = node->references[i].isInverse;
         item.sourceNodeId = node->references[i].targetId.nodeId;
         item.referenceTypeId = node->references[i].referenceTypeId;
-        Service_DeleteReferences_single(server, session, &item);
+        deleteReference(server, session, &item);
     }
 }
 
-UA_StatusCode
-Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
-                           const UA_NodeId *nodeId,
-                           UA_Boolean deleteReferences) {
+static UA_StatusCode
+deleteNode(UA_Server *server, UA_Session *session,
+           const UA_NodeId *nodeId, UA_Boolean deleteReferences) {
     const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
@@ -1406,9 +1421,8 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session,
 
     for(size_t i = 0; i < request->nodesToDeleteSize; ++i) {
         UA_DeleteNodesItem *item = &request->nodesToDelete[i];
-        response->results[i] =
-            Service_DeleteNodes_single(server, session, &item->nodeId,
-                                       item->deleteTargetReferences);
+        response->results[i] = deleteNode(server, session, &item->nodeId,
+                                          item->deleteTargetReferences);
     }
 }
 
@@ -1416,8 +1430,8 @@ UA_StatusCode
 UA_Server_deleteNode(UA_Server *server, const UA_NodeId nodeId,
                      UA_Boolean deleteReferences) {
     UA_RCU_LOCK();
-    UA_StatusCode retval = Service_DeleteNodes_single(server, &adminSession,
-                                                      &nodeId, deleteReferences);
+    UA_StatusCode retval = deleteNode(server, &adminSession,
+                                      &nodeId, deleteReferences);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1456,12 +1470,11 @@ deleteOneWayReference(UA_Server *server, UA_Session *session, UA_Node *node,
     return UA_STATUSCODE_GOOD;;
 }
 
-UA_StatusCode
-Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
-                                const UA_DeleteReferencesItem *item) {
-    UA_StatusCode retval =
-        UA_Server_editNode(server, session, &item->sourceNodeId,
-                           (UA_EditNodeCallback)deleteOneWayReference, item);
+static UA_StatusCode
+deleteReference(UA_Server *server, UA_Session *session,
+                const UA_DeleteReferencesItem *item) {
+    UA_StatusCode retval = UA_Server_editNode(server, session, &item->sourceNodeId,
+                                              (UA_EditNodeCallback)deleteOneWayReference, item);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     if(!item->deleteBidirectional || item->targetNodeId.serverIndex != 0)
@@ -1498,8 +1511,7 @@ Service_DeleteReferences(UA_Server *server, UA_Session *session,
 
     for(size_t i = 0; i < size; ++i)
         response->results[i] =
-            Service_DeleteReferences_single(server, session,
-                                            &request->referencesToDelete[i]);
+            deleteReference(server, session, &request->referencesToDelete[i]);
 }
 
 UA_StatusCode
@@ -1514,8 +1526,7 @@ UA_Server_deleteReference(UA_Server *server, const UA_NodeId sourceNodeId,
     item.targetNodeId = targetNodeId;
     item.deleteBidirectional = deleteBidirectional;
     UA_RCU_LOCK();
-    UA_StatusCode retval =
-        Service_DeleteReferences_single(server, &adminSession, &item);
+    UA_StatusCode retval = deleteReference(server, &adminSession, &item);
     UA_RCU_UNLOCK();
     return retval;
 }
@@ -1533,7 +1544,7 @@ setValueCallback(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
                                         const UA_ValueCallback callback) {
     UA_RCU_LOCK();
@@ -1582,7 +1593,7 @@ setOLM(UA_Server *server, UA_Session *session,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
                                                 UA_ObjectLifecycleManagement olm) {
     UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
@@ -1613,7 +1624,7 @@ editMethodCallback(UA_Server *server, UA_Session* session,
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId,
                                  UA_MethodCallback method, void *handle) {
     addMethodCallback cb;

+ 1 - 0
src/server/ua_services_subscription.c

@@ -192,6 +192,7 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 }
 
 static const UA_String binaryEncoding = {sizeof("Default Binary")-1, (UA_Byte*)"Default Binary"};
+/* static const UA_String xmlEncoding = {sizeof("Default Xml")-1, (UA_Byte*)"Default Xml"}; */
 
 static void
 Service_CreateMonitoredItems_single(UA_Server *server, UA_Session *session,

+ 6 - 5
src/server/ua_services_view.c

@@ -521,8 +521,9 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     return retval;
 }
 
-void Service_TranslateBrowsePathsToNodeIds_single(UA_Server *server, UA_Session *session,
-                                                  const UA_BrowsePath *path, UA_BrowsePathResult *result) {
+static void
+translateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
+                              const UA_BrowsePath *path, UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -570,7 +571,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
     UA_RCU_LOCK();
-    Service_TranslateBrowsePathsToNodeIds_single(server, &adminSession, browsePath, &result);
+    translateBrowsePathsToNodeIds(server, &adminSession, browsePath, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -623,8 +624,8 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
 #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif
-            Service_TranslateBrowsePathsToNodeIds_single(server, session, &request->browsePaths[i],
-                                                         &response->results[i]);
+            translateBrowsePathsToNodeIds(server, session, &request->browsePaths[i],
+                                          &response->results[i]);
     }
 }
 

+ 2 - 2
src/ua_types_encoding_binary.h

@@ -9,12 +9,12 @@
 
 typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_ByteString *buf, size_t offset);
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_encodeBinary(const void *src, const UA_DataType *type,
                 UA_exchangeEncodeBuffer exchangeCallback, void *exchangeHandle,
                 UA_ByteString *dst, size_t *offset) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
 
-UA_StatusCode UA_EXPORT
+UA_StatusCode
 UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
                 const UA_DataType *type, size_t customTypesSize,
                 const UA_DataType *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT;

+ 4 - 1
tests/CMakeLists.txt

@@ -13,7 +13,6 @@ include_directories(${CHECK_INCLUDE_DIRS})
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/tests)
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/tests)
 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/tests)
-###
 
 set(TESTS_BINARY_DIR ${CMAKE_BINARY_DIR}/bin/tests)
 
@@ -173,3 +172,7 @@ add_test_valgrind(check_client ${TESTS_BINARY_DIR}/check_client)
 add_executable(check_client_subscriptions check_client_subscriptions.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_client_subscriptions ${LIBS})
 add_test_valgrind(check_client_subscriptions ${TESTS_BINARY_DIR}/check_client_subscriptions)
+
+add_executable(check_client_highlevel check_client_highlevel.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_client_highlevel ${LIBS})
+add_test_valgrind(check_client_highlevel ${TESTS_BINARY_DIR}/check_client_highlevel)

+ 419 - 0
tests/check_client_highlevel.c

@@ -0,0 +1,419 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <ua_types.h>
+
+#include "ua_server.h"
+#include "ua_client.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#include "check.h"
+
+UA_Server *server;
+UA_Boolean *running;
+UA_ServerNetworkLayer nl;
+pthread_t server_thread;
+
+UA_Client *client;
+
+#define CUSTOM_NS "http://open62541.org/ns/test"
+#define CUSTOM_NS_UPPER "http://open62541.org/ns/Test"
+
+
+static void *serverloop(void *_) {
+    while (*running)
+        UA_Server_run_iterate(server, true);
+    return NULL;
+}
+
+static void setup(void) {
+    running = UA_Boolean_new();
+    *running = true;
+    UA_ServerConfig config = UA_ServerConfig_standard;
+    nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    config.networkLayers = &nl;
+    config.networkLayersSize = 1;
+    server = UA_Server_new(config);
+
+    ck_assert_uint_eq(2, UA_Server_addNamespace(server, CUSTOM_NS));
+
+    UA_Server_run_startup(server);
+    pthread_create(&server_thread, NULL, serverloop, NULL);
+
+    client = UA_Client_new(UA_ClientConfig_standard);
+    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
+
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+}
+
+static void teardown(void) {
+    *running = false;
+    UA_Client_disconnect(client);
+    UA_Client_delete(client);
+    pthread_join(server_thread, NULL);
+    UA_Server_run_shutdown(server);
+    UA_Boolean_delete(running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+}
+
+START_TEST(Misc_State)
+    {
+
+        UA_ClientState state = UA_Client_getState(client);
+
+        ck_assert_uint_eq(state, UA_CLIENTSTATE_CONNECTED);
+
+    }
+END_TEST
+
+START_TEST(Misc_NamespaceGetIndex)
+    {
+        UA_UInt16 idx;
+
+        UA_String ns = UA_STRING(CUSTOM_NS);
+        UA_StatusCode retval = UA_Client_NamespaceGetIndex(client, &ns, &idx);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        ck_assert_uint_eq(idx, 2);
+
+
+        // namespace uri is case sensitive
+        ns = UA_STRING(CUSTOM_NS_UPPER);
+        retval = UA_Client_NamespaceGetIndex(client, &ns, &idx);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_BADNOTFOUND);
+    }
+END_TEST
+
+
+UA_NodeId newReferenceTypeId;
+UA_NodeId newObjectTypeId;
+UA_NodeId newDataTypeId;
+UA_NodeId newVariableTypeId;
+UA_NodeId newObjectId;
+UA_NodeId newVariableId;
+UA_NodeId newMethodId;
+UA_NodeId newViewId;
+
+START_TEST(Node_Add)
+    {
+        UA_StatusCode retval;
+
+
+        // Create custom reference type 'HasSubSubType' as child of HasSubtype
+        {
+            UA_ReferenceTypeAttributes attr;
+            UA_ReferenceTypeAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Some HasSubSubType");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "HasSubSubType");
+            retval = UA_Client_addReferenceTypeNode(client, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                    UA_QUALIFIEDNAME(1, "HasSubSubType"), attr, &newReferenceTypeId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+        // Create TestObjectType SubType within BaseObjectType
+        {
+
+            UA_ObjectTypeAttributes attr;
+            UA_ObjectTypeAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Some TestObjectType");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "TestObjectType");
+
+            retval = UA_Client_addObjectTypeNode(client, UA_NODEID_NULL,
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                 UA_QUALIFIEDNAME(1, "TestObjectType"), attr, &newObjectTypeId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+
+        // Create Int128 DataType within Integer Datatype
+        {
+
+            UA_DataTypeAttributes attr;
+            UA_DataTypeAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Some Int128");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "Int128");
+
+            retval = UA_Client_addDataTypeNode(client, UA_NODEID_NULL,
+                                               UA_NODEID_NUMERIC(0, UA_NS0ID_INTEGER),
+                                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                               UA_QUALIFIEDNAME(1, "Int128"), attr, &newDataTypeId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+
+
+        // Create PointType VariableType within BaseDataVariableType
+        {
+
+            UA_VariableTypeAttributes attr;
+            UA_VariableTypeAttributes_init(&attr);
+
+            attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+            attr.valueRank = 1; /* array with one dimension */
+            UA_UInt32 arrayDims[1] = {2};
+            attr.arrayDimensions = arrayDims;
+            attr.arrayDimensionsSize = 1;
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "PointType");
+
+            /* a matching default value is required */
+            UA_Double zero[2] = {0.0, 0.0};
+            UA_Variant_setArray(&attr.value, zero, 2, &UA_TYPES[UA_TYPES_INT32]);
+
+            retval = UA_Client_addVariableTypeNode(client, UA_NODEID_NULL,
+                                                   UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE),
+                                                   UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                   UA_QUALIFIEDNAME(1, "PointType"), attr, &newVariableTypeId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+        // create Coordinates Object within ObjectsFolder
+        {
+            UA_ObjectAttributes attr;
+            UA_ObjectAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Some Coordinates");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "Coordinates");
+
+            retval = UA_Client_addObjectNode(client, UA_NODEID_NULL,
+                                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                             UA_QUALIFIEDNAME(1, "Coordinates"),
+                                             UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), attr, &newObjectId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+
+        // create Variable 'Top' within Coordinates Object
+        {
+            UA_VariableAttributes attr;
+            UA_VariableAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Top Coordinate");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "Top");
+
+            UA_Int32 values[2];
+            values[1] = 10;
+            values[2] = 20;
+
+            UA_Variant_setArray(&attr.value, values, 2, &UA_TYPES[UA_TYPES_INT32]);
+            attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+            attr.valueRank = 1; /* array with one dimension */
+            UA_UInt32 arrayDims[1] = {2};
+            attr.arrayDimensions = arrayDims;
+            attr.arrayDimensionsSize = 1;
+
+            retval = UA_Client_addVariableNode(client, UA_NODEID_NULL,
+                                               newObjectId,
+                                               UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                                               UA_QUALIFIEDNAME(1, "Top"),
+                                               newVariableTypeId, attr, &newVariableId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+        // create Method 'Dummy' within Coordinates Object. Fails with BADNODECLASSINVALID
+        {
+            // creating a method from a client does not yet make much sense since the corresponding
+            // action code can not be set from the client side
+            UA_MethodAttributes attr;
+            UA_MethodAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "Dummy method");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "Dummy");
+            attr.executable = true;
+            attr.userExecutable = true;
+            retval = UA_Client_addMethodNode(client, UA_NODEID_NULL,
+                                             newObjectId,
+                                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASORDEREDCOMPONENT),
+                                             UA_QUALIFIEDNAME(1, "Dummy"),
+                                             attr, &newMethodId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
+        }
+
+        // create View 'AllTopCoordinates' whithin Views Folder
+        {
+            UA_ViewAttributes attr;
+            UA_ViewAttributes_init(&attr);
+            attr.description = UA_LOCALIZEDTEXT("en_US", "List of all top coordinates");
+            attr.displayName = UA_LOCALIZEDTEXT("en_US", "AllTopCoordinates");
+
+            retval = UA_Client_addViewNode(client, UA_NODEID_NULL,
+                                           UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
+                                           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                           UA_QUALIFIEDNAME(1, "AllTopCoordinates"),
+                                           attr, &newViewId);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+
+        // Add 'Top' to view
+        {
+
+            retval = UA_Client_addReference(client, newViewId, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                            UA_TRUE, UA_STRING_NULL,
+                                            UA_EXPANDEDNODEID_NUMERIC(1, newObjectId.identifier.numeric),
+                                            UA_NODECLASS_VARIABLE);
+
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        }
+
+        // Delete 'Top' from view
+        {
+
+            retval = UA_Client_deleteReference(client, newViewId, UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                               UA_TRUE, UA_EXPANDEDNODEID_NUMERIC(1, newObjectId.identifier.numeric), UA_TRUE);
+
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        }
+
+        // Delete 'AllTopCoordinates' view
+
+        {
+            retval = UA_Client_deleteNode(client, newViewId, UA_TRUE);
+
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+    }
+END_TEST
+
+START_TEST(Node_Browse)
+    {
+
+        // Browse node in server folder
+        {
+            UA_BrowseRequest bReq;
+            UA_BrowseRequest_init(&bReq);
+            // normally is set to 0, to get all the nodes, but we want to test browse next
+            bReq.requestedMaxReferencesPerNode = 1;
+            bReq.nodesToBrowse = UA_BrowseDescription_new();
+            bReq.nodesToBrowseSize = 1;
+            bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+            bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
+
+
+            UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
+            ck_assert_uint_eq(bResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bResp.resultsSize, 1);
+            ck_assert_uint_eq(bResp.results[0].statusCode, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bResp.results[0].referencesSize, 1);
+
+            /* References might have a different order in generated nodesets */
+            /* UA_ReferenceDescription *ref = &(bResp.results[0].references[0]); */
+            /* ck_assert_uint_eq(ref->nodeId.nodeId.identifier.numeric, UA_NS0ID_SERVERTYPE); */
+
+            // browse next
+            UA_BrowseNextRequest bNextReq;
+            UA_BrowseNextRequest_init(&bNextReq);
+            // normally is set to 0, to get all the nodes, but we want to test browse next
+            bNextReq.releaseContinuationPoints = UA_FALSE;
+            bNextReq.continuationPoints = &bResp.results[0].continuationPoint;
+            bNextReq.continuationPointsSize = 1;
+
+            UA_BrowseNextResponse bNextResp = UA_Client_Service_browseNext(client, bNextReq);
+
+            ck_assert_uint_eq(bNextResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bNextResp.resultsSize, 1);
+            ck_assert_uint_eq(bNextResp.results[0].statusCode, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bNextResp.results[0].referencesSize, 1);
+
+            /* ref = &(bNextResp.results[0].references[0]); */
+            /* ck_assert_uint_eq(ref->nodeId.nodeId.identifier.numeric, UA_NS0ID_SERVER_NAMESPACEARRAY); */
+
+            UA_BrowseNextResponse_deleteMembers(&bNextResp);
+
+            bNextResp = UA_Client_Service_browseNext(client, bNextReq);
+            ck_assert_uint_eq(bNextResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bNextResp.resultsSize, 1);
+            ck_assert_uint_eq(bNextResp.results[0].statusCode, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bNextResp.results[0].referencesSize, 1);
+
+            /* ref = &(bNextResp.results[0].references[0]); */
+            /* ck_assert_uint_eq(ref->nodeId.nodeId.identifier.numeric, UA_NS0ID_SERVER_SERVERARRAY); */
+
+            UA_BrowseNextResponse_deleteMembers(&bNextResp);
+
+            // release continuation point. Result is then empty
+            bNextReq.releaseContinuationPoints = UA_TRUE;
+            bNextResp = UA_Client_Service_browseNext(client, bNextReq);
+            UA_BrowseNextResponse_deleteMembers(&bNextResp);
+            ck_assert_uint_eq(bNextResp.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(bNextResp.resultsSize, 0);
+
+            UA_BrowseRequest_deleteMembers(&bReq);
+            UA_BrowseResponse_deleteMembers(&bResp);
+            // already deleted by browse request
+            bNextReq.continuationPoints = NULL;
+            bNextReq.continuationPointsSize = 0;
+            UA_BrowseNextRequest_deleteMembers(&bNextReq);
+        }
+    }
+END_TEST
+
+START_TEST(Node_Register)
+    {
+        {
+            UA_RegisterNodesRequest req;
+            UA_RegisterNodesRequest_init(&req);
+
+            req.nodesToRegister = UA_NodeId_new();
+            req.nodesToRegister[0] = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+            req.nodesToRegisterSize = 1;
+
+            UA_RegisterNodesResponse res = UA_Client_Service_registerNodes(client, req);
+
+            ck_assert_uint_eq(res.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+            ck_assert_uint_eq(res.registeredNodeIdsSize, 1);
+
+            UA_UnregisterNodesRequest reqUn;
+            UA_UnregisterNodesRequest_init(&reqUn);
+
+            reqUn.nodesToUnregister = UA_NodeId_new();
+            reqUn.nodesToUnregister[0] = res.registeredNodeIds[0];
+            reqUn.nodesToUnregisterSize = 1;
+
+            UA_UnregisterNodesResponse resUn = UA_Client_Service_unregisterNodes(client, reqUn);
+
+            ck_assert_uint_eq(resUn.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+
+
+            UA_UnregisterNodesRequest_deleteMembers(&reqUn);
+            UA_UnregisterNodesResponse_deleteMembers(&resUn);
+            UA_RegisterNodesRequest_deleteMembers(&req);
+            UA_RegisterNodesResponse_deleteMembers(&res);
+        }
+    }
+END_TEST
+
+
+static Suite *testSuite_Client(void) {
+    Suite *s = suite_create("Client Highlevel");
+    TCase *tc_misc = tcase_create("Client Highlevel Misc");
+    tcase_add_checked_fixture(tc_misc, setup, teardown);
+    tcase_add_test(tc_misc, Misc_State);
+    tcase_add_test(tc_misc, Misc_NamespaceGetIndex);
+    suite_add_tcase(s, tc_misc);
+
+    TCase *tc_nodes = tcase_create("Client Highlevel Node Management");
+    tcase_add_checked_fixture(tc_nodes, setup, teardown);
+    tcase_add_test(tc_nodes, Node_Add);
+    tcase_add_test(tc_nodes, Node_Browse);
+    tcase_add_test(tc_nodes, Node_Register);
+    suite_add_tcase(s, tc_nodes);
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 209 - 1
tests/check_services_subscriptions.c

@@ -23,6 +23,9 @@ static void teardown(void) {
     UA_Server_delete(server);
 }
 
+UA_UInt32 subscriptionId;
+UA_UInt32 monitoredItemId;
+
 START_TEST(Server_createSubscription) {
     /* Create a subscription */
     UA_CreateSubscriptionRequest request;
@@ -34,10 +37,89 @@ START_TEST(Server_createSubscription) {
 
     Service_CreateSubscription(server, &adminSession, &request, &response);
     ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
-    UA_UInt32 subscriptionId = response.subscriptionId;
+    subscriptionId = response.subscriptionId;
 
     UA_CreateSubscriptionResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_modifySubscription) {
+    UA_ModifySubscriptionRequest request;
+    UA_ModifySubscriptionRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    // just some arbitrary numbers to test. They have no specific reason
+    request.requestedPublishingInterval = 100; // in ms
+    request.requestedLifetimeCount = 1000;
+    request.requestedMaxKeepAliveCount = 1000;
+    request.maxNotificationsPerPublish = 1;
+    request.priority = 10;
+        
+
+    UA_ModifySubscriptionResponse response;
+    UA_ModifySubscriptionResponse_init(&response);
+
+    Service_ModifySubscription(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+
+    UA_ModifySubscriptionResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_setPublishingMode) {
+    UA_SetPublishingModeRequest request;
+    UA_SetPublishingModeRequest_init(&request);
+    request.publishingEnabled = UA_TRUE;
+    request.subscriptionIdsSize = 1;
+    request.subscriptionIds = &subscriptionId;
+
+    UA_SetPublishingModeResponse response;
+    UA_SetPublishingModeResponse_init(&response);
+
+    Service_SetPublishingMode(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(response.resultsSize, 1);
+    ck_assert_uint_eq(response.results[0], UA_STATUSCODE_GOOD);
+
+    UA_SetPublishingModeResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_republish) {
+    UA_RepublishRequest request;
+    UA_RepublishRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.retransmitSequenceNumber = 0;
 
+    UA_RepublishResponse response;
+    UA_RepublishResponse_init(&response);
+
+    Service_Republish(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_BADMESSAGENOTAVAILABLE);
+
+    UA_RepublishResponse_deleteMembers(&response);
+
+}
+END_TEST
+
+
+START_TEST(Server_republish_invalid) {
+    UA_RepublishRequest request;
+    UA_RepublishRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.retransmitSequenceNumber = 0;
+
+    UA_RepublishResponse response;
+    UA_RepublishResponse_init(&response);
+
+    Service_Republish(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID);
+
+    UA_RepublishResponse_deleteMembers(&response);
+
+}
+END_TEST
+
+START_TEST(Server_deleteSubscription) {
     /* Remove the subscription */
     UA_DeleteSubscriptionsRequest del_request;
     UA_DeleteSubscriptionsRequest_init(&del_request);
@@ -104,6 +186,7 @@ START_TEST(Server_publishCallback) {
     UA_DeleteSubscriptionsResponse_init(&del_response);
 
     Service_DeleteSubscriptions(server, &adminSession, &del_request, &del_response);
+    ck_assert_uint_eq(del_response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(del_response.resultsSize, 2);
     ck_assert_uint_eq(del_response.results[0], UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(del_response.results[1], UA_STATUSCODE_GOOD);
@@ -112,13 +195,138 @@ START_TEST(Server_publishCallback) {
 }
 END_TEST
 
+START_TEST(Server_createMonitoredItems) {
+
+    UA_CreateMonitoredItemsRequest request;
+    UA_CreateMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.timestampsToReturn = UA_TIMESTAMPSTORETURN_SERVER;
+    UA_MonitoredItemCreateRequest item;
+    UA_MonitoredItemCreateRequest_init(&item);
+    UA_ReadValueId rvi;
+    UA_ReadValueId_init(&rvi);
+    rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+    rvi.attributeId = UA_ATTRIBUTEID_BROWSENAME;
+    rvi.indexRange = UA_STRING_NULL;
+    item.itemToMonitor = rvi;
+    item.monitoringMode = UA_MONITORINGMODE_REPORTING;
+    UA_MonitoringParameters params;
+    UA_MonitoringParameters_init(&params);
+    item.requestedParameters = params;
+    request.itemsToCreateSize = 1;
+    request.itemsToCreate = &item;
+
+
+    UA_CreateMonitoredItemsResponse response;
+    UA_CreateMonitoredItemsResponse_init(&response);
+
+    Service_CreateMonitoredItems(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(response.resultsSize, 1);
+    ck_assert_uint_eq(response.results[0].statusCode, UA_STATUSCODE_GOOD);
+
+    monitoredItemId = response.results[0].monitoredItemId;
+
+    UA_MonitoredItemCreateRequest_deleteMembers(&item);
+
+    UA_CreateMonitoredItemsResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_modifyMonitoredItems) {
+    UA_ModifyMonitoredItemsRequest request;
+    UA_ModifyMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.timestampsToReturn = UA_TIMESTAMPSTORETURN_SERVER;
+    UA_MonitoredItemModifyRequest item;
+    UA_MonitoredItemModifyRequest_init(&item);
+    UA_ReadValueId rvi;
+    UA_ReadValueId_init(&rvi);
+    rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
+    rvi.attributeId = UA_ATTRIBUTEID_DESCRIPTION;
+    rvi.indexRange = UA_STRING_NULL;
+    item.monitoredItemId = monitoredItemId;
+    UA_MonitoringParameters params;
+    UA_MonitoringParameters_init(&params);
+    item.requestedParameters = params;
+    request.itemsToModifySize = 1;
+    request.itemsToModify = &item;
+
+
+    UA_ModifyMonitoredItemsResponse response;
+    UA_ModifyMonitoredItemsResponse_init(&response);
+
+    Service_ModifyMonitoredItems(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(response.resultsSize, 1);
+    ck_assert_uint_eq(response.results[0].statusCode, UA_STATUSCODE_GOOD);
+
+    UA_MonitoredItemModifyRequest_deleteMembers(&item);
+
+    UA_ModifyMonitoredItemsResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_setMonitoringMode) {
+    UA_SetMonitoringModeRequest request;
+    UA_SetMonitoringModeRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.monitoringMode = UA_MONITORINGMODE_DISABLED;
+    request.monitoredItemIdsSize = 1;
+    request.monitoredItemIds = &monitoredItemId;
+
+    UA_SetMonitoringModeResponse response;
+    UA_SetMonitoringModeResponse_init(&response);
+
+    Service_SetMonitoringMode(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(response.resultsSize, 1);
+    ck_assert_uint_eq(response.results[0], UA_STATUSCODE_GOOD);
+
+
+    UA_SetMonitoringModeResponse_deleteMembers(&response);
+}
+END_TEST
+
+START_TEST(Server_deleteMonitoredItems) {
+    UA_DeleteMonitoredItemsRequest request;
+    UA_DeleteMonitoredItemsRequest_init(&request);
+    request.subscriptionId = subscriptionId;
+    request.monitoredItemIdsSize = 1;
+    request.monitoredItemIds = &monitoredItemId;
+
+    UA_DeleteMonitoredItemsResponse response;
+    UA_DeleteMonitoredItemsResponse_init(&response);
+
+    Service_DeleteMonitoredItems(server, &adminSession, &request, &response);
+    ck_assert_uint_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+    ck_assert_uint_eq(response.resultsSize, 1);
+    ck_assert_uint_eq(response.results[0], UA_STATUSCODE_GOOD);
+
+
+    UA_DeleteMonitoredItemsResponse_deleteMembers(&response);
+
+}
+END_TEST
+
+
 static Suite* testSuite_Client(void) {
     Suite *s = suite_create("Server Subscription");
     TCase *tc_server = tcase_create("Server Subscription Basic");
     tcase_add_checked_fixture(tc_server, setup, teardown);
     tcase_add_test(tc_server, Server_createSubscription);
+    tcase_add_test(tc_server, Server_modifySubscription);
+    tcase_add_test(tc_server, Server_setPublishingMode);
+    tcase_add_test(tc_server, Server_createMonitoredItems);
+    tcase_add_test(tc_server, Server_modifyMonitoredItems);
+    tcase_add_test(tc_server, Server_setMonitoringMode);
+    tcase_add_test(tc_server, Server_deleteMonitoredItems);
+    tcase_add_test(tc_server, Server_republish);
+    tcase_add_test(tc_server, Server_deleteSubscription);
+    tcase_add_test(tc_server, Server_republish_invalid);
     tcase_add_test(tc_server, Server_publishCallback);
     suite_add_tcase(s, tc_server);
+
     return s;
 }
 

+ 1 - 1
tools/amalgamate.py

@@ -22,7 +22,7 @@ if outname[-2:] == ".c":
 pos = outname.find(".")
 if pos > 0:
     outname = outname[:pos]
-include_re = re.compile("^#include (\".*\").*$")
+include_re = re.compile("^#[\s]*include (\".*\").*$")
 guard_re = re.compile("^#(?:(?:ifndef|define)\s*[A-Z_]+_H_|endif /\* [A-Z_]+_H_ \*/|endif // [A-Z_]+_H_|endif\s*/\*\s*!?[A-Z_]+_H[_]+\s*\*/)")
 
 print ("Starting amalgamating file "+ args.outfile)

+ 1 - 3
tools/generate_datatypes.py

@@ -420,7 +420,7 @@ extern "C" {
 #endif
 
 #include "ua_types.h"
-''' + ('\n#include "ua_types_generated.h"\n' if outname != "ua_types" else ''))
+''' + ('#include "ua_types_generated.h"\n' if outname != "ua_types" else ''))
 
 printh('''/**
  * Every type is assigned an index in an array containing the type descriptions.
@@ -494,8 +494,6 @@ printc('''/* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '
  * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + \
        ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + ''' */
 
-#include "stddef.h"
-#include "ua_types.h"
 #include "''' + outname + '''_generated.h"''')
 
 for t in iter_types(types):

+ 13 - 9
tools/travis/travis_linux_after_success.sh

@@ -1,13 +1,17 @@
 #!/bin/bash
 set -ev
 
-if [ "$ANALYZE" = "false" ] && [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
-    echo "=== Executing after_success scripts ==="
-    if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "0.2" ]; then
-        sh ./tools/travis/travis_push_doc.sh
-        sh ./tools/travis/travis_push_coverity.sh
-    fi
-    sh ./tools/travis/travis_push_release.sh;
-else
-    echo "=== Not in the main repository or not the main build; Skip release scripts ==="
+if [ -z ${DOCKER+x} ]; then
+	# Only on non-docker builds required
+
+	if [ "$ANALYZE" = "false" ] && [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
+		echo "=== Executing after_success scripts ==="
+		if [ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "0.2" ]; then
+			sh ./tools/travis/travis_push_doc.sh
+			sh ./tools/travis/travis_push_coverity.sh
+		fi
+		sh ./tools/travis/travis_push_release.sh;
+	else
+		echo "=== Not in the main repository or not the main build; Skip release scripts ==="
+	fi
 fi

+ 24 - 19
tools/travis/travis_linux_before_install.sh

@@ -1,24 +1,29 @@
 #!/bin/bash
 set -ev
 
-echo "=== Installing from external package sources ===" && echo -en 'travis_fold:start:before_install.external\\r'
-wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
-echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee -a /etc/apt/sources.list
-sudo add-apt-repository -y ppa:lttng/ppa
-sudo apt-get update -qq
-sudo apt-get install -y clang-3.9 clang-tidy-3.9
-sudo apt-get install -y liburcu4 liburcu-dev
-echo -en 'travis_fold:end:script.before_install.external\\r'
+if [ -z ${DOCKER+x} ]; then
+	# Only on non-docker builds required
 
-echo "=== Installing python packages ===" && echo -en 'travis_fold:start:before_install.python\\r'
-pip install --user cpp-coveralls
-pip install --user sphinx
-pip install --user sphinx_rtd_theme
-echo -en 'travis_fold:end:script.before_install.python\\r'
+	echo "=== Installing from external package sources ===" && echo -en 'travis_fold:start:before_install.external\\r'
+	wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
+	echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee -a /etc/apt/sources.list
+	sudo add-apt-repository -y ppa:lttng/ppa
+	sudo apt-get update -qq
+	sudo apt-get install -y clang-3.9 clang-tidy-3.9
+	sudo apt-get install -y liburcu4 liburcu-dev
+	echo -en 'travis_fold:end:script.before_install.external\\r'
 
-echo "=== Installed versions are ===" && echo -en 'travis_fold:start:before_install.versions\\r'
-clang --version
-g++ --version
-cppcheck --version
-valgrind --version
-echo -en 'travis_fold:end:script.before_install.versions\\r'
+	echo "=== Installing python packages ===" && echo -en 'travis_fold:start:before_install.python\\r'
+	pip install --user cpp-coveralls
+	pip install --user sphinx
+	pip install --user sphinx_rtd_theme
+	echo -en 'travis_fold:end:script.before_install.python\\r'
+
+	echo "=== Installed versions are ===" && echo -en 'travis_fold:start:before_install.versions\\r'
+	clang --version
+	g++ --version
+	cppcheck --version
+	valgrind --version
+	echo -en 'travis_fold:end:script.before_install.versions\\r'
+
+fi

+ 10 - 0
tools/travis/travis_linux_script.sh

@@ -1,6 +1,16 @@
 #!/bin/bash
 set -ev
 
+# Docker build test
+if ! [ -z ${DOCKER+x} ]; then
+    docker build -t open62541 .
+    docker run -d -p 127.0.0.1:80:80 --name open62541 open62541 /bin/sh
+    docker ps | grep -q open62541
+    # disabled since it randomly fails
+    # docker ps | grep -q open62541
+    exit 0
+fi
+
 if [ $ANALYZE = "true" ]; then
     echo "=== Running static code analysis ===" && echo -en 'travis_fold:start:script.analyze\\r'
     if [ "$CC" = "clang" ]; then