浏览代码

Merge branch 'master' into dev

Conflicts:
	CMakeLists.txt
	README.md
	examples/server.c
	src/server/ua_securechannel_manager.c
	src/server/ua_server.c
	src/server/ua_server_binary.c
	src/server/ua_server_internal.h
	src/server/ua_server_worker.c
	src/server/ua_services_session.c
	src/server/ua_session_manager.c
	src/ua_session.c
	tools/pyUANamespace/generate_open62541CCode.py
	tools/pyUANamespace/logger.py
	tools/pyUANamespace/open62541_MacroHelper.py
	tools/pyUANamespace/ua_builtin_types.py
	tools/pyUANamespace/ua_constants.py
	tools/pyUANamespace/ua_namespace.py
	tools/pyUANamespace/ua_node_types.py
Julius Pfrommer 9 年之前
父节点
当前提交
b125569533
共有 49 个文件被更改,包括 1753 次插入24348 次删除
  1. 23 14
      .travis.yml
  2. 74 74
      CMakeLists.txt
  3. 7 6
      README.md
  4. 15 27
      examples/logger_stdout.c
  5. 2 2
      examples/logger_stdout.h
  6. 32 18
      examples/networklayer_tcp.c
  7. 2 2
      examples/networklayer_tcp.h
  8. 2 2
      examples/networklayer_udp.c
  9. 105 50
      examples/server.c
  10. 6 3
      examples/server.cpp
  11. 7 7
      examples/server_simple.c
  12. 0 1
      examples/server_udp.c
  13. 6 2
      include/ua_client.h
  14. 9 0
      src/ua_config.h.in
  15. 23 25
      include/ua_log.h
  16. 65 16
      include/ua_server.h
  17. 41 81
      include/ua_types.h
  18. 50 26
      src/client/ua_client.c
  19. 2 2
      src/server/ua_nodestore_hash.inc
  20. 54 45
      src/server/ua_securechannel_manager.c
  21. 1 0
      src/server/ua_securechannel_manager.h
  22. 339 265
      src/server/ua_server.c
  23. 44 13
      src/server/ua_server_addressspace.c
  24. 51 28
      src/server/ua_server_binary.c
  25. 6 5
      src/server/ua_server_internal.h
  26. 137 91
      src/server/ua_server_worker.c
  27. 44 36
      src/server/ua_services_attribute.c
  28. 1 6
      src/server/ua_services_discovery.c
  29. 13 8
      src/server/ua_services_nodemanagement.c
  30. 58 91
      src/server/ua_services_session.c
  31. 145 108
      src/server/ua_services_view.c
  32. 38 79
      src/server/ua_session_manager.c
  33. 8 13
      src/server/ua_session_manager.h
  34. 28 2
      src/ua_connection.c
  35. 51 21
      src/ua_securechannel.c
  36. 10 3
      src/ua_securechannel.h
  37. 7 65
      src/ua_session.c
  38. 7 10
      src/ua_session.h
  39. 108 40
      src/ua_types.c
  40. 1 1
      src/ua_types_encoding_binary.c
  41. 15 1
      src/ua_util.h
  42. 19 17
      tests/CMakeLists.txt
  43. 16 4
      tests/testing_networklayers.c
  44. 0 20
      tools/.deployDoxygen.sh
  45. 59 0
      tools/.deployGH.sh
  46. 13 5
      tools/amalgamate.py
  47. 6 2
      tools/certs/create_self-signed.py
  48. 3 3
      tools/generate_datatypes.py
  49. 0 23008
      tools/schema/Opc.Ua.NodeSet2.xml

+ 23 - 14
.travis.yml

@@ -29,33 +29,48 @@ before_install:
 script:
 - echo "Checking the applicability of patches"
 - # ./tools/.checkPorts.sh
-- echo "Testing builds"
+- echo "Documentation and certificate build"
 - mkdir -p build
 - cd build
+- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_DOCUMENTATION=ON -DGENERATE_SELFSIGNED=ON ..
+- make -j doc
+- make selfsigned
+- cp -r doc ..
+- cp server_cert.der ..
+- echo "Testing builds"
+- cd .. && rm build -rf && mkdir -p build && cd build
 - echo "Cross compile release build for MinGW 32 bit"
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
 - cp ../README.md .
-- zip open62541-win32.zip README.md exampleServer.exe exampleClient.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+- cp ../LICENSE .
+- cp ../AUTHORS .
+- cp -r ../doc .
+- cp ../server_cert.der .
+- 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 && mkdir -p build && cd build
 - echo "Cross compile release build for MinGW 64 bit"
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw64.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
 - cp ../README.md .
-- zip open62541-win64.zip README.md exampleServer.exe exampleClient.exe libopen62541.dll libopen62541.dll.a open62541.h open62541.c
+- cp ../LICENSE .
+- cp ../AUTHORS .
+- cp -r ../doc .
+- cp ../server_cert.der .
+- 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 && mkdir -p build && cd build
 - echo "Cross compile release build for 32-bit linux"
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-gcc-m32.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-linux32.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
+- 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 && mkdir -p build && cd build
 - echo "Compile release build for 64-bit linux"
 - cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_AMALGAMATION=ON -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-linux64.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
+- 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
@@ -74,15 +89,9 @@ script:
 - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_DEMO_NODESET=ON -DBUILD_UNIT_TESTS=ON -DENABLE_COVERAGE=ON ..
 - make -j && make test
 - coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../
-- cd .. && rm build -rf && mkdir -p build && cd build
-- echo "Documentation build"
-- cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_DOCUMENTATION=ON ..
-- make -j
-- echo "Generate documentation"
-- make -j doc
-- cd ..
+- cd .. 
 after_success:
-- ./tools/.deployDoxygen.sh
+- ./tools/.deployGH.sh
 - ./tools/.coverity.sh
 before_deploy:
 - rm build -rf && mkdir -p build && cd build
@@ -93,7 +102,7 @@ before_deploy:
 - export PATH=$PATH:~/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/
 - cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-rpi64.cmake -DENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLESERVER=ON -DBUILD_EXAMPLECLIENT=ON ..
 - make -j
-- tar -pczf open62541-raspberrypi.tar.gz ../README.md exampleServer exampleClient libopen62541.so open62541.h open62541.c
+- tar -pczf open62541-raspberrypi.tar.gz ../doc ../server_cert.der ../LICENSE ../AUTHORS ../README.md server_static server client_static client libopen62541.so open62541.h open62541.c
 - cp open62541-raspberrypi.tar.gz ..
 - cd ..
 deploy:

+ 74 - 74
CMakeLists.txt

@@ -2,12 +2,23 @@ cmake_minimum_required(VERSION 2.8.8)
 # set(CMAKE_VERBOSE_MAKEFILE ON)
 
 project(open62541 C)
-set(open62541_VERSION_MAJOR 0)
-set(open62541_VERSION_MINOR 0)
-set(open62541_VERSION_PATCH 0)
-add_definitions(-DOPEN62541_VERSION_MAJOR=${open62541_VERSION_MAJOR})
-add_definitions(-DOPEN62541_VERSION_MINOR=${open62541_VERSION_MINOR})
-add_definitions(-DOPEN62541_VERSION_PATCH=${open62541_VERSION_PATCH})
+
+find_package(Git)
+if(GIT_FOUND)
+    execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=7 --dirty --always --tags RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID )
+    if( NOT ${res_var} EQUAL 0 )
+        set( GIT_COMMIT_ID "git commit id unknown")
+        message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." )
+    else()
+        string( REPLACE "\n" "" GIT_COMMIT_ID ${GIT_COM_ID} )
+    endif()
+else()
+    set( GIT_COMMIT_ID "unknown (git not found!)")
+    message( WARNING "Git not found. Build will not contain git revision info." )
+endif()
+add_definitions("-DVERSION=${GIT_COMMIT_ID}")
+
+MESSAGE (STATUS "Git version: "  ${GIT_COMMIT_ID})
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
 
@@ -34,9 +45,9 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
         set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-z,norelro -Wl,--hash-style=gnu -Wl,--build-id=none")
 	endif()
     if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR CMAKE_BUILD_TYPE STREQUAL "Release")
-        add_definitions(-flto)
-        set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -flto -s")
-        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -flto -s")
+#        add_definitions(-flto)
+        set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -s")
+        set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s")
     endif()
     option(ENABLESUBSCRIPTIONS "Enable compilation of subscription and monitoring support." OFF)
     if (ENABLESUBSCRIPTIONS)
@@ -50,11 +61,12 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_types.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.h
-                     ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
                      ${PROJECT_SOURCE_DIR}/include/ua_connection.h
                      ${PROJECT_SOURCE_DIR}/include/ua_log.h
                      ${PROJECT_SOURCE_DIR}/include/ua_server.h
-                     ${PROJECT_SOURCE_DIR}/include/ua_client.h)
+                     ${PROJECT_SOURCE_DIR}/include/ua_client.h
+                     ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.h 
+                     ${PROJECT_SOURCE_DIR}/examples/logger_stdout.h)
 set(internal_headers ${PROJECT_SOURCE_DIR}/src/ua_util.h
                      ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_BINARY_DIR}/src_generated/ua_transport_generated.h
@@ -89,7 +101,9 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_view.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_services_subscription.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_subscription_manager.c
-                ${PROJECT_SOURCE_DIR}/src/client/ua_client.c)
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
+                ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.c 
+                ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c)
                 ##TODO: make client stuff optional
 
 ## generate code from xml definitions
@@ -127,32 +141,11 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
                    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeids.py
                            ${CMAKE_CURRENT_SOURCE_DIR}/tools/schema/NodeIds.csv)
 
-option(LOADGENERATEDNS "Generate and load UA XML Namespace 0 definition" OFF)
-if(LOADGENERATEDNS)
-add_definitions(-DLOADGENERATEDNS)
-add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-                   ## Note: currently only Opc.Ua.NodeSet2.xml is included in the generated header. You can extend the call to include any number of XML files as parameters.
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.xml ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/logger.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py  
-                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
-)
-else()
-add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-                   COMMAND touch ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-		   DEPENDS
-)
-endif()
-
 ## logging
 set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
 
 ## multithreading
-option(ENABLE_MULTITHREADING "Enable multithreading" OFF)
+option(ENABLE_MULTITHREADING "Enable multithreading (experimental)" OFF)
 if(ENABLE_MULTITHREADING)
     add_definitions(-DUA_MULTITHREADING)
     find_package(Threads REQUIRED)
@@ -161,8 +154,13 @@ else()
     list(APPEND lib_sources ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore.c)
 endif()
 
+option(ENABLE_EXTERNAL_NAMESPACES "Enable namespace handling by an external component (experimental)" OFF)
+if(ENABLE_EXTERNAL_NAMESPACES)
+    add_definitions(-DUA_EXTERNAL_NAMESPACES)
+endif()
+
 ## set the precompiler flags
-configure_file("src/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
+configure_file("include/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
 
 ## extensions
 option(EXTENSION_UDP "Enable udp extension" OFF)
@@ -180,29 +178,24 @@ if(EXTENSION_STATELESS)
 	add_definitions(-DEXTENSION_STATELESS)
 endif()
 
-option(BUILD_DEMO_NODESET "Create a demo node for every built-in datatype" OFF)
-if(BUILD_DEMO_NODESET)
-	message(STATUS "Enabling demo nodeset")
-	add_definitions(-DDEMO_NODESET)
-endif()
-
 option(ENABLE_AMALGAMATION "Concatenate the library to a single file open62541.h/.c" OFF)
-add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h ${PROJECT_BINARY_DIR}/open62541.c
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.h
+               PRE_BUILD
+               COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${GIT_COMMIT_ID} ${CMAKE_CURRENT_BINARY_DIR}/open62541.h ${exported_headers}
+
+
+               DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${exported_headers} ${internal_headers})
+
+add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                PRE_BUILD
-               COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${CMAKE_CURRENT_BINARY_DIR}/open62541.h ${exported_headers}
-                                                                              ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.h
-                                                                              ${PROJECT_SOURCE_DIR}/examples/logger_stdout.h
-               COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${CMAKE_CURRENT_BINARY_DIR}/open62541.c
+
+               COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${GIT_COMMIT_ID} ${CMAKE_CURRENT_BINARY_DIR}/open62541.c
                               ${PROJECT_BINARY_DIR}/src_generated/ua_config.h ${internal_headers} ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources}
-                                                                              ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.c
-                                                                              ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c
-               DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${exported_headers} ${internal_headers}
-                       ${PROJECT_BINARY_DIR}/src_generated/ua_config.h ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources}
-                       ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.h ${PROJECT_SOURCE_DIR}/examples/logger_stdout.h
-                       ${PROJECT_SOURCE_DIR}/examples/networklayer_tcp.c ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c)
+
+               DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${PROJECT_BINARY_DIR}/src_generated/ua_config.h ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources})
 
 if(ENABLE_AMALGAMATION)
-    add_custom_target(amalgamation ALL DEPENDS ${PROJECT_BINARY_DIR}/open62541.h ${PROJECT_BINARY_DIR}/open62541.c)
+    add_custom_target(amalgamation ALL DEPENDS ${PROJECT_BINARY_DIR}/open62541.h)
     add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c)
     include_directories(${PROJECT_BINARY_DIR})
 else()
@@ -213,8 +206,10 @@ else()
     include_directories(${PROJECT_SOURCE_DIR}/src)
     include_directories(${PROJECT_BINARY_DIR}/src_generated)
 endif()
+target_compile_definitions(open62541-object PRIVATE -DUA_DYNAMIC_LINKING)
 add_library(open62541 SHARED $<TARGET_OBJECTS:open62541-object>)
-if(WIN32 AND ENABLE_AMALGAMATION)
+target_compile_definitions(open62541 PRIVATE -DUA_DYNAMIC_LINKING)
+if(WIN32)
     target_link_libraries(open62541 ws2_32) #since networklayer_tcp is linked into the amalgate
 endif()
 
@@ -239,21 +234,23 @@ endif()
 option(BUILD_EXAMPLESERVER "Build the example server" OFF)
 if(BUILD_EXAMPLESERVER)
     set(server_source $<TARGET_OBJECTS:open62541-object>)
-    if(NOT ENABLE_AMALGAMATION)
-        list(APPEND server_source examples/networklayer_tcp.c examples/logger_stdout.c)
-    endif()
-    add_executable(exampleServer examples/server.c ${server_source})
-    add_executable(exampleServer_simple examples/server_simple.c ${server_source})
+    add_executable(server_static examples/server.c ${server_source})
+    add_executable(server_simple examples/server_simple.c ${server_source})
+    add_executable(server examples/server.c)
+	target_link_libraries(server open62541)
     if(WIN32)
-        target_link_libraries(exampleServer ws2_32)
-        target_link_libraries(exampleServer_simple ws2_32)
+        target_link_libraries(server_static ws2_32)
+        target_link_libraries(server_simple ws2_32)
+        target_link_libraries(server ws2_32)
     else()
-        target_link_libraries(exampleServer rt)
-        target_link_libraries(exampleServer_simple rt)
+        target_link_libraries(server_static rt)
+        target_link_libraries(server_simple rt)
+        target_link_libraries(server rt)
     endif()
     if(ENABLE_MULTITHREADING)
-        target_link_libraries(exampleServer urcu-cds urcu urcu-common pthread)
-        target_link_libraries(exampleServer_simple urcu-cds urcu urcu-common pthread)
+        target_link_libraries(server_static urcu-cds urcu urcu-common pthread)
+        target_link_libraries(server_simple urcu-cds urcu urcu-common pthread)
+        target_link_libraries(server urcu-cds urcu urcu-common pthread)
     endif()
 endif()
 
@@ -261,6 +258,7 @@ endif()
 option(GENERATE_SELFSIGNED "Generate self-signed certificates" OFF)
 if(GENERATE_SELFSIGNED)
     message(STATUS "Enabling self-signed certificates")
+    find_package(OpenSSL REQUIRED)
     add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/localhost.der
                               ${PROJECT_BINARY_DIR}/ca.crt
                    COMMAND python ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${PROJECT_BINARY_DIR}
@@ -274,22 +272,24 @@ option(BUILD_EXAMPLECLIENT "Build a test client" OFF)
 if(BUILD_EXAMPLECLIENT)
 	add_definitions(-DBENCHMARK)
     set(client_source $<TARGET_OBJECTS:open62541-object>)
-    if(NOT ENABLE_AMALGAMATION)
-        list(APPEND client_source examples/networklayer_tcp.c examples/logger_stdout.c)
-    endif()
-	add_executable(exampleClient examples/client.c ${client_source})
+	add_executable(client_static examples/client.c ${client_source})
+	add_executable(client examples/client.c)
+	target_link_libraries(client open62541)
     if(WIN32)
-        target_link_libraries(exampleClient ws2_32)
+        target_link_libraries(client_static ws2_32)
+        target_link_libraries(client ws2_32)
     else()
-        target_link_libraries(exampleClient rt)
+        target_link_libraries(client_static rt)
+        target_link_libraries(client rt)
     endif()
     if(ENABLE_MULTITHREADING)
-        target_link_libraries(exampleClient urcu-cds urcu urcu-common pthread)
+        target_link_libraries(client_static urcu-cds urcu urcu-common pthread)
+        target_link_libraries(client urcu-cds urcu urcu-common pthread)
     endif()
     if(EXTENSION_STATELESS)
-        add_executable(statelessClient examples/client_stateless.c ${client_source})
+        add_executable(client_stateless examples/client_stateless.c ${client_source})
         if(ENABLE_MULTITHREADING)
-            target_link_libraries(statelessClient urcu-cds urcu urcu-common pthread)
+            target_link_libraries(client_stateless urcu-cds urcu urcu-common pthread)
         endif()
     endif()
 endif()

+ 7 - 6
README.md

@@ -19,6 +19,8 @@ For discussion and help, you can use
 - our [IRC channel](http://webchat.freenode.net/?channels=%23open62541)
 - the [bugtracker](https://github.com/acplt/open62541/issues)
 
+Auomated builds of 50 last single-file distributions are available [here](http://open62541.org/releases)
+
 ### Contribute to open62541
 As an open source project, we invite new contributors to help improving open62541. If you are a developer, your bugfixes and new features are very welcome. Note that there are ways to contribute even without deep knowledge of the project or the UA standard:
 - [Report bugs](https://github.com/acplt/open62541/issues)
@@ -50,15 +52,13 @@ int main(int argc, char** argv)
     UA_Server_setLogger(server, Logger_Stdout_new());
     UA_Server_addNetworkLayer(server,
         ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, PORT));
-    UA_UInt16 nsIndex = UA_Server_addNamespace(server, 
-                                  UA_ServerConfig_standard.Application_applicationURI);
 
     /* add a variable node */
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(nsIndex, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(nsIndex, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
@@ -78,7 +78,7 @@ int main(int argc, char** argv)
 
 int main(int argc, char *argv[])
 {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
+    UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
     UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect,
                                              "opc.tcp://localhost:16664");
     if(retval != UA_STATUSCODE_GOOD) {
@@ -91,7 +91,8 @@ int main(int argc, char *argv[])
     UA_ReadRequest_init(&req);
     req.nodesToRead = UA_ReadValueId_new();
     req.nodesToReadSize = 1;
-    req.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+    /* copy the nodeid-string to the heap (deleted with the req) */
+    req.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
 
     UA_ReadResponse resp = UA_Client_read(client, &req);

+ 15 - 27
examples/logger_stdout.c

@@ -15,39 +15,27 @@ static void print_time(void) {
 	UA_ByteString_deleteMembers(&str);
 }
 
-#define LOG_FUNCTION(LEVEL) \
-	static void log_##LEVEL(UA_LoggerCategory category, const char *msg, ...) { \
-        printf("[");                                                    \
-		print_time();                                                   \
-        va_list ap;                                                     \
-        va_start(ap, msg);                                              \
-        printf("] " #LEVEL "/%s\t", UA_LoggerCategoryNames[category]);  \
-        vprintf(msg, ap);                                               \
-        printf("\n");                                                   \
-        va_end(ap);                                                     \
-	}
+const char *LogLevelNames[6] = {"trace", "debug", "info", "warning", "error", "fatal"};
+const char *LogCategoryNames[4] = {"communication", "server", "client", "userland"};
 
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
-LOG_FUNCTION(trace)
-LOG_FUNCTION(debug)
-LOG_FUNCTION(info)
-LOG_FUNCTION(warning)
-LOG_FUNCTION(error)
-LOG_FUNCTION(fatal)
-#if defined(__GNUC__) || defined(__clang__)
+static void Logger_Stdout(UA_LogLevel level, UA_LogCategory category, const char *msg, ...) {
+    printf("[");
+    print_time();
+    va_list ap;
+    va_start(ap, msg);
+    printf("] %s/%s\t", LogLevelNames[level], LogCategoryNames[category]);
+    vprintf(msg, ap);
+    printf("\n");
+    va_end(ap);
+}
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
 
 UA_Logger Logger_Stdout_new(void) {
-	return (UA_Logger){
-		.log_trace = log_trace,
-		.log_debug = log_debug,
-		.log_info = log_info,
-		.log_warning = log_warning,
-		.log_error = log_error,
-		.log_fatal = log_fatal
-	};
+	return Logger_Stdout;
 }

+ 2 - 2
examples/logger_stdout.h

@@ -14,6 +14,6 @@
 #endif
 
 /** Initialises the logger for the current thread. */
-UA_Logger Logger_Stdout_new(void);
+UA_EXPORT UA_Logger Logger_Stdout_new(void);
 
-#endif /* LOGGER_STDOUT_H_ */
+#endif /* LOGGER_STDOUT_H_ */

+ 32 - 18
examples/networklayer_tcp.c

@@ -47,11 +47,11 @@ static UA_StatusCode socket_write(UA_Connection *connection, const UA_ByteString
         do {
 #ifdef _WIN32
             n = send((SOCKET)connection->sockfd, (const char*)buf->data, buf->length, 0);
-            if(n < 0 && WSAGetLastError() != WSAEINTR)
+            if(n < 0 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK)
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
 #else
             n = send(connection->sockfd, (const char*)buf->data, buf->length, MSG_NOSIGNAL);
-            if(n == -1L && errno != EINTR)
+            if(n == -1L && errno != EINTR && errno != EAGAIN)
                 return UA_STATUSCODE_BADCONNECTIONCLOSED;
 #endif
         } while (n == -1L);
@@ -62,12 +62,14 @@ static UA_StatusCode socket_write(UA_Connection *connection, const UA_ByteString
 
 static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-	if(response->data == UA_NULL)
+	if(response->data == NULL)
         retval = connection->getBuffer(connection, response, connection->localConf.recvBufferSize);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     struct timeval tmptv = {0, timeout * 1000};
-    setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval));
+    if(0 != setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval))){
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
     int ret = recv(connection->sockfd, (char*)response->data, response->length, 0);
 	if(ret == 0) {
 		connection->releaseBuffer(connection, response);
@@ -214,6 +216,9 @@ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
     socket_close(connection);
     ServerNetworkLayerTCP *layer = (ServerNetworkLayerTCP*)connection->handle;
     struct DeleteList *d = malloc(sizeof(struct DeleteList));
+    if(!d){
+        return;
+    }
     d->connection = connection;
 #ifdef UA_MULTITHREADING
     while(1) {
@@ -257,13 +262,13 @@ static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, U
     layer->logger = logger;
 #ifdef _WIN32
     if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket, code: %d",
+        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error opening socket, code: %d",
                        WSAGetLastError());
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 #else
     if((layer->serversockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error opening socket");
+        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error opening socket");
         return UA_STATUSCODE_BADINTERNALERROR;
     } 
 #endif
@@ -274,19 +279,19 @@ static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, U
     if(setsockopt(layer->serversockfd, SOL_SOCKET,
                   SO_REUSEADDR, (const char *)&optval,
                   sizeof(optval)) == -1) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during setting of socket options");
+        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error during setting of socket options");
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
             sizeof(serv_addr)) < 0) {
-        UA_LOG_WARNING((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Error during socket binding");
+        UA_LOG_WARNING((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Error during socket binding");
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     socket_set_nonblocking(layer->serversockfd);
     listen(layer->serversockfd, MAXBACKLOG);
-    UA_LOG_INFO((*layer->logger), UA_LOGGERCATEGORY_COMMUNICATION, "Listening on %.*s",
+    UA_LOG_INFO((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Listening on %.*s",
                 layer->discoveryUrl.length, layer->discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
 }
@@ -329,7 +334,7 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
 #endif
     removeMappings(layer, deletes);
     setFDSet(layer);
-    struct timeval tmptv = {0, timeout * 1000};
+    struct timeval tmptv = {0, timeout};
     UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
     UA_WorkItem *items;
     if(resultsize < 0 || !(items = malloc(sizeof(UA_WorkItem)*(resultsize+1)))) {
@@ -396,6 +401,9 @@ static UA_Int32 ServerNetworkLayerTCP_getWork(ServerNetworkLayerTCP *layer, UA_W
         j++;
     }
 
+    if(buf.data)
+        free(buf.data);
+
     /* free the array if there is no work */
     if(j == 0) {
         free(items);
@@ -431,10 +439,11 @@ static UA_Int32 ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Work
 /* run only when the server is stopped */
 static void ServerNetworkLayerTCP_delete(ServerNetworkLayerTCP *layer) {
     UA_String_deleteMembers(&layer->discoveryUrl);
+    removeMappings(layer, layer->deletes);
+    freeConnections(NULL, layer->deletes);
     for(size_t i = 0; i < layer->mappingsSize; i++)
         free(layer->mappings[i].connection);
     free(layer->mappings);
-    freeConnections(NULL, layer->deletes);
     free(layer);
 }
 
@@ -445,7 +454,13 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
     wVersionRequested = MAKEWORD(2, 2);
     WSAStartup(wVersionRequested, &wsaData);
 #endif
+    UA_ServerNetworkLayer nl;
+    memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
+
     ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP));
+    if(!layer){
+        return nl;
+    }
     layer->conf = conf;
     layer->mappingsSize = 0;
     layer->mappings = NULL;
@@ -455,7 +470,6 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
     gethostname(hostname, 255);
     UA_String_copyprintf("opc.tcp://%s:%d", &layer->discoveryUrl, hostname, port);
 
-    UA_ServerNetworkLayer nl;
     nl.nlHandle = layer;
     nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))ServerNetworkLayerTCP_start;
     nl.getWork = (UA_Int32 (*)(void*, UA_WorkItem**, UA_UInt16))ServerNetworkLayerTCP_getWork;
@@ -475,11 +489,11 @@ UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger
 
     size_t urlLength = strlen(endpointUrl);
     if(urlLength < 11 || urlLength >= 512) {
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Server url size invalid");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url size invalid");
         return connection;
     }
     if(strncmp(endpointUrl, "opc.tcp://", 10) != 0) {
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Server url does not begin with opc.tcp://");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Server url does not begin with opc.tcp://");
         return connection;
     }
 
@@ -492,7 +506,7 @@ UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger
         }
     }
     if(port == 0) {
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Port invalid");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Port invalid");
         return connection;
     }
 
@@ -509,12 +523,12 @@ UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger
 #else
     if((connection.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
 #endif
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Could not create socket");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Could not create socket");
         return connection;
     }
     struct hostent *server = gethostbyname(hostname);
     if(server == NULL) {
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "DNS lookup of %s failed", hostname);
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "DNS lookup of %s failed", hostname);
         return connection;
     }
     struct sockaddr_in server_addr;
@@ -523,7 +537,7 @@ UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger
     server_addr.sin_family = AF_INET;
     server_addr.sin_port = htons(port);
     if(connect(connection.sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
-        UA_LOG_WARNING((*logger), UA_LOGGERCATEGORY_COMMUNICATION, "Connection failed");
+        UA_LOG_WARNING((*logger), UA_LOGCATEGORY_COMMUNICATION, "Connection failed");
         return connection;
     }
     connection.state = UA_CONNECTION_OPENING;

+ 2 - 2
examples/networklayer_tcp.h

@@ -18,8 +18,8 @@ extern "C" {
 #endif
 
 /** @brief Create the TCP networklayer and listen to the specified port */
-UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
-UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger);
+UA_EXPORT UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+UA_EXPORT UA_Connection ClientNetworkLayerTCP_connect(char *endpointUrl, UA_Logger *logger);
 
 #ifdef __cplusplus
 } // extern "C"

+ 2 - 2
examples/networklayer_udp.c

@@ -78,12 +78,12 @@ static void writeCallbackUDP(UDPConnection *handle, UA_ByteStringArray gather_bu
 	}
 	struct sockaddr_in *sin = NULL;
 	if (handle->from.sa_family == AF_INET) {
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
 #endif
 	    sin = (struct sockaddr_in *) &(handle->from);
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
 	} else {

+ 105 - 50
examples/server.c

@@ -8,6 +8,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h> //access
+
 #define __USE_XOPEN2K
 #ifdef UA_MULTITHREADING
 # include <pthread.h>
@@ -33,7 +35,13 @@ UA_Logger logger;
 /*************************/
 /* Read-only data source */
 /*************************/
-static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp,
+                                  const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
 	UA_DateTime *currentTime = UA_DateTime_new();
 	if(!currentTime)
 		return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -52,7 +60,8 @@ static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, UA_D
 }
 
 static void releaseTimeData(void *handle, UA_DataValue *value) {
-	UA_DateTime_delete((UA_DateTime*)value->value.data);
+    if(value->hasValue)
+        UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 
 /*****************************/
@@ -60,7 +69,14 @@ static void releaseTimeData(void *handle, UA_DataValue *value) {
 /*      Only on Linux        */
 /*****************************/
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp,
+                                     const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
+
 	UA_Double* currentTemperature = UA_Double_new();
 
 	if(!currentTemperature)
@@ -69,7 +85,7 @@ static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, U
 	fseek(temperatureFile, 0, SEEK_SET);
 
 	if(fscanf(temperatureFile, "%lf", currentTemperature) != 1){
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "Can not parse temperature");
+		UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "Can not parse temperature");
 		exit(1);
 	}
 
@@ -85,7 +101,8 @@ static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, U
 }
 
 static void releaseTemperature(void *handle, UA_DataValue *value) {
-	UA_Double_delete((UA_Double*)value->value.data);
+    if(value->hasValue)
+        UA_Double_delete((UA_Double*)value->value.data);
 }
 
 /*************************/
@@ -98,7 +115,14 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
-static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp,
+                                   const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
+
 	/* In order to reduce blocking time, we could alloc memory for every read
        and return a copy of the data. */
 #ifdef UA_MULTITHREADING
@@ -118,6 +142,8 @@ static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, UA_
 }
 
 static void releaseLedStatus(void *handle, UA_DataValue *value) {
+    if(!value->hasValue)
+        return;
 	/* If we allocated memory for a specific read, free the content of the
        variantdata. */
 	value->value.arrayLength = -1;
@@ -127,7 +153,10 @@ static void releaseLedStatus(void *handle, UA_DataValue *value) {
 #endif
 }
 
-static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data) {
+static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const UA_NumericRange *range) {
+    if(range)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+    
 #ifdef UA_MULTITHREADING
 	pthread_rwlock_wrlock(&writeLock);
 #endif
@@ -151,12 +180,8 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data) {
 	return UA_STATUSCODE_GOOD;
 }
 
-static void printLedStatus(UA_Server *server, void *data) {
-	UA_LOG_INFO(logger, UA_LOGGERCATEGORY_SERVER, ledStatus ? "LED is on" : "LED is off");
-}
-
 static void stopHandler(int sign) {
-	printf("Received Ctrl-C\n");
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Received Ctrl-C\n");
 	running = 0;
 }
 
@@ -164,7 +189,7 @@ static UA_ByteString loadCertificate(void) {
 	UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
 	//FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
-	fp=fopen("localhost.der", "rb");
+	fp=fopen("server_cert.der", "rb");
 
 	if(!fp) {
         errno = 0; // we read errno also from the tcp layer...
@@ -198,12 +223,6 @@ int main(int argc, char** argv) {
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
 	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_UInt16 nsIndex = UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
-
-	// print the status every 2 sec
-	UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
-			.work.methodCall = {.method = printLedStatus, .data = NULL} };
-	UA_Server_addRepeatedWorkItem(server, &work, 2000, NULL);
 
 	// add node with the datetime data source
 	UA_DataSource dateDataSource = (UA_DataSource)
@@ -211,50 +230,52 @@ int main(int argc, char** argv) {
 		.read = readTimeData,
 		.release = releaseTimeData,
 		.write = NULL};
-	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(nsIndex, "current time");
+	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
 	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
-	if(!(temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Linux specific] Can not open temperature file, no temperature node will be added");
-	} else {
-		// add node with the datetime data source
+	//cpu temperature monitoring for linux machines
+	if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
+		// add node with the data source
 		UA_DataSource temperatureDataSource = (UA_DataSource)
     	    {.handle = NULL,
 			.read = readTemperature,
 			.release = releaseTemperature,
 			.write = NULL};
-		const UA_QualifiedName ledName = UA_QUALIFIEDNAME(nsIndex, "cpu temperature");
-		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, ledName, UA_NODEID_NULL, 
+		const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
+		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, tempName, UA_NODEID_NULL,
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 	}
 
-	if (	!(triggerFile = fopen("/sys/class/leds/led0/trigger", "w"))
-		|| 	!(ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "[Raspberry Pi specific] Can not open trigger or LED file (try to run server with sudo if on a Raspberry PI)");
-		UA_LOG_WARNING(logger, UA_LOGGERCATEGORY_USERLAND, "An LED node will be added but no physical LED will be operated");
-	} else {
-		//setting led mode to manual
-		fprintf(triggerFile, "%s", "none");
-		fflush(triggerFile);
-
-		//turning off led initially
-		fprintf(ledFile, "%s", "1");
-		fflush(ledFile);
-	}
-
-	// add node with the LED status data source
-	UA_DataSource ledStatusDataSource = (UA_DataSource)
-   		{.handle = NULL,
-		.read = readLedStatus,
-		.release = releaseLedStatus,
-		.write = writeLedStatus};
-	const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
-	UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+	//LED control for rpi
+	if(  access("/sys/class/leds/led0/trigger", F_OK ) != -1
+	  || access("/sys/class/leds/led0/brightness", F_OK ) != -1){
+        if (	(triggerFile = fopen("/sys/class/leds/led0/trigger", "w"))
+            && 	(ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
+            //setting led mode to manual
+            fprintf(triggerFile, "%s", "none");
+            fflush(triggerFile);
+
+            //turning off led initially
+            fprintf(ledFile, "%s", "1");
+            fflush(ledFile);
+
+            // add node with the LED status data source
+            UA_DataSource ledStatusDataSource = (UA_DataSource)
+                {.handle = NULL,
+                .read = readLedStatus,
+                .release = releaseLedStatus,
+                .write = writeLedStatus};
+            const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
+            UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+        }else{
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "[Raspberry Pi] LED file exist, but I have no access (try to run server with sudo)");
+        }
+    }
 
 	// add a static variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();
@@ -267,6 +288,40 @@ int main(int argc, char** argv) {
     UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
                               myIntegerNodeId, parentNodeId, parentReferenceNodeId);
 
+   /**************/
+   /* Demo Nodes */
+   /**************/
+
+#define DEMOID 50000
+   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+
+#define SCALARID 50001
+   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Scalar"), UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+
+#define ARRAYID 50002
+   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Array"), UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+
+   UA_UInt32 id = 51000; //running id in namespace 0
+   for(UA_UInt32 type = 0; UA_IS_BUILTIN(type); type++) {
+       if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
+           continue;
+       //add a scalar node for every built-in type
+        void *value = UA_new(&UA_TYPES[type]);
+        UA_Variant *variant = UA_Variant_new();
+        UA_Variant_setScalar(variant, value, &UA_TYPES[type]);
+        char name[15];
+        sprintf(name, "%02d", type);
+        UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, name);
+        UA_Server_addVariableNode(server, variant, myIntegerName, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+
+        //add an array node for every built-in type
+        UA_Variant *arrayvar = UA_Variant_new();
+        UA_Variant_setArray(arrayvar, UA_Array_new(&UA_TYPES[type], 10), 10, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, arrayvar, myIntegerName, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+   }
+
 	//start server
 	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
 

+ 6 - 3
examples/server.cpp

@@ -3,6 +3,7 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 #include <iostream>
+#include <cstring>
 
 #ifdef NOT_AMALGATED
 # include "ua_server.h"
@@ -22,11 +23,13 @@ using namespace std;
 
 int main()
 {
-	UA_Server *server = UA_Server_new();
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
-	// add a variable node to the adresspace
-    UA_Server_addNamespace(server, "myApplicationNamespace");
+    UA_Logger logger = Logger_Stdout_new();
+    UA_Server_setLogger(server, logger);
+
+    // add a variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);

+ 7 - 7
examples/server_simple.c

@@ -32,7 +32,7 @@ static UA_ByteString loadCertificate(void) {
     UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
 	//FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
-	fp=fopen("localhost.der", "rb");
+	fp=fopen("server_cert.der", "rb");
 
 	if(!fp) {
         errno = 0; // we read errno also from the tcp layer...
@@ -54,7 +54,7 @@ static UA_ByteString loadCertificate(void) {
 }
 
 static void testCallback(UA_Server *server, void *data) {
-    logger.log_info(UA_LOGGERCATEGORY_USERLAND, "testcallback");
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "testcallback");
 }
 
 int main(int argc, char** argv) {
@@ -67,7 +67,6 @@ int main(int argc, char** argv) {
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-    UA_Server_addNamespace(server, UA_ServerConfig_standard.Application_applicationURI);
 
     UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL,
                         .work.methodCall = {.method = testCallback, .data = NULL} };
@@ -83,12 +82,13 @@ int main(int argc, char** argv) {
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
                               myIntegerNodeId, parentNodeId, parentReferenceNodeId);
-    
+
+/*
 #ifdef BENCHMARK
     UA_UInt32 nodeCount = 50;
     char str[32];
     for(UA_UInt32 i = 0;i<nodeCount;i++) {
-        /* scalar */
+        // scalar
         void *data = UA_new(&UA_TYPES[i]);
         UA_Variant *variant = UA_Variant_new();
         UA_Variant_setScalar(variant, data, &UA_TYPES[i]);
@@ -99,7 +99,7 @@ int main(int argc, char** argv) {
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
-        /* array */
+        // array
         data = UA_Array_new(&UA_TYPES[i], 10);
         variant = UA_Variant_new();
         UA_Variant_setArray(variant, data, 10, &UA_TYPES[i]);
@@ -111,7 +111,7 @@ int main(int argc, char** argv) {
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
     }
 #endif
-
+*/
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
 

+ 0 - 1
examples/server_udp.c

@@ -28,7 +28,6 @@ int main(int argc, char** argv) {
 
 	UA_Server *server = UA_Server_new();
     UA_Server_addNetworkLayer(server, ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664));
-    UA_Server_addNamespace(server, "myApplicationNamespace");
 
 	// add a variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();

+ 6 - 2
include/ua_client.h

@@ -22,17 +22,21 @@ typedef UA_Connection (*UA_ConnectClientConnection)(char *endpointUrl, UA_Logger
 
 typedef struct UA_ClientConfig {
     UA_Int32 timeout; //sync response timeout
+    UA_Int32 secureChannelLifeTime; // lifetime in ms (then the channel needs to be renewed)
+    UA_Int32 timeToRenewSecureChannel; //time in ms  before expiration to renew the secure channel
     UA_ConnectionConfig localConnectionConfig;
 } UA_ClientConfig;
 
-extern const UA_ClientConfig UA_ClientConfig_standard;
+extern UA_EXPORT const UA_ClientConfig UA_ClientConfig_standard;
 UA_Client UA_EXPORT * UA_Client_new(UA_ClientConfig config, UA_Logger logger);
 
-void UA_Client_delete(UA_Client* client);
+UA_EXPORT void UA_Client_delete(UA_Client* client);
 
 UA_StatusCode UA_EXPORT UA_Client_connect(UA_Client *client, UA_ConnectClientConnection connFunc, char *endpointUrl);
 UA_StatusCode UA_EXPORT UA_Client_disconnect(UA_Client *client);
 
+UA_StatusCode UA_EXPORT UA_Client_renewSecureChannel(UA_Client *client);
+
 /* Attribute Service Set */
 UA_ReadResponse UA_EXPORT UA_Client_read(UA_Client *client, UA_ReadRequest *request);
 UA_WriteResponse UA_EXPORT UA_Client_write(UA_Client *client, UA_WriteRequest *request);

+ 9 - 0
src/ua_config.h.in

@@ -29,4 +29,13 @@
 #  endif
 #endif
 
+/*	Define your own htoleXX and leXXtoh here if needed.
+	Otherwise the ones defined in endian.h are used		*/
+//	#define htole16(x)	{...}(x)
+//	#define htole32(x)	{...}(x)
+//	#define htole64(x)	{...}(x)
+//	#define le16toh(x)	{...}(x)
+//	#define le32toh(x)	{...}(x)
+//	#define le64toh(x)	{...}(x)
+
 #endif /* UA_CONFIG_H_ */

+ 23 - 25
include/ua_log.h

@@ -23,8 +23,6 @@ extern "C" {
 #include "ua_config.h"
 
 /**
- * @ingroup server
- *
  * @defgroup logging Logging
  *
  * @brief Custom logging solutions can be "plugged in" with this interface
@@ -32,62 +30,62 @@ extern "C" {
  * @{
  */
 
-typedef enum UA_LoggerCategory {
-    UA_LOGGERCATEGORY_COMMUNICATION,
-    UA_LOGGERCATEGORY_SERVER,
-    UA_LOGGERCATEGORY_CLIENT,
-    UA_LOGGERCATEGORY_USERLAND
-} UA_LoggerCategory;
-
-extern UA_EXPORT const char *UA_LoggerCategoryNames[4];
-
-typedef struct UA_Logger {
-    void (*log_trace)(UA_LoggerCategory category, const char *msg, ...);
-    void (*log_debug)(UA_LoggerCategory category, const char *msg, ...);
-    void (*log_info)(UA_LoggerCategory category, const char *msg, ...);
-    void (*log_warning)(UA_LoggerCategory category, const char *msg, ...);
-    void (*log_error)(UA_LoggerCategory category, const char *msg, ...);
-    void (*log_fatal)(UA_LoggerCategory category, const char *msg, ...);
-} UA_Logger;
+typedef enum {
+    UA_LOGLEVEL_TRACE,
+    UA_LOGLEVEL_DEBUG,
+    UA_LOGLEVEL_INFO,
+    UA_LOGLEVEL_WARNING,
+    UA_LOGLEVEL_ERROR,
+    UA_LOGLEVEL_FATAL
+} UA_LogLevel;
+
+typedef enum {
+    UA_LOGCATEGORY_COMMUNICATION,
+    UA_LOGCATEGORY_SERVER,
+    UA_LOGCATEGORY_CLIENT,
+    UA_LOGCATEGORY_USERLAND
+} UA_LogCategory;
+    
+typedef void (*UA_Logger)(UA_LogLevel level, UA_LogCategory category, const char *msg, ...);
 
 #if UA_LOGLEVEL <= 100
 #define UA_LOG_TRACE(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_trace) LOGGER.log_trace(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_TRACE, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_TRACE(LOGGER, CATEGORY, ...) do {} while(0)
 #endif
 
 #if UA_LOGLEVEL <= 200
 #define UA_LOG_DEBUG(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_debug) LOGGER.log_debug(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_DEBUG, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_DEBUG(LOGGER, CATEGORY, ...) do {} while(0)
 #endif
 
 #if UA_LOGLEVEL <= 300
 #define UA_LOG_INFO(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_info) LOGGER.log_info(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_INFO, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_INFO(LOGGER, CATEGORY, ...) do {} while(0)
 #endif
 
 #if UA_LOGLEVEL <= 400
 #define UA_LOG_WARNING(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_warning) LOGGER.log_warning(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_WARNING, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_WARNING(LOGGER, CATEGORY, ...) do {} while(0)
 #endif
 
 #if UA_LOGLEVEL <= 500
 #define UA_LOG_ERROR(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_error) LOGGER.log_error(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_ERROR, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_ERROR(LOGGER, CATEGORY, ...) do {} while(0)
 #endif
 
 #if UA_LOGLEVEL <= 600
 #define UA_LOG_FATAL(LOGGER, CATEGORY, ...) do { \
-        if(LOGGER.log_fatal) LOGGER.log_fatal(CATEGORY, __VA_ARGS__); } while(0)
+        if(LOGGER) LOGGER(UA_LOGLEVEL_FATAL, CATEGORY, __VA_ARGS__); } while(0)
 #else
 #define UA_LOG_FATAL(LOGGER, CATEGORY, ...) do {} while(0)
 #endif

+ 65 - 16
include/ua_server.h

@@ -44,7 +44,7 @@ typedef struct UA_ServerConfig {
     char*       Application_applicationName;
 } UA_ServerConfig;
 
-extern const UA_ServerConfig UA_ServerConfig_standard;
+extern UA_EXPORT const UA_ServerConfig UA_ServerConfig_standard;
 
 struct UA_Server;
 typedef struct UA_Server UA_Server;
@@ -55,7 +55,7 @@ void UA_EXPORT UA_Server_delete(UA_Server *server);
 
 /** Sets the logger used by the server */
 void UA_EXPORT UA_Server_setLogger(UA_Server *server, UA_Logger logger);
-UA_Logger UA_EXPORT * UA_Server_getLogger(UA_Server *server);
+UA_Logger UA_EXPORT UA_Server_getLogger(UA_Server *server);
 
 /**
  * Runs the main loop of the server. In each iteration, this calls into the
@@ -73,22 +73,67 @@ UA_Logger UA_EXPORT * UA_Server_getLogger(UA_Server *server);
  */
 UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
 
-/** @brief A datasource is the interface to interact with a local data provider.
- *
- * Implementors of datasources need to provide functions for the callbacks in
- * this structure. After every read, the handle needs to be released to indicate
- * that the pointer is no longer accessed. As a rule, datasources are never
- * copied, but only their content. The only way to write into a datasource is
- * via the write-service.
+/**
+ * The prologue part of UA_Server_run (no need to use if you call UA_Server_run)
+ */
+UA_StatusCode UA_EXPORT UA_Server_run_startup(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
+/**
+ * The epilogue part of UA_Server_run (no need to use if you call UA_Server_run)
+ */
+UA_StatusCode UA_EXPORT UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads);
+/**
+ * One iteration of UA_Server_run (no need to use if you call UA_Server_run)
+ */
+UA_StatusCode UA_EXPORT UA_Server_run_getAndProcessWork(UA_Server *server, UA_Boolean *running);
+
+
+/**
+ * Datasources are the interface to local data providers. Implementors of datasources need to
+ * provide functions for the callbacks in this structure. After every read, the handle needs to be
+ * released to indicate that the pointer is no longer accessed. As a rule, datasources are never
+ * copied, but only their content. The only way to write into a datasource is via the write-service.
  *
  * It is expected that the read and release callbacks are implemented. The write
  * callback can be set to null.
- **/
+ */
 typedef struct {
-    void *handle;
-    UA_StatusCode (*read)(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value);
+    void *handle; ///> A custom pointer to reuse the same datasource functions for multiple sources
+
+    /**
+     * Read current data from the data source
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param includeSourceTimeStamp If true, then the datasource is expected to set the source
+     *        timestamp in the returned value
+     * @param range If not null, then the datasource shall return only a selection of the (nonscalar)
+     *        data. Set UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not apply.
+     * @param value The (non-null) DataValue that is returned to the client. The data source sets the
+     *        read data, the result status and optionally a sourcetimestamp.
+     * @return Returns a status code for logging. Error codes intended for the original caller are set
+     *         in the value. If an error is returned, then no releasing of the value is done.
+     */
+    UA_StatusCode (*read)(void *handle, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range,
+                          UA_DataValue *value);
+
+    /**
+     * Release data that was allocated during a read (and/or release locks in the data source)
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param value The DataValue that was used for a successful read.
+     */
     void (*release)(void *handle, UA_DataValue *value);
-    UA_StatusCode (*write)(void *handle, const UA_Variant *data);
+
+    /**
+     * Write into a data source. The write member of UA_DataSource can be empty if the operation
+     * is unsupported.
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param data The data to be written into the data source
+     * @param range An optional data range. If the data source is scalar or does not support writing
+     *        of ranges, then an error code is returned.
+     * @return Returns a status code that is returned to the user
+     */
+    UA_StatusCode (*write)(void *handle, const UA_Variant *data, const UA_NumericRange *range);
 } UA_DataSource;
 
 /** @brief Add a new namespace to the server. Returns the index of the new namespace */
@@ -101,13 +146,17 @@ UA_StatusCode UA_EXPORT
 UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_QualifiedName browseName, 
                           UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId);
 
+UA_StatusCode UA_EXPORT
+UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName,
+                        UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_NodeId typeDefinition);
+
 UA_StatusCode UA_EXPORT
 UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                                     const UA_QualifiedName browseName, UA_NodeId nodeId,
                                     const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId);
 
-/** Work that is run in the main loop (singlethreaded) or dispatched to a worker
-    thread. */
+/** Work that is run in the main loop (singlethreaded) or dispatched to a worker thread */
 typedef struct UA_WorkItem {
     enum {
         UA_WORKITEMTYPE_NOTHING,
@@ -197,7 +246,7 @@ typedef struct {
      *
      * @param workItems When the returned integer is positive, *workItems points
      * to an array of WorkItems of the returned size.
-     * @param timeout The timeout during which an event must arrive.
+     * @param timeout The timeout during which an event must arrive in microseconds
      * @return The size of the returned workItems array. If the result is
      * negative, an error has occured.
      */

+ 41 - 81
include/ua_types.h

@@ -263,16 +263,12 @@ typedef struct UA_DiagnosticInfo {
     struct UA_DiagnosticInfo *innerDiagnosticInfo;
 } UA_DiagnosticInfo;
 
-#ifndef SWIG
 #define UA_TYPE_HANDLING_FUNCTIONS(TYPE)                             \
     TYPE UA_EXPORT * TYPE##_new(void);                               \
     void UA_EXPORT TYPE##_init(TYPE * p);                            \
     void UA_EXPORT TYPE##_delete(TYPE * p);                          \
     void UA_EXPORT TYPE##_deleteMembers(TYPE * p);                   \
     UA_StatusCode UA_EXPORT TYPE##_copy(const TYPE *src, TYPE *dst);
-#else
-#define UA_TYPE_HANDLING_FUNCTIONS(TYPE)
-#endif
 
 /* Functions for all types */
 UA_TYPE_HANDLING_FUNCTIONS(UA_Boolean)
@@ -321,22 +317,17 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
 /* Custom functions for the builtin datatypes */
 /**********************************************/
 
-#ifdef __cplusplus
-#define CPP_ONLY(STR) STR
-#else
-#define CPP_ONLY(STR)
-#endif
-
 /* String */
 /** Copy a (zero terminated) char-array into a UA_String. Memory for the string data is
     allocated. If the memory cannot be allocated, a null-string is returned. */
-UA_String UA_EXPORT UA_String_fromChars(char const *src);
+UA_String UA_EXPORT UA_String_fromChars(char const src[]);
+
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
-#define UA_STRING(CHARS) (const UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
+#define UA_STRING(CHARS) (UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
 
 /** Printf a char-array into a UA_String. Memory for the string data is allocated. */
-UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
+UA_StatusCode UA_EXPORT UA_String_copyprintf(char const fmt[], UA_String *dst, ...);
 
 /** Compares two strings */
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
@@ -385,56 +376,23 @@ UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
 /** Is the nodeid a null-nodeid? */
 UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
 
-#ifndef __cplusplus
-#define UA_NODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_NodeId) {           \
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_NUMERIC,                       \
-        .identifier.numeric = NUMERICID }
-
-#define UA_NODEID_STRING(NS_INDEX, CHARS) (const UA_NodeId) {          \
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_STRING,                        \
-        .identifier.string = UA_STRING(CHARS) }
-    
-#define UA_NODEID_STRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) {    \
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_STRING,                        \
-        .identifier.string = UA_STRING_ALLOC(CHARS) }
-
-#define UA_NODEID_GUID(NS_INDEX, GUID) (UA_NodeId) {                   \
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_GUID,                          \
-        .identifier.guid = GUID }
-
-#define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) (const UA_NodeId) {      \
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_BYTESTRING,                    \
-        .identifier.byteString = UA_STRING(CHARS) }
-
-#define UA_NODEID_BYTESTRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) {\
-        .namespaceIndex = NS_INDEX,                                    \
-        .identifierType = UA_NODEIDTYPE_BYTESTRING,                    \
-        .identifier.byteString = UA_STRING_ALLOC(CHARS) }
-#else
-#define UA_NODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_NodeId) {    \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_NUMERIC, NUMERICID }
-
-#define UA_NODEID_STRING(NS_INDEX, CHARS) (const UA_NodeId) {           \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_STRING, UA_STRING(CHARS) }
-
-#define UA_NODEID_STRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) {     \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_STRING, UA_STRING_ALLOC(CHARS) }
-
-#define UA_NODEID_GUID(NS_INDEX, GUID) (UA_NodeId) {    \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_GUID, GUID }
-
-#define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) (const UA_NodeId) {       \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_BYTESTRING, UA_STRING(CHARS) }
-
-#define UA_NODEID_BYTESTRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) { \
-        NS_INDEX, UA_NodeId::UA_NODEIDTYPE_BYTESTRING, UA_STRING_ALLOC(CHARS) }
-#endif
-
+UA_NodeId UA_EXPORT UA_NodeId_fromInteger(UA_UInt16 nsIndex, UA_Int32 identifier);
+UA_NodeId UA_EXPORT UA_NodeId_fromCharString(UA_UInt16 nsIndex, char identifier[]);
+UA_NodeId UA_EXPORT UA_NodeId_fromCharStringCopy(UA_UInt16 nsIndex, char const identifier[]);
+UA_NodeId UA_EXPORT UA_NodeId_fromString(UA_UInt16 nsIndex, UA_String identifier);
+UA_NodeId UA_EXPORT UA_NodeId_fromStringCopy(UA_UInt16 nsIndex, UA_String identifier);
+UA_NodeId UA_EXPORT UA_NodeId_fromGuid(UA_UInt16 nsIndex, UA_Guid identifier);
+UA_NodeId UA_EXPORT UA_NodeId_fromCharByteString(UA_UInt16 nsIndex, char identifier[]);
+UA_NodeId UA_EXPORT UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char const identifier[]);
+UA_NodeId UA_EXPORT UA_NodeId_fromByteString(UA_UInt16 nsIndex, UA_ByteString identifier);
+UA_NodeId UA_EXPORT UA_NodeId_fromByteStringCopy(UA_UInt16 nsIndex, UA_ByteString identifier);
+
+#define UA_NODEID_NUMERIC(NS_INDEX, NUMERICID) UA_NodeId_fromInteger(NS_INDEX, NUMERICID)
+#define UA_NODEID_STRING(NS_INDEX, CHARS) UA_NodeId_fromCharString(NS_INDEX, CHARS)
+#define UA_NODEID_STRING_ALLOC(NS_INDEX, CHARS) UA_NodeId_fromCharStringCopy(NS_INDEX, CHARS)
+#define UA_NODEID_GUID(NS_INDEX, GUID) UA_NodeId_fromGuid(NS_INDEX, GUID)
+#define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) UA_NodeId_fromCharByteString(NS_INDEX, CHARS)
+#define UA_NODEID_BYTESTRING_ALLOC(NS_INDEX, CHARS) UA_NodeId_fromCharStringCopy(NS_INDEX, CHARS)
 #define UA_NODEID_NULL UA_NODEID_NUMERIC(0,0)
 
 /* ExpandedNodeId */
@@ -452,9 +410,9 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
         .namespaceIndex = NS_INDEX, .name = UA_STRING_ALLOC(CHARS) }
 
 /* LocalizedText */
-#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (const UA_LocalizedText) {     \
+#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (const UA_LocalizedText) {       \
         .locale = UA_STRING(LOCALE), .text = UA_STRING(TEXT) }
-#define UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT) (UA_LocalizedText) {             \
+#define UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT) (UA_LocalizedText) {       \
         .locale = UA_STRING_ALLOC(LOCALE), .text = UA_STRING_ALLOC(TEXT) }
 
 /* Variant */
@@ -466,7 +424,7 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
  * @param v The variant
  * @return Does the variant contain a scalar value.
  */
-UA_Boolean UA_EXPORT UA_Variant_isScalar(UA_Variant *v);
+UA_Boolean UA_EXPORT UA_Variant_isScalar(const UA_Variant *v);
     
 /**
  * Set the variant to a scalar value that already resides in memory. The value takes on the
@@ -515,33 +473,35 @@ UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array
                                                 const UA_DataType *type);
 
 /**
- * Copy the variant, but use only a subset of the (multidimensional) array. Returns an error code if
- * the variant is no array or if the indicated range does not fit.
+ * Copy the variant, but use only a subset of the (multidimensional) array into a variant. Returns
+ * an error code if the variant is not an array or if the indicated range does not fit.
  */
 UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
 
 /**
- * Insert a range of data into an existing variant of the dimensionality. This overwrites data in
- * the variant. The inserted data is managed by the variant (members are deleted with it).
+ * Insert a range of data into an existing variant. The data array can't be reused afterwards if it
+ * contains types without a fixed size (e.g. strings) since they take on the lifetime of the
+ * variant.
  *
  * @param v The variant
- * @param data The data array. Obviously the type must match the variant and the length the range.
+ * @param dataArray The data array. The type must match the variant
+ * @param dataarraySize The length of the data array. This is checked to match the range size.
  * @param range The range of where the new data is inserted
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *data, const UA_NumericRange range);
+UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
+                                            const UA_NumericRange range);
 
 /**
- * Copies the variant and inserts data from the range. The inserted data is managed by the variant
- * (members are deleted with it).
+ * Deep-copy a range of data into an existing variant.
  *
- * @param src The source variant
- * @param dst The target variant
- * @param data The data array. Obviously the type must match the variant and the length the range.
+ * @param v The variant
+ * @param dataArray The data array. The type must match the variant
+ * @param dataarraySize The length of the data array. This is checked to match the range size.
  * @param range The range of where the new data is inserted
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setCopyRange(const UA_Variant *src, UA_Variant *dst, void *data,
+UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
                                                 const UA_NumericRange range);
 
 /****************************/
@@ -565,8 +525,8 @@ typedef struct {
                                                   namespace may contain members from the same
                                                   namespace or ns0 only.*/
     UA_Byte padding UA_BITFIELD(5); /**< How much padding is there before this member element? For
-                                         arrays this is split into 2 bytes padding for for the
-                                         length index (max 4 bytes) and 3 bytes padding for the
+                                         arrays this is split into 2 bytes padding before the
+                                         length index (max 4 bytes) and 3 bytes padding before the
                                          pointer (max 8 bytes) */
     UA_Boolean isArray UA_BITFIELD(1); ///< The member is an array of the given type
 } UA_DataTypeMember;
@@ -575,7 +535,7 @@ struct UA_DataType {
     UA_NodeId typeId; ///< The nodeid of the type
     ptrdiff_t memSize UA_BITFIELD(16); ///< Size of the struct in memory
     UA_UInt16 typeIndex UA_BITFIELD(13); ///< Index of the type in the datatypetable
-    UA_Boolean namespaceZero UA_BITFIELD(1); ///< The type is defined in namespace zero.
+    UA_Boolean namespaceZero UA_BITFIELD(1); ///< The type is defined in namespace zero
     UA_Boolean fixedSize UA_BITFIELD(1); ///< The type (and its members) contains no pointers
     UA_Boolean zeroCopyable UA_BITFIELD(1); ///< Can the type be copied directly off the stream?
     UA_Byte membersSize; ///< How many members does the type have?

+ 50 - 26
src/client/ua_client.c

@@ -1,3 +1,4 @@
+#include <ua_types_generated.h>
 #include "ua_client.h"
 #include "ua_nodeids.h"
 #include "ua_types.h"
@@ -29,11 +30,13 @@ struct UA_Client {
     /* Config */
     UA_Logger logger;
     UA_ClientConfig config;
+    UA_DateTime scExpiresAt;
 };
 
-const UA_ClientConfig UA_ClientConfig_standard =
-    { 5 /* ms receive timout */, {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize  = 65536,
-                                  .maxMessageSize = 65536, .maxChunkCount = 1}};
+const UA_EXPORT UA_ClientConfig UA_ClientConfig_standard =
+    { 5 /* ms receive timout */, 30000, 2000,
+      {.protocolVersion = 0, .sendBufferSize = 65536, .recvBufferSize  = 65536,
+       .maxMessageSize = 65536, .maxChunkCount = 1}};
 
 UA_Client * UA_Client_new(UA_ClientConfig config, UA_Logger logger) {
     UA_Client *client = UA_malloc(sizeof(UA_Client));
@@ -47,6 +50,8 @@ UA_Client * UA_Client_new(UA_ClientConfig config, UA_Logger logger) {
     client->sequenceNumber = 0;
     client->requestId = 0;
 
+    client->scExpiresAt = 0;
+
     /* Secure Channel */
     UA_ChannelSecurityToken_deleteMembers(&client->securityToken);
     UA_ByteString_init(&client->clientNonce);
@@ -126,7 +131,7 @@ static UA_StatusCode HelAckHandshake(UA_Client *c) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
+static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
     UA_ByteString_deleteMembers(&client->clientNonce); // if the handshake is repeated
     UA_ByteString_newMembers(&client->clientNonce, 1);
     client->clientNonce.data[0] = 0;
@@ -149,13 +154,15 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
     UA_OpenSecureChannelRequest opnSecRq;
     UA_OpenSecureChannelRequest_init(&opnSecRq);
     opnSecRq.requestHeader.timestamp = UA_DateTime_now();
-    UA_ByteString_copy(&client->clientNonce, &opnSecRq.clientNonce);
-    opnSecRq.requestedLifetime = 30000;
-    opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
-    opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
-    opnSecRq.requestHeader.authenticationToken.identifier.numeric = 10;
-    opnSecRq.requestHeader.authenticationToken.identifierType = UA_NODEIDTYPE_NUMERIC;
-    opnSecRq.requestHeader.authenticationToken.namespaceIndex = 10;
+    opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
+    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
+    if(renew) {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
+    } else {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
+        UA_ByteString_copy(&client->clientNonce, &opnSecRq.clientNonce);
+        opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
+    }
 
     messageHeader.messageHeader.messageSize =
         UA_SecureConversationMessageHeader_calcSizeBinary(&messageHeader) +
@@ -202,9 +209,9 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
     UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
     UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_NodeId_decodeBinary(&reply, &offset, &requestType);
-
-    if(!UA_NodeId_equal(&requestType, &UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
-                                                         UA_ENCODINGOFFSET_BINARY))) {
+    UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
+                                                  UA_ENCODINGOFFSET_BINARY);
+    if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
         client->connection.releaseBuffer(&client->connection, &reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
@@ -213,10 +220,11 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
 
     UA_OpenSecureChannelResponse response;
     UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
+    client->scExpiresAt = UA_DateTime_now() + response.securityToken.revisedLifetime * 10000;
     client->connection.releaseBuffer(&client->connection, &reply);
     retval = response.responseHeader.serviceResult;
 
-    if(retval == UA_STATUSCODE_GOOD) {
+    if(!renew && retval == UA_STATUSCODE_GOOD) {
         UA_ChannelSecurityToken_copy(&response.securityToken, &client->securityToken);
         UA_ByteString_deleteMembers(&client->serverNonce); // if the handshake is repeated
         UA_ByteString_copy(&response.serverNonce, &client->serverNonce);
@@ -233,8 +241,16 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client) {
 static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *requestType,
                                void *response, const UA_DataType *responseType, UA_Client *client,
                                UA_Boolean sendOnly) {
+
+    //check if sc needs to be renewed
+    if(client->scExpiresAt-UA_DateTime_now() <= client->config.timeToRenewSecureChannel * 10000){ //less than 3 seconds left to expire -> renew
+        UA_Client_renewSecureChannel(client);
+    }
+
     if(response)
         UA_init(response, responseType);
+    else
+        return;
 
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
 
@@ -265,7 +281,8 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
     UA_ByteString message;
     UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message, msgHeader.messageHeader.messageSize);
     if(retval != UA_STATUSCODE_GOOD) {
-        // todo
+        // todo: print error message
+        return;
     }
 
     size_t offset = 0;
@@ -276,19 +293,22 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
     retval |= UA_NodeId_encodeBinary(&requestId, &message, &offset);
     retval |= UA_encodeBinary(request, requestType, &message, &offset);
 
-    retval = client->connection.write(&client->connection, &message);
-    client->connection.releaseBuffer(&client->connection, &message);
-
-    //TODO: rework to get return value
-    if(sendOnly)
-        return;
+    retval |= client->connection.write(&client->connection, &message);
 
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
+
+    client->connection.releaseBuffer(&client->connection, &message);
+
     if(retval != UA_STATUSCODE_GOOD) {
+        //send failed
         respHeader->serviceResult = retval;
         return;
     }
 
+    //TODO: rework to get return value
+    if(sendOnly)
+        return;
+
     /* Response */
     UA_ByteString reply;
     UA_ByteString_init(&reply);
@@ -306,9 +326,9 @@ static void sendReceiveRequest(UA_RequestHeader *request, const UA_DataType *req
     retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_NodeId responseId;
     retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
-
-    if(!UA_NodeId_equal(&responseId, &UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
-                                                       UA_ENCODINGOFFSET_BINARY))) {
+    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
+                                                 UA_ENCODINGOFFSET_BINARY);
+    if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
         client->connection.releaseBuffer(&client->connection, &reply);
         UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
         respHeader->serviceResult = retval;
@@ -494,7 +514,7 @@ UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection co
     client->connection.localConf = client->config.localConnectionConfig;
     UA_StatusCode retval = HelAckHandshake(client);
     if(retval == UA_STATUSCODE_GOOD)
-        retval = SecureChannelHandshake(client);
+        retval = SecureChannelHandshake(client, UA_FALSE);
     if(retval == UA_STATUSCODE_GOOD)
         retval = EndpointsHandshake(client);
     if(retval == UA_STATUSCODE_GOOD)
@@ -512,6 +532,10 @@ UA_StatusCode UA_Client_disconnect(UA_Client *client) {
     return retval;
 }
 
+UA_StatusCode UA_Client_renewSecureChannel(UA_Client *client) {
+    return SecureChannelHandshake(client, UA_TRUE);
+}
+
 UA_ReadResponse UA_Client_read(UA_Client *client, UA_ReadRequest *request) {
     UA_ReadResponse response;
     synchronousRequest(request, &UA_TYPES[UA_TYPES_READREQUEST], &response,

+ 2 - 2
src/server/ua_nodestore_hash.inc

@@ -18,12 +18,12 @@ static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
     static const uint32_t n  = 0xe6546b64;
     hash_t hash = seed;
     /* Somce compilers emit a warning when casting from a byte array to ints. */
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wcast-align"
 #endif
     blocks = (const uint32_t *)data;
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
     for(int32_t i = 0;i < nblocks;i++) {

+ 54 - 45
src/server/ua_securechannel_manager.c

@@ -14,69 +14,79 @@ UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt3
 }
 
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm) {
-    channel_list_entry *current, *next = LIST_FIRST(&cm->channels);
-    while(next) {
-        current = next;
-        next = LIST_NEXT(current, pointers);
+    channel_list_entry *current = LIST_FIRST(&cm->channels);
+    while(current) {
         LIST_REMOVE(current, pointers);
-        if(current->channel.session)
-            current->channel.session->channel = UA_NULL;
-        if(current->channel.connection)
-            current->channel.connection->channel = UA_NULL;
-        UA_SecureChannel_deleteMembers(&current->channel);
+        UA_SecureChannel_deleteMembersCleanup(&current->channel);
         UA_free(current);
+        current = LIST_FIRST(&cm->channels);
+    }
+}
+
+void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime now) {
+    channel_list_entry *entry = LIST_FIRST(&cm->channels);
+    /* remove channels that were not renewed or who have no connection attached */
+    while(entry) {
+        if(entry->channel.securityToken.createdAt +
+            (10000 * entry->channel.securityToken.revisedLifetime) > now &&
+            entry->channel.connection) {
+            entry = LIST_NEXT(entry, pointers);
+        }
+        else {
+            channel_list_entry *next = LIST_NEXT(entry, pointers);
+            LIST_REMOVE(entry, pointers);
+            UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+            UA_free(entry);
+            entry = next;
+        }
     }
 }
 
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
                                            const UA_OpenSecureChannelRequest *request,
-                                           UA_OpenSecureChannelResponse *response)
-{
+                                           UA_OpenSecureChannelResponse *response) {
     switch(request->securityMode) {
+    case UA_MESSAGESECURITYMODE_NONE:
+        break;
     case UA_MESSAGESECURITYMODE_INVALID:
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
-        return response->responseHeader.serviceResult;
-
-        // fall through and handle afterwards
-    /* case UA_MESSAGESECURITYMODE_NONE: */
-    /*     UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce); */
-    /*     break; */
-
     case UA_MESSAGESECURITYMODE_SIGN:
     case UA_MESSAGESECURITYMODE_SIGNANDENCRYPT:
+    default:
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURITYMODEREJECTED;
         return response->responseHeader.serviceResult;
-
-    default:
-        // do nothing
-        break;
     }
 
     channel_list_entry *entry = UA_malloc(sizeof(channel_list_entry));
-    if(!entry) return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_SecureChannel_init(&entry->channel);
+    if(!entry)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
 
+    UA_SecureChannel_init(&entry->channel);
     response->responseHeader.stringTableSize = 0;
     response->responseHeader.timestamp       = UA_DateTime_now();
+    response->serverProtocolVersion = 0;
 
-    entry->channel.connection = conn;
-    conn->channel = &entry->channel;
     entry->channel.securityToken.channelId       = cm->lastChannelId++;
     entry->channel.securityToken.tokenId         = cm->lastTokenId++;
     entry->channel.securityToken.createdAt       = UA_DateTime_now();
     entry->channel.securityToken.revisedLifetime = (request->requestedLifetime > cm->maxChannelLifetime) ?
                                                    cm->maxChannelLifetime : request->requestedLifetime;
+    //FIXME: pragmatic workaround to get clients requesting lifetime of 0 working
+    if(entry->channel.securityToken.revisedLifetime == 0) {
+        entry->channel.securityToken.revisedLifetime = cm->maxChannelLifetime;
+        //FIXME: I'd log it, but there is no pointer to the logger
+        // printf("Warning: client requests token lifetime of 0 in OpenSecureChannelRequest setting it to %llu\n", cm->maxChannelLifetime);
+    }
 
     UA_ByteString_copy(&request->clientNonce, &entry->channel.clientNonce);
     entry->channel.serverAsymAlgSettings.securityPolicyUri =
         UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
-    LIST_INSERT_HEAD(&cm->channels, entry, pointers);
 
-    response->serverProtocolVersion = 0;
     UA_SecureChannel_generateNonce(&entry->channel.serverNonce);
     UA_ByteString_copy(&entry->channel.serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&entry->channel.securityToken, &response->securityToken);
-    conn->channel = &entry->channel;
+
+    UA_Connection_attachSecureChannel(conn, &entry->channel);
+    LIST_INSERT_HEAD(&cm->channels, entry, pointers);
 
     return UA_STATUSCODE_GOOD;
 }
@@ -93,13 +103,18 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Conn
     channel->securityToken.createdAt       = UA_DateTime_now(); // todo: is wanted?
     channel->securityToken.revisedLifetime = (request->requestedLifetime > cm->maxChannelLifetime) ?
                                              cm->maxChannelLifetime : request->requestedLifetime;
+    //FIXME: pragmatic workaround to get clients requesting lifetime of 0 working
+    if(channel->securityToken.revisedLifetime == 0){
+        channel->securityToken.revisedLifetime = cm->maxChannelLifetime;
+        //FIXME: I'd log it, but there is no pointer to the logger
+        // printf("Warning: client requests token lifetime of 0 in renewing SecureChannel setting it to %llu\n", cm->maxChannelLifetime);
+    }
 
     if(channel->serverNonce.data != UA_NULL)
         UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_SecureChannel_generateNonce(&channel->serverNonce);
     UA_ByteString_copy(&channel->serverNonce, &response->serverNonce);
     UA_ChannelSecurityToken_copy(&channel->securityToken, &response->securityToken);
-
     return UA_STATUSCODE_GOOD;
 }
 
@@ -113,21 +128,15 @@ UA_SecureChannel * UA_SecureChannelManager_get(UA_SecureChannelManager *cm, UA_U
 }
 
 UA_StatusCode UA_SecureChannelManager_close(UA_SecureChannelManager *cm, UA_UInt32 channelId) {
-    // TODO lock access
     channel_list_entry *entry;
     LIST_FOREACH(entry, &cm->channels, pointers) {
-        if(entry->channel.securityToken.channelId == channelId) {
-            UA_Connection *c = entry->channel.connection;
-            if(c) {
-                UA_Connection_detachSecureChannel(c);
-                c->close(c);
-            }
-            entry->channel.session = UA_NULL;
-            UA_SecureChannel_deleteMembers(&entry->channel);
-            LIST_REMOVE(entry, pointers);
-            UA_free(entry);
-            return UA_STATUSCODE_GOOD;
-        }
+        if(entry->channel.securityToken.channelId == channelId)
+            break;
     }
-    return UA_STATUSCODE_BADINTERNALERROR;
+    if(!entry)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    LIST_REMOVE(entry, pointers);
+    UA_SecureChannel_deleteMembersCleanup(&entry->channel);
+    UA_free(entry);
+    return UA_STATUSCODE_GOOD;
 }

+ 1 - 0
src/server/ua_securechannel_manager.h

@@ -25,6 +25,7 @@ UA_StatusCode UA_SecureChannelManager_init(UA_SecureChannelManager *cm, UA_UInt3
                                            UA_UInt32 tokenLifetime, UA_UInt32 startChannelId,
                                            UA_UInt32 startTokenId);
 void UA_SecureChannelManager_deleteMembers(UA_SecureChannelManager *cm);
+void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_DateTime now);
 UA_StatusCode UA_SecureChannelManager_open(UA_SecureChannelManager *cm, UA_Connection *conn,
                                            const UA_OpenSecureChannelRequest *request,
                                            UA_OpenSecureChannelResponse *response);

+ 339 - 265
src/server/ua_server.c

@@ -6,15 +6,14 @@
 #include "ua_services.h"
 #include "ua_nodeids.h"
 
-const UA_ServerConfig UA_ServerConfig_standard = {
-        UA_TRUE,
-        UA_TRUE,
-        (char *[]){"user1","user2"},
-        (char *[]){"password","password1"},
-        2,
-        "urn:unconfigured:open62541:open62541Server",
-        "Unconfigured open62541 application"
-};
+const UA_EXPORT UA_ServerConfig UA_ServerConfig_standard = {
+    .Login_enableAnonymous = UA_TRUE,
+    .Login_enableUsernamePassword = UA_TRUE,
+    .Login_usernames = (char *[]){"user1","user2"},
+    .Login_passwords = (char *[]){"password","password1"},
+    .Login_loginsCount = 2,
+    .Application_applicationURI = "urn:unconfigured:open62541:open62541Server",
+    .Application_applicationName = "open62541" };
 
 /**********************/
 /* Namespace Handling */
@@ -34,15 +33,15 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
 /* Configuration */
 /*****************/
 
-UA_Logger * UA_Server_getLogger(UA_Server *server) {
-    return &server->logger;
+UA_Logger UA_Server_getLogger(UA_Server *server) {
+    return server->logger;
 }
 
 void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkLayer) {
     UA_ServerNetworkLayer *newlayers =
         UA_realloc(server->networkLayers, sizeof(UA_ServerNetworkLayer)*(server->networkLayersSize+1));
     if(!newlayers) {
-        UA_LOG_ERROR(server->logger, UA_LOGGERCATEGORY_SERVER, "Networklayer added");
+        UA_LOG_ERROR(server->logger, UA_LOGCATEGORY_SERVER, "Networklayer added");
         return;
     }
     server->networkLayers = newlayers;
@@ -55,13 +54,16 @@ void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkL
         UA_String* newUrls = UA_realloc(server->description.discoveryUrls,
                                         sizeof(UA_String)*(server->description.discoveryUrlsSize+1));
         if(!newUrls) {
-            UA_LOG_ERROR(server->logger, UA_LOGGERCATEGORY_SERVER, "Adding discoveryUrl");
+            UA_LOG_ERROR(server->logger, UA_LOGCATEGORY_SERVER, "Adding discoveryUrl");
             return;
         }
         server->description.discoveryUrls = newUrls;
         UA_String_copy(networkLayer.discoveryUrl,
                        &server->description.discoveryUrls[server->description.discoveryUrlsSize]);
         server->description.discoveryUrlsSize++;
+        for(UA_Int32 i = 0; i < server->endpointDescriptionsSize; i++)
+            if(!server->endpointDescriptions[i].endpointUrl.data)
+                UA_String_copy(networkLayer.discoveryUrl, &server->endpointDescriptions[i].endpointUrl);
     }
 }
 
@@ -113,60 +115,41 @@ void UA_Server_delete(UA_Server *server) {
     UA_free(server);
 }
 
-/**
- * Recurring cleanup. Removing unused and timed-out channels and sessions
- * Todo: make this thread-safe
- */
+/* Recurring cleanup. Removing unused and timed-out channels and sessions */
 static void UA_Server_cleanup(UA_Server *server, void *nothing) {
     UA_DateTime now = UA_DateTime_now();
-    channel_list_entry *entry = LIST_FIRST(&server->secureChannelManager.channels);
-    /* remove channels that were not renewed or who have no connection attached */
-    while(entry) {
-        if(entry->channel.securityToken.createdAt +
-           (10000 * entry->channel.securityToken.revisedLifetime) > now &&
-           entry->channel.connection) {
-            entry = LIST_NEXT(entry, pointers);
-        } else {
-            channel_list_entry *next = LIST_NEXT(entry, pointers);
-            LIST_REMOVE(entry, pointers);
-            UA_Connection *c = entry->channel.connection;
-            if (c) {
-                UA_Connection_detachSecureChannel(c);
-                c->close(c);
-            }
-            UA_SecureChannel_detachSession(&entry->channel);
-            UA_SecureChannel_deleteMembers(&entry->channel);
-            UA_free(entry);
-            entry = next;
-        }
-    }
+    UA_SessionManager_cleanupTimedOut(&server->sessionManager, now);
+    UA_SecureChannelManager_cleanupTimedOut(&server->secureChannelManager, now);
+}
 
-    session_list_entry *sentry = LIST_FIRST(&server->sessionManager.sessions);
-    while(sentry) {
-        if(sentry->session.validTill < now) {
-            session_list_entry *next = LIST_NEXT(sentry, pointers);
-            LIST_REMOVE(sentry, pointers);
-            UA_SecureChannel_detachSession(sentry->session.channel);
-            UA_Session_deleteMembers(&sentry->session);
-            UA_free(sentry);
-            sentry = next;
-        }
-    }
+#define MANUFACTURER_NAME "open62541"
+#define PRODUCT_NAME "open62541 OPC UA Server"
+#define STRINGIFY(x) #x //some magic
+#define TOSTRING(x) STRINGIFY(x) //some magic
+#define SOFTWARE_VERSION TOSTRING(VERSION)
+#define BUILD_NUMBER "0"
+
+static void getBulidInfo(const UA_Server* server, UA_BuildInfo *buildInfo) {
+    buildInfo->productUri = UA_STRING_ALLOC(PRODUCT_URI);
+    buildInfo->manufacturerName = UA_STRING_ALLOC(MANUFACTURER_NAME);
+    buildInfo->productName = UA_STRING_ALLOC(PRODUCT_NAME);
+    buildInfo->softwareVersion = UA_STRING_ALLOC(SOFTWARE_VERSION);
+    buildInfo->buildNumber = UA_STRING_ALLOC(BUILD_NUMBER);
+    buildInfo->buildDate = server->buildDate;
 }
 
-static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp,
+                                const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = ((const UA_Server*)handle)->startTime;
     status->currentTime = UA_DateTime_now();
     status->state       = UA_SERVERSTATE_RUNNING;
-    status->buildInfo.productUri = UA_STRING("http://www.open62541.org");
-    status->buildInfo.manufacturerName = UA_STRING("open62541");
-    status->buildInfo.productName = UA_STRING("open62541 OPC UA Server");
-#define STRINGIFY(x) #x //some magic
-#define TOSTRING(x) STRINGIFY(x) //some magic
-    status->buildInfo.softwareVersion = UA_STRING(TOSTRING(OPEN62541_VERSION_MAJOR) "." TOSTRING(OPEN62541_VERSION_MINOR) "." TOSTRING(OPEN62541_VERSION_PATCH));
-    status->buildInfo.buildNumber = UA_STRING("0");
-    status->buildInfo.buildDate = ((const UA_Server*)handle)->buildDate;
+    getBulidInfo(((const UA_Server*)handle), &status->buildInfo);
     status->secondsTillShutdown = 0;
 
     value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE];
@@ -183,13 +166,21 @@ static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_Dat
 }
 
 static void releaseStatus(void *handle, UA_DataValue *value) {
-    UA_free(value->value.data);
+    if(!value->hasValue)
+        return;
+    UA_ServerStatusDataType_delete((UA_ServerStatusDataType*)value->value.data);
     value->value.data = UA_NULL;
     value->hasValue = UA_FALSE;
     UA_DataValue_deleteMembers(value);
 }
 
-static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp, UA_DataValue *value) {
+static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp,
+                                    const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_Server *server = (UA_Server*)handle;
     value->hasValue = UA_TRUE;
     value->value.storageType = UA_VARIANT_DATA_NODELETE;
@@ -206,16 +197,19 @@ static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp, UA
 static void releaseNamespaces(void *handle, UA_DataValue *value) {
 }
 
-static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp,
+                                     const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_DateTime *currentTime = UA_DateTime_new();
     if(!currentTime)
         return UA_STATUSCODE_BADOUTOFMEMORY;
     *currentTime = UA_DateTime_now();
     value->value.type = &UA_TYPES[UA_TYPES_DATETIME];
-    value->value.arrayLength = -1;
     value->value.data = currentTime;
-    value->value.arrayDimensionsSize = -1;
-    value->value.arrayDimensions = NULL;
     value->hasValue = UA_TRUE;
     if(sourceTimeStamp) {
         value->hasSourceTimestamp = UA_TRUE;
@@ -225,7 +219,8 @@ static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp, U
 }
 
 static void releaseCurrentTime(void *handle, UA_DataValue *value) {
-    UA_DateTime_delete((UA_DateTime*)value->value.data);
+    if(value->hasValue)
+        UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 
 static void copyNames(UA_Node *node, char *name) {
@@ -239,8 +234,8 @@ static void addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid,
     copyNames((UA_Node*)datatype, name);
     datatype->nodeId.identifier.numeric = datatypeid;
     UA_Server_addNode(server, (UA_Node*)datatype,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, parent),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+                      UA_EXPANDEDNODEID_NUMERIC(0, parent),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 }
 
 static void addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttypeid, UA_Int32 parent,
@@ -249,14 +244,13 @@ static void addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttyp
     copyNames((UA_Node*)objecttype, name);
     objecttype->nodeId.identifier.numeric = objecttypeid;
     UA_Server_addNode(server, (UA_Node*)objecttype,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, parent),
-                      &UA_NODEID_NUMERIC(0, parentreference));
+                      UA_EXPANDEDNODEID_NUMERIC(0, parent),
+                      UA_NODEID_NUMERIC(0, parentreference));
 }
 
 static UA_VariableTypeNode*
 createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                       UA_Int32 parent, UA_Boolean abstract)
-{
+                       UA_Int32 parent, UA_Boolean abstract) {
     UA_VariableTypeNode *variabletype = UA_VariableTypeNode_new();
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
@@ -268,19 +262,17 @@ createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
 static void addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid,
                                           UA_Int32 parent, UA_Boolean abstract) {
     UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-
     UA_Server_addNode(server, (UA_Node*)variabletype,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, parent),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+                      UA_EXPANDEDNODEID_NUMERIC(0, parent),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 }
 
 static void addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid,
                                         UA_Int32 parent, UA_Boolean abstract) {
     UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-
     UA_Server_addNode(server, (UA_Node*)variabletype,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, parent),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, parent),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 }
 
 UA_Server * UA_Server_new(UA_ServerConfig config) {
@@ -299,7 +291,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 #endif
 
     // logger
-    server->logger = (UA_Logger){ UA_NULL, UA_NULL, UA_NULL, UA_NULL, UA_NULL, UA_NULL };
+    server->logger = UA_NULL;
 
     // random seed
     server->random_seed = (UA_UInt32)UA_DateTime_now();
@@ -321,9 +313,11 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
 
-    server->namespaces = UA_String_new();
-    *server->namespaces = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
-    server->namespacesSize = 1;
+    /* ns0 and ns1 */
+    server->namespaces = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
+    server->namespaces[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
+    UA_String_copy(&server->description.applicationUri, &server->namespaces[1]);
+    server->namespacesSize = 2;
 
     server->endpointDescriptions = UA_NULL;
     server->endpointDescriptionsSize = 0;
@@ -368,7 +362,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
         server->endpointDescriptions = endpoint;
         server->endpointDescriptionsSize = 1;
-    }
+    } 
 
 #define MAXCHANNELCOUNT 100
 #define STARTCHANNELID 1
@@ -427,17 +421,17 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 #define HUNDRED_NANOSEC_PER_SEC (HUNDRED_NANOSEC_PER_USEC * 1000000LL)
     server->buildDate = (mktime(&ct) + UNIX_EPOCH_BIAS_SEC) * HUNDRED_NANOSEC_PER_SEC;
 
-#ifndef LOADGENERATEDNS
     /**************/
     /* References */
     /**************/
-
+    
     /* bootstrap by manually inserting "references" and "hassubtype" */
     UA_ReferenceTypeNode *references = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)references, "References");
     references->nodeId.identifier.numeric = UA_NS0ID_REFERENCES;
     references->isAbstract = UA_TRUE;
     references->symmetric  = UA_TRUE;
+    references->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "References");
     // this node has no parent??
     UA_NodeStore_insert(server->nodestore, (UA_Node*)references, UA_NULL);
 
@@ -456,8 +450,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hierarchicalreferences->isAbstract = UA_TRUE;
     hierarchicalreferences->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hierarchicalreferences,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *nonhierarchicalreferences = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)nonhierarchicalreferences, "NonHierarchicalReferences");
@@ -465,8 +459,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     nonhierarchicalreferences->isAbstract = UA_TRUE;
     nonhierarchicalreferences->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)nonhierarchicalreferences,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *haschild = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haschild, "HasChild");
@@ -474,8 +468,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     haschild->isAbstract = UA_TRUE;
     haschild->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)haschild,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)organizes, "Organizes");
@@ -484,8 +478,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     organizes->isAbstract = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)organizes,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseventsource, "HasEventSource");
@@ -494,8 +488,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     haseventsource->isAbstract = UA_FALSE;
     haseventsource->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)haseventsource,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodellingrule, "HasModellingRule");
@@ -504,8 +498,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasmodellingrule->isAbstract = UA_FALSE;
     hasmodellingrule->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasmodellingrule,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasencoding, "HasEncoding");
@@ -514,8 +508,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasencoding->isAbstract = UA_FALSE;
     hasencoding->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasencoding,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasdescription, "HasDescription");
@@ -524,8 +518,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasdescription->isAbstract = UA_FALSE;
     hasdescription->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasdescription,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hastypedefinition, "HasTypeDefinition");
@@ -534,8 +528,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hastypedefinition->isAbstract = UA_FALSE;
     hastypedefinition->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hastypedefinition,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)generatesevent, "GeneratesEvent");
@@ -544,8 +538,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     generatesevent->isAbstract = UA_FALSE;
     generatesevent->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)generatesevent,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *aggregates = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)aggregates, "Aggregates");
@@ -554,8 +548,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     aggregates->isAbstract = UA_TRUE;
     aggregates->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)aggregates,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASCHILD),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASCHILD),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     // complete bootstrap of hassubtype
     ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_HASCHILD), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
@@ -568,8 +562,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasproperty->isAbstract = UA_FALSE;
     hasproperty->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasproperty,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascomponent, "HasComponent");
@@ -578,8 +572,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hascomponent->isAbstract = UA_FALSE;
     hascomponent->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hascomponent,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasnotifier, "HasNotifier");
@@ -588,8 +582,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasnotifier->isAbstract = UA_FALSE;
     hasnotifier->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasnotifier,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASEVENTSOURCE),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASEVENTSOURCE),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasorderedcomponent, "HasOrderedComponent");
@@ -598,8 +592,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasorderedcomponent->isAbstract = UA_FALSE;
     hasorderedcomponent->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasorderedcomponent,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodelparent, "HasModelParent");
@@ -608,8 +602,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hasmodelparent->isAbstract = UA_FALSE;
     hasmodelparent->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hasmodelparent,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)fromstate, "FromState");
@@ -618,8 +612,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     fromstate->isAbstract = UA_FALSE;
     fromstate->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)fromstate,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)tostate, "ToState");
@@ -628,8 +622,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     tostate->isAbstract = UA_FALSE;
     tostate->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)tostate,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascause, "HasCause");
@@ -638,9 +632,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hascause->isAbstract = UA_FALSE;
     hascause->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hascause,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
-
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+    
     UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseffect, "HasEffect");
     haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "MayBeEffectedBy");
@@ -648,8 +642,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     haseffect->isAbstract = UA_FALSE;
     haseffect->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)haseffect,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hashistoricalconfiguration, "HasHistoricalConfiguration");
@@ -658,8 +652,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     hashistoricalconfiguration->isAbstract = UA_FALSE;
     hashistoricalconfiguration->symmetric  = UA_FALSE;
     UA_Server_addNode(server, (UA_Node*)hashistoricalconfiguration,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
 
     /*****************/
     /* Basic Folders */
@@ -674,22 +668,32 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     copyNames((UA_Node*)objects, "Objects");
     objects->nodeId.identifier.numeric = UA_NS0ID_OBJECTSFOLDER;
     UA_Server_addNode(server, (UA_Node*)objects,
-        &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-        &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
     UA_ObjectNode *types = UA_ObjectNode_new();
     copyNames((UA_Node*)types, "Types");
     types->nodeId.identifier.numeric = UA_NS0ID_TYPESFOLDER;
     UA_Server_addNode(server, (UA_Node*)types,
-        &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-        &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
     UA_ObjectNode *views = UA_ObjectNode_new();
     copyNames((UA_Node*)views, "Views");
     views->nodeId.identifier.numeric = UA_NS0ID_VIEWSFOLDER;
     UA_Server_addNode(server, (UA_Node*)views,
-        &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
-        &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+
+    UA_ObjectNode *referencetypes = UA_ObjectNode_new();
+    copyNames((UA_Node*)referencetypes, "ReferenceTypes");
+    referencetypes->nodeId.identifier.numeric = UA_NS0ID_REFERENCETYPESFOLDER;
+    UA_Server_addNode(server, (UA_Node*)referencetypes,
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCETYPESFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES));
 
     /**********************/
     /* Basic Object Types */
@@ -699,8 +703,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     copyNames((UA_Node*)objecttypes, "ObjectTypes");
     objecttypes->nodeId.identifier.numeric = UA_NS0ID_OBJECTTYPESFOLDER;
     UA_Server_addNode(server, (UA_Node*)objecttypes,
-        &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-        &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
     addObjectTypeNode(server, "BaseObjectType", UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_OBJECTTYPESFOLDER, UA_NS0ID_ORGANIZES);
     addObjectTypeNode(server, "FolderType", UA_NS0ID_FOLDERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
@@ -714,10 +718,13 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
     ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCETYPESFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+    addObjectTypeNode(server, "ServerType", UA_NS0ID_SERVERTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerDiagnosticsType", UA_NS0ID_SERVERDIAGNOSTICSTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerCapatilitiesType", UA_NS0ID_SERVERCAPABILITIESTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
     addObjectTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
-
+    addObjectTypeNode(server, "BuildInfoType", UA_NS0ID_BUILDINFOTYPE, UA_NS0ID_BASEOBJECTTYPE, UA_NS0ID_HASSUBTYPE);
 
     /**************/
     /* Data Types */
@@ -727,8 +734,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     copyNames((UA_Node*)datatypes, "DataTypes");
     datatypes->nodeId.identifier.numeric = UA_NS0ID_DATATYPESFOLDER;
     UA_Server_addNode(server, (UA_Node*)datatypes,
-                      &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                      &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
     ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_DATATYPESFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                  UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
 
@@ -758,6 +765,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     addDataTypeNode(server, "QualifiedName", UA_NS0ID_QUALIFIEDNAME, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "LocalizedText", UA_NS0ID_LOCALIZEDTEXT, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "Structure", UA_NS0ID_STRUCTURE, UA_NS0ID_BASEDATATYPE);
+        addDataTypeNode(server, "ServerStatusDataType", UA_NS0ID_SERVERSTATUSDATATYPE, UA_NS0ID_STRUCTURE);
+        addDataTypeNode(server, "BuildInfo", UA_NS0ID_BUILDINFO, UA_NS0ID_STRUCTURE);
     addDataTypeNode(server, "DataValue", UA_NS0ID_DATAVALUE, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "DiagnosticInfo", UA_NS0ID_DIAGNOSTICINFO, UA_NS0ID_BASEDATATYPE);
     addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, UA_NS0ID_BASEDATATYPE);
@@ -767,8 +776,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    copyNames((UA_Node*)variabletypes, "VariableTypes");
    variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
    UA_Server_addNode(server, (UA_Node*)variabletypes,
-                     &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
-                     &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+                     UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
+                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_VARIABLETYPESFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                 UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
    addVariableTypeNode_organized(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE, UA_NS0ID_VARIABLETYPESFOLDER, UA_TRUE);
@@ -783,8 +792,11 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    copyNames((UA_Node*)servernode, "Server");
    servernode->nodeId.identifier.numeric = UA_NS0ID_SERVER;
    UA_Server_addNode(server, (UA_Node*)servernode,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
+   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+       UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE));
 
    UA_VariableNode *namespaceArray = UA_VariableNode_new();
    copyNames((UA_Node*)namespaceArray, "NamespaceArray");
@@ -796,8 +808,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    namespaceArray->minimumSamplingInterval = 1.0;
    namespaceArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)namespaceArray,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+           UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                 UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
@@ -813,8 +825,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    serverArray->minimumSamplingInterval = 1.0;
    serverArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)serverArray,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+           UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY),
                 UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                 UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
@@ -823,8 +835,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    copyNames((UA_Node*)servercapablities, "ServerCapabilities");
    servercapablities->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES;
    UA_Server_addNode(server, (UA_Node*)servercapablities,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+           UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
 
@@ -839,126 +851,188 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    localeIdArray->minimumSamplingInterval = 1.0;
    localeIdArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)localeIdArray,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+           UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
-   UA_ObjectNode *serverdiagnostics = UA_ObjectNode_new();
-   copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
-   serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
-   UA_Server_addNode(server, (UA_Node*)serverdiagnostics,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
-
-   UA_VariableNode *enabledFlag = UA_VariableNode_new();
-   copyNames((UA_Node*)enabledFlag, "EnabledFlag");
-   enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
-   enabledFlag->value.variant.data = UA_Boolean_new(); //itialized as false
-   enabledFlag->value.variant.type = &UA_TYPES[UA_TYPES_BOOLEAN];
-   enabledFlag->valueRank = 1;
-   enabledFlag->minimumSamplingInterval = 1.0;
-   enabledFlag->historizing = UA_FALSE;
-   UA_Server_addNode(server, (UA_Node*)enabledFlag,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
-
-   UA_VariableNode *serverstatus = UA_VariableNode_new();
-   copyNames((UA_Node*)serverstatus, "ServerStatus");
-   serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
-   serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
-   serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
-       .release = releaseStatus, .write = UA_NULL};
-   UA_Server_addNode(server, (UA_Node*)serverstatus, &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE));
-
-   UA_VariableNode *state = UA_VariableNode_new();
-   UA_ServerState *stateEnum = UA_ServerState_new();
-   *stateEnum = UA_SERVERSTATE_RUNNING;
-   copyNames((UA_Node*)state, "State");
-   state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-   state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-   state->value.variant.arrayLength = -1;
-   state->value.variant.data = stateEnum; // points into the other object.
-   UA_NodeStore_insert(server->nodestore, (UA_Node*)state, UA_NULL);
-   ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE));
-
-   UA_VariableNode *currenttime = UA_VariableNode_new();
-   copyNames((UA_Node*)currenttime, "CurrentTime");
-   currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
-   currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
-   currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
-       .release = releaseCurrentTime, .write = UA_NULL};
-   UA_Server_addNode(server, (UA_Node*)currenttime, &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
-
-#ifdef DEMO_NODESET
-
-   /**************/
-   /* Demo Nodes */
-   /**************/
-
-#define DEMOID 990
-   UA_ObjectNode *demo = UA_ObjectNode_new();
-   copyNames((UA_Node*)demo, "Demo");
-   demo->nodeId = UA_NODEID_NUMERIC(1, DEMOID);
-   UA_Server_addNode(server, (UA_Node*)demo,
-           &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
-   ADDREFERENCE(UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
-
-#define SCALARID 991
-   UA_ObjectNode *scalar = UA_ObjectNode_new();
-   copyNames((UA_Node*)scalar, "Scalar");
-   scalar->nodeId = UA_NODEID_NUMERIC(1, SCALARID);
-   UA_Server_addNode(server, (UA_Node*)scalar,
-           &UA_EXPANDEDNODEID_NUMERIC(1, DEMOID),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
-   ADDREFERENCE(UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
-
-#define ARRAYID 992
-   UA_ObjectNode *array = UA_ObjectNode_new();
-   copyNames((UA_Node*)array, "Arrays");
-   array->nodeId = UA_NODEID_NUMERIC(1, ARRAYID);
-   UA_Server_addNode(server, (UA_Node*)array,
-           &UA_EXPANDEDNODEID_NUMERIC(1, DEMOID),
-           &UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
-   ADDREFERENCE(UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
-           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
-
-   UA_UInt32 id = 1000; //running id in namespace 1
-   for(UA_UInt32 type = 0; UA_IS_BUILTIN(type); type++) {
-       if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
-           continue;
-       //add a scalar node for every built-in type
-        void *value = UA_new(&UA_TYPES[type]);
-        UA_Variant *variant = UA_Variant_new();
-        UA_Variant_setScalar(variant, value, &UA_TYPES[type]);
-        char name[15];
-        sprintf(name, "%02d", type);
-        UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, name);
-        UA_Server_addVariableNode(server, variant, myIntegerName, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
-
-        //add an array node for every built-in type
-        UA_Variant *arrayvar = UA_Variant_new();
-        UA_Variant_setArray(arrayvar, UA_Array_new(&UA_TYPES[type], 10), 10, &UA_TYPES[type]);
-        UA_Server_addVariableNode(server, arrayvar, myIntegerName, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
-   }
-#endif
-#else
-  #include "ua_namespaceinit_generated.h"
-#endif
+   UA_VariableNode *maxBrowseContinuationPoints = UA_VariableNode_new();
+    copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
+    maxBrowseContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
+    maxBrowseContinuationPoints->value.variant.data = UA_UInt16_new();
+    *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.data) = MAXCONTINUATIONPOINTS;
+    maxBrowseContinuationPoints->value.variant.type = &UA_TYPES[UA_TYPES_UINT16];
+    UA_Server_addNode(server, (UA_Node*)maxBrowseContinuationPoints,
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
+
+    UA_ObjectNode *serverdiagnostics = UA_ObjectNode_new();
+    copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
+    serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
+    UA_Server_addNode(server, (UA_Node*)serverdiagnostics,
+            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE));
+
+    UA_VariableNode *enabledFlag = UA_VariableNode_new();
+     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
+     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
+     enabledFlag->value.variant.data = UA_Boolean_new(); //itialized as false
+     enabledFlag->value.variant.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+     enabledFlag->valueRank = 1;
+     enabledFlag->minimumSamplingInterval = 1.0;
+     enabledFlag->historizing = UA_FALSE;
+     UA_Server_addNode(server, (UA_Node*)enabledFlag,
+             UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
+             UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
+     ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+             UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
+
+     UA_VariableNode *serverstatus = UA_VariableNode_new();
+      copyNames((UA_Node*)serverstatus, "ServerStatus");
+      serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
+      serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
+      serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
+          .release = releaseStatus, .write = UA_NULL};
+      UA_Server_addNode(server, (UA_Node*)serverstatus, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE));
+
+      UA_VariableNode *starttime = UA_VariableNode_new();
+      copyNames((UA_Node*)starttime, "StartTime");
+      starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
+      starttime->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
+      starttime->value.variant.data = &server->startTime;
+      starttime->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+      UA_Server_addNode(server, (UA_Node*)starttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *currenttime = UA_VariableNode_new();
+      copyNames((UA_Node*)currenttime, "CurrentTime");
+      currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+      currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
+      currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
+          .release = releaseCurrentTime, .write = UA_NULL};
+      UA_Server_addNode(server, (UA_Node*)currenttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *state = UA_VariableNode_new();
+      UA_ServerState *stateEnum = UA_ServerState_new();
+      *stateEnum = UA_SERVERSTATE_RUNNING;
+      copyNames((UA_Node*)state, "State");
+      state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
+      state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
+      state->value.variant.arrayLength = -1;
+      state->value.variant.data = stateEnum; // points into the other object.
+      UA_Server_addNode(server, (UA_Node*)state, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+              UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+      ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+      UA_VariableNode *buildinfo = UA_VariableNode_new();
+       copyNames((UA_Node*)buildinfo, "BuildInfo");
+       buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
+       buildinfo->value.variant.data = UA_BuildInfo_new();
+       buildinfo->value.variant.type = &UA_TYPES[UA_TYPES_BUILDINFO];
+       getBulidInfo(server, (UA_BuildInfo*)buildinfo->value.variant.data);
+       UA_Server_addNode(server, (UA_Node*)buildinfo, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE));
+
+       UA_VariableNode *producturi = UA_VariableNode_new();
+       copyNames((UA_Node*)producturi, "ProductUri");
+       producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
+       producturi->value.variant.data = UA_String_new();
+       *((UA_String*)producturi->value.variant.data) = UA_STRING_ALLOC(PRODUCT_URI);
+       producturi->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)producturi, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *manufacturername = UA_VariableNode_new();
+       copyNames((UA_Node*)manufacturername, "ManufacturererName");
+       manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
+       manufacturername->value.variant.data = UA_String_new();
+       *((UA_String*)manufacturername->value.variant.data) = UA_STRING_ALLOC(MANUFACTURER_NAME);
+       manufacturername->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)manufacturername, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *productname = UA_VariableNode_new();
+       copyNames((UA_Node*)productname, "ProductName");
+       productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
+       productname->value.variant.data = UA_String_new();
+       *((UA_String*)productname->value.variant.data) = UA_STRING_ALLOC(PRODUCT_NAME);
+       productname->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)productname, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *softwareversion = UA_VariableNode_new();
+       copyNames((UA_Node*)softwareversion, "SoftwareVersion");
+       softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
+       softwareversion->value.variant.data = UA_String_new();
+       *((UA_String*)softwareversion->value.variant.data) = UA_STRING_ALLOC(SOFTWARE_VERSION);
+       softwareversion->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)softwareversion, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *buildnumber = UA_VariableNode_new();
+       copyNames((UA_Node*)buildnumber, "BuildNumber");
+       buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
+       buildnumber->value.variant.data = UA_String_new();
+       *((UA_String*)buildnumber->value.variant.data) = UA_STRING_ALLOC(BUILD_NUMBER);
+       buildnumber->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       UA_Server_addNode(server, (UA_Node*)buildnumber, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *builddate = UA_VariableNode_new();
+       copyNames((UA_Node*)builddate, "BuildDate");
+       builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
+       builddate->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
+       builddate->value.variant.data = &server->buildDate;
+       builddate->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+       UA_Server_addNode(server, (UA_Node*)builddate, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *secondstillshutdown = UA_VariableNode_new();
+       copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
+       secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
+       secondstillshutdown->value.variant.data = UA_UInt32_new();
+       secondstillshutdown->value.variant.type = &UA_TYPES[UA_TYPES_UINT32];
+       UA_Server_addNode(server, (UA_Node*)secondstillshutdown, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
+
+       UA_VariableNode *shutdownreason = UA_VariableNode_new();
+       copyNames((UA_Node*)shutdownreason, "ShutdownReason");
+       shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
+       shutdownreason->value.variant.data = UA_LocalizedText_new();
+       shutdownreason->value.variant.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+       UA_Server_addNode(server, (UA_Node*)shutdownreason, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+               UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
+       ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON), UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
+           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
 
    return server;
 }

+ 44 - 13
src/server/ua_server_addressspace.c

@@ -14,7 +14,7 @@ UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_Qualifi
     UA_ExpandedNodeId_init(&parentId);
     UA_NodeId_copy(&parentNodeId, &parentId.nodeId);
     UA_AddNodesResult res =
-        UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, &parentId, &referenceTypeId);
+        UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId);
     ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                  UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric));
     if(res.statusCode != UA_STATUSCODE_GOOD) {
@@ -26,6 +26,33 @@ UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_Qualifi
     return res.statusCode;
 }
 
+UA_StatusCode
+UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName,
+                          UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_NodeId typeDefinition)
+{
+    UA_ObjectNode *node = UA_ObjectNode_new();
+    UA_NodeId_copy(&nodeId, &node->nodeId);
+    UA_QualifiedName_copy(&browseName, &node->browseName);
+    UA_String_copy(&browseName.name, &node->displayName.text);
+    UA_ExpandedNodeId parentId; // we need an expandednodeid
+    UA_ExpandedNodeId_init(&parentId);
+    UA_NodeId_copy(&parentNodeId, &parentId.nodeId);
+    UA_AddNodesResult res =
+        UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId);
+    if(res.statusCode != UA_STATUSCODE_GOOD) {
+        UA_ObjectNode_delete(node);
+    }
+    UA_AddNodesResult_deleteMembers(&res);
+
+    if(!(UA_NodeId_isNull(&typeDefinition))){
+        UA_ExpandedNodeId typeDefid; // we need an expandednodeid
+        UA_ExpandedNodeId_init(&typeDefid);
+        UA_NodeId_copy(&typeDefinition, &typeDefid.nodeId);
+        ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), typeDefid);
+    }
+    return res.statusCode;
+}
+
 UA_StatusCode
 UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                                     const UA_QualifiedName browseName, UA_NodeId nodeId,
@@ -41,7 +68,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
     UA_ExpandedNodeId_init(&parentId);
     UA_NodeId_copy(&parentNodeId, &parentId.nodeId);
     UA_AddNodesResult res =
-        UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, &parentId, &referenceTypeId);
+        UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId);
     ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                  UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
     if(res.statusCode != UA_STATUSCODE_GOOD)
@@ -52,8 +79,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
 
 /* Adds a one-way reference to the local nodestore */
 static UA_StatusCode
-addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item)
-{
+addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item) {
     const UA_Node *node = UA_NodeStore_get(server->nodestore, &item->sourceNodeId);
     if(!node)
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -62,7 +88,8 @@ addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, const UA_A
 	size_t i = node->referencesSize;
 	if(node->referencesSize < 0)
 		i = 0;
-	UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * (i + 1));
+    size_t refssize = (i+1) | 3; // so the realloc is not necessary every time
+	UA_ReferenceNode *new_refs = UA_realloc(node->references, sizeof(UA_ReferenceNode) * refssize);
 	if(!new_refs)
 		retval = UA_STATUSCODE_BADOUTOFMEMORY;
 	else {
@@ -182,7 +209,9 @@ UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const
     if(item->targetServerUri.length > 0)
         return UA_STATUSCODE_BADNOTIMPLEMENTED; // currently no expandednodeids are allowed
     
-    // Is this for an external nodestore?
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_ExternalNodeStore *ensFirst = UA_NULL;
     UA_ExternalNodeStore *ensSecond = UA_NULL;
     for(size_t j = 0;j<server->externalNamespacesSize && (!ensFirst || !ensSecond);j++) {
@@ -192,10 +221,10 @@ UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const
             ensSecond = &server->externalNamespaces[j].externalNodeStore;
     }
 
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(ensFirst) {
         // todo: use external nodestore
     } else
+#endif
         retval = addOneWayReferenceWithSession(server, session, item);
 
     if(retval) return retval;
@@ -205,26 +234,28 @@ UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
     secondItem.isForward = !item->isForward;
+#ifdef UA_EXTERNAL_NAMESPACES
     if(ensSecond) {
         // todo: use external nodestore
     } else
+#endif
         retval = addOneWayReferenceWithSession (server, session, &secondItem);
-    // todo: remove reference if the second direction failed
 
+    // todo: remove reference if the second direction failed
     return retval;
 } 
 
 /* userland version of addNodeWithSession */
 UA_AddNodesResult
-UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId *parentNodeId,
-                  const UA_NodeId *referenceTypeId)
+UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId parentNodeId,
+                  const UA_NodeId referenceTypeId)
 {
     return UA_Server_addNodeWithSession(server, &adminSession, node, parentNodeId, referenceTypeId);
 }
 
 UA_AddNodesResult
 UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *node,
-                             const UA_ExpandedNodeId *parentNodeId, const UA_NodeId *referenceTypeId)
+                             const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId)
 {
     UA_AddNodesResult result;
     UA_AddNodesResult_init(&result);
@@ -234,14 +265,14 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
         return result;
     }
 
-    const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId->nodeId);
+    const UA_Node *parent = UA_NodeStore_get(server->nodestore, &parentNodeId.nodeId);
     if(!parent) {
         result.statusCode = UA_STATUSCODE_BADPARENTNODEIDINVALID;
         return result;
     }
 
     const UA_ReferenceTypeNode *referenceType =
-        (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, referenceTypeId);
+        (const UA_ReferenceTypeNode *)UA_NodeStore_get(server->nodestore, &referenceTypeId);
     if(!referenceType) {
         result.statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
         goto ret;

+ 51 - 28
src/server/ua_server_binary.c

@@ -22,6 +22,10 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
     connection->remoteConf.maxMessageSize = helloMessage.maxMessageSize;
     connection->remoteConf.protocolVersion = helloMessage.protocolVersion;
     connection->remoteConf.recvBufferSize = helloMessage.receiveBufferSize;
+    if(connection->localConf.sendBufferSize > helloMessage.receiveBufferSize)
+        connection->localConf.sendBufferSize = helloMessage.receiveBufferSize;
+    if(connection->localConf.recvBufferSize > helloMessage.sendBufferSize)
+        connection->localConf.recvBufferSize = helloMessage.sendBufferSize;
     connection->remoteConf.sendBufferSize = helloMessage.sendBufferSize;
     connection->state = UA_CONNECTION_ESTABLISHED;
     UA_TcpHelloMessage_deleteMembers(&helloMessage);
@@ -103,16 +107,21 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
 
     UA_ByteString resp_msg;
     retval = connection->getBuffer(connection, &resp_msg, respHeader.messageHeader.messageSize);
-
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_OpenSecureChannelResponse_deleteMembers(&p);
+        UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+        return;
+    }
+        
     size_t tmpPos = 0;
     UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
     UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
     UA_SequenceHeader_encodeBinary(&seqHeader, &resp_msg, &tmpPos); // just mirror back
     UA_NodeId_encodeBinary(&responseType, &resp_msg, &tmpPos);
     UA_OpenSecureChannelResponse_encodeBinary(&p, &resp_msg, &tmpPos);
-
     UA_OpenSecureChannelResponse_deleteMembers(&p);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
+
     connection->write(connection, &resp_msg);
     connection->releaseBuffer(connection, &resp_msg);
 }
@@ -123,35 +132,49 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
     r->timestamp = UA_DateTime_now();
 }
 
+/* The request/response are casted to the header (first element of their struct) */
+static void invoke_service(UA_Server *server, UA_SecureChannel *channel,
+                           UA_RequestHeader *request, UA_ResponseHeader *response,
+                           void (*service)(UA_Server*, UA_Session*, void*, void*)) {
+    init_response_header(request, response);
+    /* try to get the session from the securechannel first */
+    UA_Session *session = UA_SecureChannel_getSession(channel, &request->authenticationToken);
+    if(!session)
+        session = UA_SessionManager_getSession(&server->sessionManager, &request->authenticationToken);
+    if(!session)
+        response->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
+    else if(session->activated == UA_FALSE) {
+        response->serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED;
+        /* the session is invalidated */
+        UA_SessionManager_removeSession(&server->sessionManager, &request->authenticationToken);
+    }
+    else if(session->channel != channel)
+        response->serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
+    else {
+            UA_Session_updateLifetime(session);
+            service(server, session, request, response);
+    }
+}
+
 #define INVOKE_SERVICE(TYPE) do {                                       \
         UA_##TYPE##Request p;                                           \
         UA_##TYPE##Response r;                                          \
         if(UA_##TYPE##Request_decodeBinary(msg, pos, &p))               \
             return;                                                     \
         UA_##TYPE##Response_init(&r);                                   \
-        init_response_header(&p.requestHeader, &r.responseHeader);      \
-        if(!clientChannel->session || !UA_NodeId_equal(&clientChannel->session->authenticationToken,\
-                &p.requestHeader.authenticationToken))                  \
-            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;     \
-        else if(clientChannel->session->channel != clientChannel){      \
-            r.responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID; \
-        }                                                               \
-        else if(clientChannel->session->activated == UA_FALSE){         \
-            UA_SessionManager_removeSession(&server->sessionManager, &clientChannel->session->sessionId); \
-            r.responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONNOTACTIVATED; \
-        }else{                                                          \
-            clientSession = clientChannel->session;                     \
-            Service_##TYPE(server, clientSession, &p, &r);              \
-        }                                                               \
+        invoke_service(server, clientChannel, &p.requestHeader,         \
+                       &r.responseHeader,                               \
+                       (void (*)(UA_Server*, UA_Session*, void*,void*))Service_##TYPE); \
         UA_##TYPE##Request_deleteMembers(&p);                           \
-        retval = connection->getBuffer(connection, &message, headerSize + UA_##TYPE##Response_calcSizeBinary(&r)); \
+        retval = connection->getBuffer(connection, &message,            \
+                     headerSize + UA_##TYPE##Response_calcSizeBinary(&r)); \
         if(retval != UA_STATUSCODE_GOOD) {                              \
             UA_##TYPE##Response_deleteMembers(&r);                      \
             return;                                                     \
         }                                                               \
         UA_##TYPE##Response_encodeBinary(&r, &message, &messagePos);    \
         UA_##TYPE##Response_deleteMembers(&r);                          \
-    } while(0)
+} while(0)
 
 static void processMSG(UA_Connection *connection, UA_Server *server, const UA_ByteString *msg, size_t *pos) {
     // 1) Read in the securechannel
@@ -165,6 +188,7 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     UA_SecureChannel anonymousChannel;
     if(!clientChannel) {
         UA_SecureChannel_init(&anonymousChannel);
+        anonymousChannel.session = &anonymousSession;
         clientChannel = &anonymousChannel;
     }
 #endif
@@ -182,12 +206,6 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     clientChannel->sequenceNumber = sequenceHeader.sequenceNumber;
     clientChannel->requestId = sequenceHeader.requestId;
 
-    UA_Session *clientSession = UA_NULL;
-#ifdef EXTENSION_STATELESS
-    if(clientChannel == &anonymousChannel)
-        clientSession = &anonymousSession;
-#endif
-
     // 3) Build the header and compute the header size
     UA_SecureConversationMessageHeader respHeader;
     respHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_MSGF;
@@ -343,8 +361,13 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
 #endif
 
     default: {
-        UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)",
-                    requestType.namespaceIndex, requestType.identifier.numeric);
+        if(requestType.namespaceIndex == 0 && requestType.identifier.numeric==787){
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION,
+                        "Client requested a subscription that are not supported, the message will be skipped");
+        }else{
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "Unknown request: NodeId(ns=%d, i=%d)",
+                        requestType.namespaceIndex, requestType.identifier.numeric);
+        }
         UA_RequestHeader  p;
         UA_ResponseHeader r;
         if(UA_RequestHeader_decodeBinary(msg, pos, &p) != UA_STATUSCODE_GOOD)
@@ -399,7 +422,7 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
     UA_TcpMessageHeader tcpMessageHeader;
     do {
         if(UA_TcpMessageHeader_decodeBinary(msg, &pos, &tcpMessageHeader) != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION, "Decoding of message header failed");
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION, "Decoding of message header failed");
             connection->close(connection);
             break;
         }
@@ -432,7 +455,7 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
 
         UA_TcpMessageHeader_deleteMembers(&tcpMessageHeader);
         if(pos != targetpos) {
-            UA_LOG_INFO(server->logger, UA_LOGGERCATEGORY_COMMUNICATION,
+            UA_LOG_INFO(server->logger, UA_LOGCATEGORY_COMMUNICATION,
                         "The message was not entirely processed, skipping to the end");
             pos = targetpos;
         }

+ 6 - 5
src/server/ua_server_internal.h

@@ -49,7 +49,7 @@ struct UA_Server {
     UA_ByteString serverCertificate;
     UA_SecureChannelManager secureChannelManager;
     UA_SessionManager sessionManager;
-    
+
     /* Address Space */
     UA_NodeStore *nodestore;
     size_t namespacesSize;
@@ -64,6 +64,7 @@ struct UA_Server {
     UA_Boolean *running;
     UA_UInt16 nThreads;
     UA_UInt32 **workerCounters;
+    pthread_t *thr;
     struct DelayedWork *delayedWork;
 
     // worker threads wait on the queue
@@ -76,11 +77,11 @@ struct UA_Server {
 void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, UA_ByteString *msg);
 
 UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *node,
-                                               const UA_ExpandedNodeId *parentNodeId,
-                                               const UA_NodeId *referenceTypeId);
+                                               const UA_ExpandedNodeId parentNodeId,
+                                               const UA_NodeId referenceTypeId);
 
-UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId *parentNodeId,
-                                    const UA_NodeId *referenceTypeId);
+UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId parentNodeId,
+                                    const UA_NodeId referenceTypeId);
 
 UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session, const UA_AddReferencesItem *item);
 

+ 137 - 91
src/server/ua_server_worker.c

@@ -12,11 +12,11 @@
  *    all previous work has actually finished (only for multithreading)
  */
 
-#define MAXTIMEOUT 50000 // max timeout in usec until the next main loop iteration
+#define MAXTIMEOUT 50000 // max timeout in microsec until the next main loop iteration
 #define BATCHSIZE 20 // max size of worklists that are dispatched to workers
 
-static void processWork(UA_Server *server, UA_WorkItem *work, UA_Int32 workSize) {
-    for(UA_Int32 i = 0; i < workSize; i++) {
+static void processWork(UA_Server *server, UA_WorkItem *work, size_t workSize) {
+    for(size_t i = 0; i < workSize; i++) {
         UA_WorkItem *item = &work[i];
         switch(item->type) {
         case UA_WORKITEMTYPE_BINARYMESSAGE:
@@ -135,71 +135,98 @@ static void emptyDispatchQueue(UA_Server *server) {
 /* Timed Work */
 /**************/
 
+/**
+ * The TimedWork structure contains an array of workitems that are either executed at the same time
+ * or in the same repetition inverval. The linked list is sorted, so we can stop traversing when the
+ * first element has nextTime > now.
+ */
 struct TimedWork {
     LIST_ENTRY(TimedWork) pointers;
     UA_DateTime nextTime;
-    UA_UInt32 interval; ///> in ms resolution, 0 means no repetition
+    UA_UInt32 interval; ///> in 100ns resolution, 0 means no repetition
     size_t workSize;
-    struct {
-        UA_WorkItem work;
-        UA_Guid workId;
-    } work[];
+    UA_WorkItem *work;
+    UA_Guid workIds[];
 };
 
-/* The item is copied and not freed by this function. */
+/* Traverse the list until there is a TimedWork to which the item can be added or we reached the
+   end. The item is copied into the TimedWork and not freed by this function. The interval is in
+   100ns resolution */
 static UA_StatusCode addTimedWork(UA_Server *server, const UA_WorkItem *item, UA_DateTime firstTime,
-                                  UA_UInt32 repetitionInterval, UA_Guid *resultWorkGuid) {
-    struct TimedWork *lastTw = UA_NULL, *matchingTw = UA_NULL;
+                                  UA_UInt32 interval, UA_Guid *resultWorkGuid) {
+    struct TimedWork *matchingTw = UA_NULL; // add the item here
+    struct TimedWork *lastTw = UA_NULL; // if there is no matchingTw, add a new TimedWork after this entry
+    struct TimedWork *tempTw;
 
     /* search for matching entry */
-    if(repetitionInterval == 0) {
-        LIST_FOREACH(lastTw, &server->timedWork, pointers) {
-            if(lastTw->nextTime == firstTime) {
-                if(lastTw->nextTime == firstTime)
-                    matchingTw = lastTw;
+    tempTw = LIST_FIRST(&server->timedWork);
+    if(interval == 0) {
+        /* single execution. the time needs to match */
+        while(tempTw) {
+            if(tempTw->nextTime >= firstTime) {
+                if(tempTw->nextTime == firstTime)
+                    matchingTw = tempTw;
                 break;
             }
+            lastTw = tempTw;
+            tempTw = LIST_NEXT(lastTw, pointers);
         }
     } else {
-        LIST_FOREACH(matchingTw, &server->timedWork, pointers) {
-            if(repetitionInterval == matchingTw->interval)
+        /* repeated execution. the interval needs to match */
+        while(tempTw) {
+            if(interval == tempTw->interval) {
+                matchingTw = tempTw;
                 break;
+            }
+            if(tempTw->nextTime > firstTime)
+                break;
+            lastTw = tempTw;
+            tempTw = LIST_NEXT(lastTw, pointers);
         }
     }
     
-    struct TimedWork *newWork;
     if(matchingTw) {
         /* append to matching entry */
-        newWork = UA_realloc(matchingTw, sizeof(struct TimedWork) + (sizeof(UA_WorkItem)*matchingTw->workSize + 1));
-        if(!newWork)
+        matchingTw = UA_realloc(matchingTw, sizeof(struct TimedWork) + sizeof(UA_Guid)*(matchingTw->workSize + 1));
+        if(!matchingTw)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        if(matchingTw->pointers.le_next)
+            matchingTw->pointers.le_next->pointers.le_prev = &matchingTw->pointers.le_next;
+        if(matchingTw->pointers.le_prev)
+            *matchingTw->pointers.le_prev = matchingTw;
+        UA_WorkItem *newItems = UA_realloc(matchingTw->work, sizeof(UA_WorkItem)*(matchingTw->workSize + 1));
+        if(!newItems)
             return UA_STATUSCODE_BADOUTOFMEMORY;
-        if(newWork->pointers.le_next)
-            newWork->pointers.le_next->pointers.le_prev = &newWork->pointers.le_next;
-        if(newWork->pointers.le_prev)
-            *newWork->pointers.le_prev = newWork;
+        matchingTw->work = newItems;
     } else {
         /* create a new entry */
-        newWork = UA_malloc(sizeof(struct TimedWork) + sizeof(UA_WorkItem));
-        if(!newWork)
+        matchingTw = UA_malloc(sizeof(struct TimedWork) + sizeof(UA_Guid));
+        if(!matchingTw)
             return UA_STATUSCODE_BADOUTOFMEMORY;
-        newWork->workSize = 0;
-        newWork->nextTime = firstTime;
-        newWork->interval = repetitionInterval;
+        matchingTw->work = UA_malloc(sizeof(UA_WorkItem));
+        if(!matchingTw->work) {
+            UA_free(matchingTw);
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        }
+        matchingTw->workSize = 0;
+        matchingTw->nextTime = firstTime;
+        matchingTw->interval = interval;
         if(lastTw)
-            LIST_INSERT_AFTER(lastTw, newWork, pointers);
+            LIST_INSERT_AFTER(lastTw, matchingTw, pointers);
         else
-            LIST_INSERT_HEAD(&server->timedWork, newWork, pointers);
+            LIST_INSERT_HEAD(&server->timedWork, matchingTw, pointers);
     }
-    newWork->work[newWork->workSize].work = *item;
+    matchingTw->work[matchingTw->workSize] = *item;
+    matchingTw->workSize++;
+
+    /* create a guid for finding and deleting the timed work later on */
     if(resultWorkGuid) {
-        newWork->work[newWork->workSize].workId = UA_Guid_random(&server->random_seed);
-        *resultWorkGuid = newWork->work[matchingTw->workSize - 1].workId;
+        matchingTw->workIds[matchingTw->workSize] = UA_Guid_random(&server->random_seed);
+        *resultWorkGuid = matchingTw->workIds[matchingTw->workSize];
     }
-    newWork->workSize++;
     return UA_STATUSCODE_GOOD;
 }
 
-// Currently, these functions need to get the server mutex, but should be sufficiently fast
 UA_StatusCode UA_Server_addTimedWorkItem(UA_Server *server, const UA_WorkItem *work, UA_DateTime executionTime,
                                          UA_Guid *resultWorkGuid) {
     return addTimedWork(server, work, executionTime, 0, resultWorkGuid);
@@ -207,7 +234,7 @@ UA_StatusCode UA_Server_addTimedWorkItem(UA_Server *server, const UA_WorkItem *w
 
 UA_StatusCode UA_Server_addRepeatedWorkItem(UA_Server *server, const UA_WorkItem *work, UA_UInt32 interval,
                                             UA_Guid *resultWorkGuid) {
-    return addTimedWork(server, work, UA_DateTime_now() + interval * 1000, interval * 1000, resultWorkGuid);
+    return addTimedWork(server, work, UA_DateTime_now() + interval * 10000, interval * 10000, resultWorkGuid);
 }
 
 /** Dispatches timed work, returns the timeout until the next timed work in ms */
@@ -223,17 +250,16 @@ static UA_UInt16 processTimedWork(UA_Server *server) {
         next = LIST_NEXT(tw, pointers);
 
 #ifdef UA_MULTITHREADING
-        if(tw->repetitionInterval > 0) {
+        if(tw->interval > 0) {
             // copy the entry and insert at the new location
             UA_WorkItem *workCopy = (UA_WorkItem *) UA_malloc(sizeof(UA_WorkItem) * tw->workSize);
             UA_memcpy(workCopy, tw->work, sizeof(UA_WorkItem) * tw->workSize);
             dispatchWork(server, tw->workSize, workCopy); // frees the work pointer
-            tw->time += tw->repetitionInterval;
-
+            tw->nextTime += tw->interval;
             struct TimedWork *prevTw = tw; // after which tw do we insert?
             while(UA_TRUE) {
                 struct TimedWork *n = LIST_NEXT(prevTw, pointers);
-                if(!n || n->time > tw->time)
+                if(!n || n->nextTime > tw->nextTime)
                     break;
                 prevTw = n;
             }
@@ -244,16 +270,17 @@ static UA_UInt16 processTimedWork(UA_Server *server) {
         } else {
             dispatchWork(server, tw->workSize, tw->work); // frees the work pointer
             LIST_REMOVE(tw, pointers);
-            UA_free(tw->workIds);
             UA_free(tw);
         }
 #else
         // 1) Process the work since it is past its due date
-        processWork(server, (UA_WorkItem *) tw->work, tw->workSize); // does not free the work
+        processWork(server, tw->work, tw->workSize); // does not free the work ptr
 
         // 2) If the work is repeated, add it back into the list. Otherwise remove it.
         if(tw->interval > 0) {
-            tw->nextTime += tw->interval * 10;
+            tw->nextTime += tw->interval;
+            if(tw->nextTime < current)
+                tw->nextTime = current;
             struct TimedWork *prevTw = tw;
             while(UA_TRUE) {
                 struct TimedWork *n = LIST_NEXT(prevTw, pointers);
@@ -267,6 +294,7 @@ static UA_UInt16 processTimedWork(UA_Server *server) {
             }
         } else {
             LIST_REMOVE(tw, pointers);
+            UA_free(tw->work);
             UA_free(tw);
         }
 #endif
@@ -276,7 +304,7 @@ static UA_UInt16 processTimedWork(UA_Server *server) {
     struct TimedWork *first = LIST_FIRST(&server->timedWork);
     UA_UInt16 timeout = MAXTIMEOUT;
     if(first) {
-        timeout = (first->nextTime - current)/10000;
+        timeout = (first->nextTime - current)/10;
         if(timeout > MAXTIMEOUT)
             return MAXTIMEOUT;
     }
@@ -290,6 +318,7 @@ void UA_Server_deleteTimedWork(UA_Server *server) {
         current = next;
         next = LIST_NEXT(current, pointers);
         LIST_REMOVE(current, pointers);
+        UA_free(current->work);
         UA_free(current);
     }
 }
@@ -310,7 +339,7 @@ struct DelayedWork {
 };
 
 // Dispatched as a methodcall-WorkItem when the delayedwork is added
-static void getCounters(UA_Server *server, DelayedWork *delayed) {
+static void getCounters(UA_Server *server, struct DelayedWork *delayed) {
     UA_UInt32 *counters = UA_malloc(server->nThreads * sizeof(UA_UInt32));
     for(UA_UInt16 i = 0;i<server->nThreads;i++)
         counters[i] = *server->workerCounters[i];
@@ -323,7 +352,7 @@ static void getCounters(UA_Server *server, DelayedWork *delayed) {
 static void addDelayedWork(UA_Server *server, UA_WorkItem work) {
     struct DelayedWork *dw = server->delayedWork;
     if(!dw || dw->workItemsCount >= DELAYEDWORKSIZE) {
-        struct DelayedWork *newwork = UA_malloc(sizeof(DelayedWork));
+        struct DelayedWork *newwork = UA_malloc(sizeof(struct DelayedWork));
         newwork->workItems = UA_malloc(sizeof(UA_WorkItem)*DELAYEDWORKSIZE);
         newwork->workItemsCount = 0;
         newwork->workerCounters = UA_NULL;
@@ -407,24 +436,24 @@ static void dispatchDelayedWork(UA_Server *server, void *data /* not used, but n
 /* Main Server Loop */
 /********************/
 
-UA_StatusCode UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running) {
+UA_StatusCode UA_Server_run_startup(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running){
 #ifdef UA_MULTITHREADING
     // 1) Prepare the threads
     server->running = running; // the threads need to access the variable
     server->nThreads = nThreads;
     pthread_cond_init(&server->dispatchQueue_condition, 0);
-    pthread_t *thr = UA_malloc(nThreads * sizeof(pthread_t));
+    server->thr = UA_malloc(nThreads * sizeof(pthread_t));
     server->workerCounters = UA_malloc(nThreads * sizeof(UA_UInt32 *));
     for(UA_UInt32 i=0;i<nThreads;i++) {
         struct workerStartData *startData = UA_malloc(sizeof(struct workerStartData));
         startData->server = server;
         startData->workerCounter = &server->workerCounters[i];
-        pthread_create(&thr[i], UA_NULL, (void* (*)(void*))workerLoop, startData);
+        pthread_create(&server->thr[i], UA_NULL, (void* (*)(void*))workerLoop, startData);
     }
 
     UA_WorkItem processDelayed = {.type = UA_WORKITEMTYPE_METHODCALL,
-                                  .work.methodCall = {.method = dispatchDelayedWork,
-                                                      .data = UA_NULL} };
+            .work.methodCall = {.method = dispatchDelayedWork,
+                    .data = UA_NULL} };
     UA_Server_addRepeatedWorkItem(server, &processDelayed, 10000000, UA_NULL);
 #endif
 
@@ -432,60 +461,77 @@ UA_StatusCode UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *r
     for(size_t i = 0; i <server->networkLayersSize; i++)
         server->networkLayers[i].start(server->networkLayers[i].nlHandle, &server->logger);
 
-    //3) The loop
-    while(1) {
-        // 3.1) Process timed work
-        UA_UInt16 timeout = processTimedWork(server);
-
-        // 3.2) Get work from the networklayer and dispatch it
-        for(size_t i = 0; i < server->networkLayersSize; i++) {
-            UA_ServerNetworkLayer *nl = &server->networkLayers[i];
-            UA_WorkItem *work;
-            UA_Int32 workSize;
-            if(*running) {
-            	if(i == server->networkLayersSize-1)
-            		workSize = nl->getWork(nl->nlHandle, &work, timeout);
-            	else
-            		workSize = nl->getWork(nl->nlHandle, &work, 0);
-            } else {
-                workSize = server->networkLayers[i].stop(nl->nlHandle, &work);
-            }
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_Server_run_getAndProcessWork(UA_Server *server, UA_Boolean *running){
+    // 3.1) Process timed work
+    UA_UInt16 timeout = processTimedWork(server);
+
+    // 3.2) Get work from the networklayer and dispatch it
+    for(size_t i = 0; i < server->networkLayersSize; i++) {
+        UA_ServerNetworkLayer *nl = &server->networkLayers[i];
+        UA_WorkItem *work;
+        UA_Int32 workSize;
+        if(*running) {
+            if(i == server->networkLayersSize-1)
+                workSize = nl->getWork(nl->nlHandle, &work, timeout);
+            else
+                workSize = nl->getWork(nl->nlHandle, &work, 0);
+        } else {
+            workSize = server->networkLayers[i].stop(nl->nlHandle, &work);
+        }
 
 #ifdef UA_MULTITHREADING
-            // Filter out delayed work
-            for(UA_Int32 k=0;k<workSize;k++) {
-                if(work[k].type != UA_WORKITEMTYPE_DELAYEDMETHODCALL)
-                    continue;
-                addDelayedWork(server, work[k]);
-                work[k].type = UA_WORKITEMTYPE_NOTHING;
-            }
-            dispatchWork(server, workSize, work);
-            if(workSize > 0)
-                pthread_cond_broadcast(&server->dispatchQueue_condition); 
+// Filter out delayed work
+for(UA_Int32 k=0;k<workSize;k++) {
+    if(work[k].type != UA_WORKITEMTYPE_DELAYEDMETHODCALL)
+        continue;
+    addDelayedWork(server, work[k]);
+    work[k].type = UA_WORKITEMTYPE_NOTHING;
+}
+dispatchWork(server, workSize, work);
+if(workSize > 0)
+    pthread_cond_broadcast(&server->dispatchQueue_condition);
 #else
-            processWork(server, work, workSize);
-            if(workSize > 0)
-                UA_free(work);
+processWork(server, work, workSize);
+if(workSize > 0)
+    UA_free(work);
 #endif
-        }
-
-        // 3.3) Exit?
-        if(!*running)
-            break;
     }
+    return UA_STATUSCODE_GOOD;
+}
 
+UA_StatusCode UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads){
 #ifdef UA_MULTITHREADING
     // 4) Clean up: Wait until all worker threads finish, then empty the
     // dispatch queue, then process the remaining delayed work
     for(UA_UInt32 i=0;i<nThreads;i++) {
-        pthread_join(thr[i], UA_NULL);
+        pthread_join(server->thr[i], UA_NULL);
         UA_free(server->workerCounters[i]);
     }
     UA_free(server->workerCounters);
-    UA_free(thr);
+    UA_free(server->thr);
     emptyDispatchQueue(server);
     processDelayedWork(server);
 #endif
 
     return UA_STATUSCODE_GOOD;
 }
+
+UA_StatusCode UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running) {
+    UA_Server_run_startup(server, nThreads, running);
+
+    // 3) The loop
+    while(1) {
+        UA_Server_run_getAndProcessWork(server, running);
+
+        // 3.3) Exit?
+        if(!*running)
+            break;
+    }
+
+    UA_Server_run_shutdown(server, nThreads);
+
+    return UA_STATUSCODE_GOOD;
+}

+ 44 - 36
src/server/ua_services_attribute.c

@@ -5,9 +5,7 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
-static UA_StatusCode
-parse_numericrange(const UA_String str, UA_NumericRange *range)
-{
+static UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
     if(str.length < 0 || str.length >= 1023)
         return UA_STATUSCODE_BADINTERNALERROR;
     char *cstring = UA_alloca(str.length+1);
@@ -15,7 +13,7 @@ parse_numericrange(const UA_String str, UA_NumericRange *range)
     cstring[str.length] = 0;
     UA_Int32 index = 0;
     size_t dimensionsIndex = 0;
-    size_t dimensionsMax = 3; // more should be uncommon
+    size_t dimensionsMax = 3; // more should be uncommon, realloc if necessary
     struct UA_NumericRangeDimension *dimensions = UA_malloc(sizeof(struct UA_NumericRangeDimension) * 3);
     if(!dimensions)
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -92,10 +90,11 @@ static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValu
 /** Reads a single attribute from a node in the nodestore. */
 static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                       const UA_ReadValueId *id, UA_DataValue *v) {
-
+    UA_String binEncoding = UA_STRING("DefaultBinary");
+    UA_String xmlEncoding = UA_STRING("DefaultXml");
 	if(id->dataEncoding.name.length >= 0){
-		if(memcmp(id->dataEncoding.name.data, "DefaultBinary", 13) != 0 &&
-           memcmp(id->dataEncoding.name.data, "DefaultXml", 10) != 0) {
+		if(!UA_String_equal(&binEncoding, &id->dataEncoding.name) &&
+           !UA_String_equal(&xmlEncoding, &id->dataEncoding.name)) {
 			v->hasStatus = UA_TRUE;
 			v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
 			return;
@@ -216,11 +215,14 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                 UA_DataValue_init(&val);
                 UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
                                               timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val);
+                if(hasRange)
+                    retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &range, &val);
+                else
+                    retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, UA_NULL, &val);
                 if(retval == UA_STATUSCODE_GOOD) {
-                    retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges
+                    retval |= UA_DataValue_copy(&val, v); // todo: still too much copying necessary!!
+                    vn->value.dataSource.release(vn->value.dataSource.handle, &val);
                 }
-                vn->value.dataSource.release(vn->value.dataSource.handle, &val);
             }
 
             if(hasRange)
@@ -237,7 +239,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
         else {
             UA_DataValue val;
             UA_DataValue_init(&val);
-            retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
+            retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, UA_NULL, &val);
             if(retval != UA_STATUSCODE_GOOD)
                 break;
             retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
@@ -268,7 +270,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
             } else {
                 UA_DataValue val;
                 UA_DataValue_init(&val);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
+                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, UA_NULL, &val);
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
                 if(!val.hasValue)
@@ -367,7 +369,7 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         return;
     }
 
-    /* ### Begin External Namespaces */
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
@@ -386,10 +388,12 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
         ens->readNodes(ens->ensHandle, &request->requestHeader, request->nodesToRead,
                        indices, indexSize, response->results, UA_FALSE, response->diagnosticInfos);
     }
-    /* ### End External Namespaces */
+#endif
 
     for(size_t i = 0;i < size;i++) {
+#ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
+#endif
             readValue(server, request->timestampsToReturn, &request->nodesToRead[i], &response->results[i]);
     }
 
@@ -477,12 +481,15 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
             if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
                 if(!vn->value.dataSource.write) {
                     retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-                    break;
+                    goto clean_up_range;
                 }
                 // todo: writing ranges
-                retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value);
+                if(hasRange)
+                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, &range);
+                else
+                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, UA_NULL);
                 done = UA_TRUE;
-                break;
+                goto clean_up_range;
             }
             const UA_Variant *oldV = &vn->value.variant;
 
@@ -493,10 +500,9 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                     /* An enum was sent as an int32, or an opaque type as a bytestring. This is
                        detected with the typeIndex indicated the "true" datatype. */
                     wvalue->value.value.type = oldV->type;
-                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] &&
-                        (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ &&
+                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
                         wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-                        wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) {
+                        UA_Variant_isScalar(&wvalue->value.value)) {
                     /* a string is written to a byte array */
                     UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
                     wvalue->value.value.arrayLength = str->length;
@@ -505,7 +511,7 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                     UA_free(str);
                 } else {
                     retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                    break;
+                    goto clean_up_range;
                 }
             }
 
@@ -514,34 +520,34 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                 UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new();
             if(!newVn) {
                 retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                break;
+                goto clean_up_range;
             }
             retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : 
                 UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn);
             if(retval != UA_STATUSCODE_GOOD)
                 goto clean_up;
                 
-            /* insert the new value*/
+            /* insert the new value */
             if(hasRange)
-                retval = UA_Variant_setRange(&newVn->value.variant, wvalue->value.value.data, range);
+                retval = UA_Variant_setRangeCopy(&newVn->value.variant, wvalue->value.value.data,
+                                                 wvalue->value.value.arrayLength, range);
             else {
                 UA_Variant_deleteMembers(&newVn->value.variant);
                 retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
             }
-            if(retval != UA_STATUSCODE_GOOD ||
-               UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn,
-                                    UA_NULL) != UA_STATUSCODE_GOOD)
-                goto clean_up;
-            if(hasRange)
-                UA_free(range.dimensions);
-            done = UA_TRUE;
-            break;
+
+            if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node,
+                                                   (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
+                done = UA_TRUE;
+                goto clean_up_range;
+            }
 
             clean_up:
             if(node->nodeClass == UA_NODECLASS_VARIABLE)
                 UA_VariableNode_delete(newVn);
             else
                 UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn);
+            clean_up_range:
             if(hasRange)
                 UA_free(range.dimensions);
             }
@@ -570,8 +576,8 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
     return retval;
 }
 
-void Service_Write(UA_Server *server, UA_Session *session,
-                   const UA_WriteRequest *request, UA_WriteResponse *response) {
+void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
+                   UA_WriteResponse *response) {
     UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
 
     if(request->nodesToWriteSize <= 0){
@@ -585,7 +591,7 @@ void Service_Write(UA_Server *server, UA_Session *session,
         return;
     }
 
-    /* ### Begin External Namespaces */
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
@@ -605,11 +611,13 @@ void Service_Write(UA_Server *server, UA_Session *session,
         ens->writeNodes(ens->ensHandle, &request->requestHeader, request->nodesToWrite,
                         indices, indexSize, response->results, response->diagnosticInfos);
     }
-    /* ### End External Namespaces */
+#endif
     
     response->resultsSize = request->nodesToWriteSize;
     for(UA_Int32 i = 0;i < request->nodesToWriteSize;i++) {
+#ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
+#endif
             response->results[i] = writeValue(server, &request->nodesToWrite[i]);
     }
 }

+ 1 - 6
src/server/ua_services_discovery.c

@@ -13,8 +13,6 @@ void Service_FindServers(UA_Server *server, const UA_FindServersRequest *request
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    UA_String_deleteMembers(response->servers->discoveryUrls);
-    UA_String_copy(&request->endpointUrl, response->servers->discoveryUrls);
 	response->serversSize = 1;
 }
 
@@ -54,10 +52,7 @@ void Service_GetEndpoints(UA_Server *server, const UA_GetEndpointsRequest *reque
     for(UA_Int32 j = 0; j < server->endpointDescriptionsSize && retval == UA_STATUSCODE_GOOD; j++) {
         if(relevant_endpoints[j] != UA_TRUE)
             continue;
-        retval = UA_copy(&server->endpointDescriptions[j], &response->endpoints[j],
-                         &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
-        UA_String_deleteMembers(&response->endpoints[j].endpointUrl);
-        UA_String_copy(&request->endpointUrl, &response->endpoints[j].endpointUrl);
+        retval = UA_EndpointDescription_copy(&server->endpointDescriptions[j], &response->endpoints[k]);
         k++;
     }
 

+ 13 - 8
src/server/ua_services_nodemanagement.c

@@ -156,6 +156,7 @@ static UA_StatusCode parseObjectTypeNode(UA_ExtensionObject *attributes, UA_Node
 static UA_StatusCode parseViewNode(UA_ExtensionObject *attributes, UA_Node **new_node) {
     UA_ViewAttributes attr;
     size_t pos = 0;
+
     // todo return more informative error codes from decodeBinary
     if(UA_ViewAttributes_decodeBinary(&attributes->body, &pos, &attr) != UA_STATUSCODE_GOOD)
         return UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
@@ -164,6 +165,7 @@ static UA_StatusCode parseViewNode(UA_ExtensionObject *attributes, UA_Node **new
         UA_ViewAttributes_deleteMembers(&attr);
         return UA_STATUSCODE_BADOUTOFMEMORY;
     }
+
     // now copy all the attributes. This potentially removes them from the decoded attributes.
     COPY_STANDARDATTRIBUTES;
     if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_CONTAINSNOLOOPS)
@@ -207,8 +209,8 @@ static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_Add
         return;
 
     // add the node
-    *result = UA_Server_addNodeWithSession(server, session, node, &item->parentNodeId,
-                                           &item->referenceTypeId);
+    *result = UA_Server_addNodeWithSession(server, session, node, item->parentNodeId,
+                                           item->referenceTypeId);
     if(result->statusCode != UA_STATUSCODE_GOOD) {
         switch (node->nodeClass) {
         case UA_NODECLASS_OBJECT:
@@ -243,7 +245,7 @@ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesR
         return;
     }
 
-    /* ### Begin External Namespaces */
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
@@ -263,11 +265,13 @@ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesR
         ens->addNodes(ens->ensHandle, &request->requestHeader, request->nodesToAdd,
                       indices, indexSize, response->results, response->diagnosticInfos);
     }
-    /* ### End External Namespaces */
+#endif
     
     response->resultsSize = size;
     for(size_t i = 0;i < size;i++) {
+#ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
+#endif
             addNodeFromAttributes(server, session, &request->nodesToAdd[i], &response->results[i]);
     }
 }
@@ -287,7 +291,7 @@ void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddR
 	response->resultsSize = size;
 	UA_memset(response->results, UA_STATUSCODE_GOOD, sizeof(UA_StatusCode) * size);
 
-	/* ### Begin External Namespaces */
+#ifdef UA_EXTERNAL_NAMESPACES
 	UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
 	UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
 	UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
@@ -307,11 +311,13 @@ void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddR
 		ens->addReferences(ens->ensHandle, &request->requestHeader, request->referencesToAdd,
                            indices, indicesSize, response->results, response->diagnosticInfos);
 	}
-	/* ### End External Namespaces */
+#endif
 
 	response->resultsSize = size;
 	for(UA_Int32 i = 0; i < response->resultsSize; i++) {
+#ifdef UA_EXTERNAL_NAMESPACES
 		if(!isExternal[i])
+#endif
 			UA_Server_addReference(server, &request->referencesToAdd[i]);
 	}
 }
@@ -321,8 +327,7 @@ void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_Delete
 
 }
 
-void Service_DeleteReferences(UA_Server *server, UA_Session *session,
-                              const UA_DeleteReferencesRequest *request,
+void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response) {
 
 }

+ 58 - 91
src/server/ua_services_session.c

@@ -7,25 +7,19 @@
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
                            const UA_CreateSessionRequest *request,
                            UA_CreateSessionResponse *response) {
-
-    response->serverEndpoints = UA_malloc(sizeof(UA_EndpointDescription));
-    if(!response->serverEndpoints || (response->responseHeader.serviceResult =
-        UA_EndpointDescription_copy(server->endpointDescriptions, response->serverEndpoints)) !=
-       UA_STATUSCODE_GOOD)
+    response->responseHeader.serviceResult =
+        UA_Array_copy(server->endpointDescriptions, (void**)&response->serverEndpoints,
+                      &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION], server->endpointDescriptionsSize);
+    if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
         return;
-    response->serverEndpointsSize = 1;
+    response->serverEndpointsSize = server->endpointDescriptionsSize;
 
-    // creates a session and adds a pointer to the channel. Only when the
-    // session is activated will the channel point to the session as well
 	UA_Session *newSession;
     response->responseHeader.serviceResult = UA_SessionManager_createSession(&server->sessionManager,
                                                                              channel, request, &newSession);
 	if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
 		return;
 
-	//bind session to channel
-	channel->session = newSession;
-
     //TODO get maxResponseMessageSize internally
     newSession->maxResponseMessageSize = request->maxResponseMessageSize;
     response->sessionId = newSession->sessionId;
@@ -36,34 +30,24 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
         response->responseHeader.serviceResult |=
             UA_ByteString_copy(&server->endpointDescriptions->serverCertificate, &response->serverCertificate);
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
-        UA_SessionManager_removeSession(&server->sessionManager, &newSession->sessionId);
+        UA_SessionManager_removeSession(&server->sessionManager, &newSession->authenticationToken);
          return;
     }
 }
 
-#ifdef RETURN
-#undef RETURN
-#endif
-#define RETURN  UA_UserIdentityToken_deleteMembers(&token); \
-                UA_UserNameIdentityToken_deleteMembers(&username_token); \
-                return
-void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
+void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
                              const UA_ActivateSessionRequest *request,
                              UA_ActivateSessionResponse *response) {
     // make the channel know about the session
-	UA_Session *foundSession;
-	UA_SessionManager_getSessionByToken(&server->sessionManager,
-                                        (const UA_NodeId*)&request->requestHeader.authenticationToken,
-                                        &foundSession);
+	UA_Session *foundSession =
+        UA_SessionManager_getSession(&server->sessionManager,
+                                     (const UA_NodeId*)&request->requestHeader.authenticationToken);
 
-	if(foundSession == UA_NULL){
+	if(foundSession == UA_NULL) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
         return;
-	}
-
-	if(foundSession->validTill < UA_DateTime_now()){
+	} else if(foundSession->validTill < UA_DateTime_now()) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
-        //TODO: maybe delete session? or wait for a recurring cleanup?
         return;
 	}
 
@@ -75,80 +59,63 @@ void Service_ActivateSession(UA_Server *server,UA_SecureChannel *channel,
     UA_UserNameIdentityToken username_token;
     UA_UserNameIdentityToken_init(&username_token);
 
-    //check policies
-
-    if(token.policyId.data == UA_NULL){ //user identity token is NULL
+    if(token.policyId.data == UA_NULL) {
+        /* 1) no policy defined */
         response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-        //todo cleanup session
-        RETURN;
-    }
-
-    //anonymous logins
-    if(server->config.Login_enableAnonymous && UA_String_equalchars(&token.policyId, ANONYMOUS_POLICY)){
-        //success - activate
-        channel->session = foundSession;
-        channel->session->activated = UA_TRUE;
-        //TODO: not sure if we have to do this, tests seem to work
-        //if(foundSession->channel) //in case session is being rebound
-        //    foundSession->channel->session = UA_NULL;
-        foundSession->channel=channel;
-        RETURN;
-    //username logins
-    }else if(server->config.Login_enableUsernamePassword && UA_String_equalchars(&token.policyId, USERNAME_POLICY)){
+    } else if(server->config.Login_enableAnonymous &&
+              UA_String_equalchars(&token.policyId, ANONYMOUS_POLICY)) {
+        /* 2) anonymous logins */
+        if(foundSession->channel && foundSession->channel != channel)
+            UA_SecureChannel_detachSession(foundSession->channel, foundSession);
+        UA_SecureChannel_attachSession(channel, foundSession);
+        foundSession->activated = UA_TRUE;
+        UA_Session_updateLifetime(foundSession);
+    } else if(server->config.Login_enableUsernamePassword &&
+              UA_String_equalchars(&token.policyId, USERNAME_POLICY)) {
+        /* 3) username logins */
         offset = 0;
         UA_UserNameIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &username_token);
-        if(username_token.encryptionAlgorithm.data != UA_NULL){
-            //we only support encryption
+        if(username_token.encryptionAlgorithm.data != UA_NULL) {
+            /* 3.1) we only support encryption */
             response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            //todo cleanup session
-            RETURN;
-        }
-        if(username_token.userName.length == -1 && username_token.password.length == -1){
-            //empty username and password
+        } else  if(username_token.userName.length == -1 && username_token.password.length == -1){
+            /* 3.2) empty username and password */
             response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-            //todo cleanup session
-            RETURN;
-        }
-        for(UA_UInt32 i=0;i<server->config.Login_loginsCount;++i){
-            if(UA_String_equalchars(&username_token.userName, server->config.Login_usernames[i])
-            && UA_String_equalchars(&username_token.password, server->config.Login_passwords[i])){
-                //success - activate
-                channel->session = foundSession;
-                channel->session->activated = UA_TRUE;
-                //TODO: not sure if we have to do this, tests seem to work
-                //if(foundSession->channel) //in case session is being rebound
-                //    foundSession->channel->session = UA_NULL;
-                foundSession->channel=channel;
-                RETURN;
+        } else {
+            /* 3.3) ok, trying to match the username */
+            UA_UInt32 i = 0;
+            for(; i < server->config.Login_loginsCount; ++i) {
+                if(UA_String_equalchars(&username_token.userName, server->config.Login_usernames[i])
+                    && UA_String_equalchars(&username_token.password, server->config.Login_passwords[i])) {
+                    /* success - activate */
+                    if(foundSession->channel && foundSession->channel != channel)
+                        UA_SecureChannel_detachSession(foundSession->channel, foundSession);
+                    UA_SecureChannel_attachSession(channel, foundSession);
+                    foundSession->activated = UA_TRUE;
+                    UA_Session_updateLifetime(foundSession);
+                    break;
+                }
             }
+            /* no username/pass matched */
+            if(i >= server->config.Login_loginsCount)
+                response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
         }
-        //no username/pass matched
-       response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
-       //todo cleanup session
-       RETURN;
+    } else {
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
     }
-
-    //default case - no login
-    response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    //todo cleanup session
-    RETURN;
-
-
-
+    UA_UserIdentityToken_deleteMembers(&token);
+    UA_UserNameIdentityToken_deleteMembers(&username_token);
+    return;
 }
-#undef RETURN
 
 void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_CloseSessionRequest *request,
                           UA_CloseSessionResponse *response) {
-	UA_Session *foundSession;
-	UA_SessionManager_getSessionByToken(&server->sessionManager,
-			(const UA_NodeId*)&request->requestHeader.authenticationToken, &foundSession);
-
-	if(foundSession == UA_NULL){
+	UA_Session *foundSession =
+        UA_SessionManager_getSession(&server->sessionManager,
+		                             (const UA_NodeId*)&request->requestHeader.authenticationToken);
+	if(foundSession == UA_NULL)
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
-		return;
-	}
-
-	response->responseHeader.serviceResult =
-        UA_SessionManager_removeSession(&server->sessionManager, &session->sessionId);
+	else 
+        response->responseHeader.serviceResult =
+            UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken);
 }

+ 145 - 108
src/server/ua_services_view.c

@@ -75,21 +75,21 @@ is_relevant: ;
  * 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)
-{
-    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;
-
+                                  size_t *reftypes_count) {
     const UA_Node *node = UA_NodeStore_get(ns, root);
-    if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)  {
+    if(!node)
+        return UA_STATUSCODE_BADNOMATCH;
+    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)  {
         UA_NodeStore_release(node);
-        UA_free(results);
         return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
     }
     UA_NodeStore_release(node);
 
+    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);
@@ -136,21 +136,38 @@ static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_No
     return UA_STATUSCODE_GOOD;
 }
 
-/* Results for a single browsedescription. Call this either with an existing continuationpoint from
-   which we take the entire context for the search. Or, call with a browsedescription and maxrefs
-   value. If we need to create a new continuationpoint, it will be alloced and the new pointer
-   stored in *cp.
+static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session){
+    session->availableContinuationPoints++;
+    UA_ByteString_deleteMembers(&cp->identifier);
+    UA_BrowseDescription_deleteMembers(&cp->browseDescription);
+    LIST_REMOVE(cp, pointers);
+    UA_free(cp);
+}
+
+/**
+ * Results for a single browsedescription. This is the inner loop for both Browse and BrowseNext
+ * @param session Session to save continuationpoints
+ * @param ns The nodstore where the to-be-browsed node can be found
+ * @param cp If cp points to a continuationpoint, we continue from there.
+ *           If cp is null, we can add a new continuation point if possible and necessary.
+ * @param descr If no cp is set, we take the browsedescription from there
+ * @param maxrefs The maximum number of references the client has requested
+ * @param result The entry in the request
  */
-static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const UA_BrowseDescription *descr, 
-                   UA_UInt32 maxrefs, UA_BrowseResult *result)
-{
+static void browse(UA_Session *session, UA_NodeStore *ns, struct ContinuationPointEntry *cp,
+                   const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) {
     UA_UInt32 continuationIndex = 0;
-    if(*cp) {
-        descr = &(*cp)->browseDescription;
-        maxrefs = (*cp)->maxReferences;
-        continuationIndex = (*cp)->continuationIndex;
+    size_t referencesCount = 0;
+    UA_Int32 referencesIndex = 0;
+    
+    /* set the browsedescription if a cp is given */
+    if(cp) {
+        descr = &cp->browseDescription;
+        maxrefs = cp->maxReferences;
+        continuationIndex = cp->continuationIndex;
     }
 
+    /* is the browsedirection valid? */
     if(descr->browseDirection != UA_BROWSEDIRECTION_BOTH &&
        descr->browseDirection != UA_BROWSEDIRECTION_FORWARD &&
        descr->browseDirection != UA_BROWSEDIRECTION_INVERSE) {
@@ -158,30 +175,32 @@ static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const U
         return;
     }
     
+    /* get the references that match the browsedescription */
     size_t relevant_refs_size = 0;
     UA_NodeId *relevant_refs = UA_NULL;
-    // what are the relevant references?
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
-
-    //check if reference type id exists
-    if(!all_refs && UA_NodeStore_get(ns, &descr->referenceTypeId) == UA_NULL) {
-        result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-        return;
-    }
-
     if(!all_refs) {
         if(descr->includeSubtypes) {
-            result->statusCode = findsubtypes(ns, &descr->referenceTypeId, &relevant_refs,
-                                              &relevant_refs_size);
+            result->statusCode = findsubtypes(ns, &descr->referenceTypeId, &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
+            const UA_Node *rootRef = UA_NodeStore_get(ns, &descr->referenceTypeId);
+            if(!rootRef) {
+                result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+                return;
+            } else if(rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+                UA_NodeStore_release(rootRef);
+                result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+                return;
+            }
+            UA_NodeStore_release(rootRef);
             relevant_refs = (UA_NodeId*)(uintptr_t)&descr->referenceTypeId;
             relevant_refs_size = 1;
         }
     }
 
-    // get the node
+    /* get the node */
     const UA_Node *node = UA_NodeStore_get(ns, &descr->nodeId);
     if(!node) {
         result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
@@ -190,11 +209,16 @@ static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const U
         return;
     }
 
+    /* if the node has no references, just return */
     if(node->referencesSize <= 0) {
         result->referencesSize = 0;
-        goto cleanup;
+        UA_NodeStore_release(node);
+        if(!all_refs && descr->includeSubtypes)
+            UA_Array_delete(relevant_refs, &UA_TYPES[UA_TYPES_NODEID], relevant_refs_size);
+        return;
     }
 
+    /* how many references can we return at most? */
     UA_UInt32 real_maxrefs = maxrefs;
     if(real_maxrefs == 0)
         real_maxrefs = node->referencesSize;
@@ -204,10 +228,10 @@ static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const U
         goto cleanup;
     }
 
-    size_t count = 0;
+    /* loop over the node's references */
     size_t skipped = 0;
-    for(UA_Int32 i = 0; i < node->referencesSize && count < real_maxrefs; i++) {
-        const UA_Node *current = relevant_node(ns, descr, all_refs, &node->references[i],
+    for(; referencesIndex < node->referencesSize && referencesCount < real_maxrefs; referencesIndex++) {
+        const UA_Node *current = relevant_node(ns, descr, all_refs, &node->references[referencesIndex],
                                                relevant_refs, relevant_refs_size);
         if(!current)
             continue;
@@ -216,53 +240,62 @@ static void browse(UA_NodeStore *ns, struct ContinuationPointEntry **cp, const U
             skipped++;
             continue;
         }
-        UA_StatusCode retval = fillrefdescr(ns, current, &node->references[i], descr->resultMask,
-                                            &result->references[count]);
+        UA_StatusCode retval = fillrefdescr(ns, current, &node->references[referencesIndex], descr->resultMask,
+                                            &result->references[referencesCount]);
         UA_NodeStore_release(current);
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], count);
+            UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], referencesCount);
             result->references = UA_NULL;
-            count = 0;
+            result->referencesSize = 0;
             result->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
             goto cleanup;
         }
-        count++;
+        referencesCount++;
     }
 
-    if(*cp) {
-        (*cp)->continuationIndex += count;
-        if((*cp)->continuationIndex == node->referencesSize) {
-            /* remove a finished continuationPoint */
-            UA_ByteString_deleteMembers(&(*cp)->identifier);
-            UA_BrowseDescription_deleteMembers(&(*cp)->browseDescription);
-            LIST_REMOVE(*cp, pointers);
-            UA_free(*cp);
-            *cp = UA_NULL;
-        }
-    } else if(maxrefs != 0 && count >= maxrefs) {
-        /* create a continuationPoint */
-        *cp = UA_malloc(sizeof(struct ContinuationPointEntry));
-        UA_BrowseDescription_copy(descr,&(*cp)->browseDescription);
-        (*cp)->maxReferences = maxrefs;
-        (*cp)->continuationIndex = count;
-        UA_Guid *ident = UA_Guid_new();
-        UA_UInt32 seed = (uintptr_t)*cp;
-        *ident = UA_Guid_random(&seed);
-        (*cp)->identifier.data = (UA_Byte*)ident;
-        (*cp)->identifier.length = sizeof(UA_Guid);
-    }
-    
-    if(count > 0)
-        result->referencesSize = count;
-    else {
+    result->referencesSize = referencesCount;
+    if(referencesCount == 0) {
         UA_free(result->references);
         result->references = UA_NULL;
     }
 
-cleanup:
+    cleanup:
     UA_NodeStore_release(node);
     if(!all_refs && descr->includeSubtypes)
         UA_Array_delete(relevant_refs, &UA_TYPES[UA_TYPES_NODEID], relevant_refs_size);
+    if(result->statusCode != UA_STATUSCODE_GOOD)
+        return;
+
+    /* create, update, delete continuation points */
+    if(cp) {
+        if(referencesIndex == node->referencesSize) {
+            /* all done, remove a finished continuationPoint */
+            removeCp(cp, session);
+        } else {
+            /* update the cp and return the cp identifier */
+            cp->continuationIndex += referencesCount;
+            UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
+        }
+    } else if(maxrefs != 0 && referencesCount >= maxrefs) {
+        /* create a cp */
+        if(session->availableContinuationPoints <= 0 || !(cp = UA_malloc(sizeof(struct ContinuationPointEntry)))) {
+            result->statusCode = UA_STATUSCODE_BADNOCONTINUATIONPOINTS;
+            return;
+        }
+        UA_BrowseDescription_copy(descr, &cp->browseDescription);
+        cp->maxReferences = maxrefs;
+        cp->continuationIndex = referencesCount;
+        UA_Guid *ident = UA_Guid_new();
+        UA_UInt32 seed = (uintptr_t)cp;
+        *ident = UA_Guid_random(&seed);
+        cp->identifier.data = (UA_Byte*)ident;
+        cp->identifier.length = sizeof(UA_Guid);
+        UA_ByteString_copy(&cp->identifier, &result->continuationPoint);
+
+        /* store the cp */
+        LIST_INSERT_HEAD(&session->continuationPoints, cp, pointers);
+        session->availableContinuationPoints--;
+    }
 }
 
 void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
@@ -272,25 +305,26 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
         return;
     }
     
-   if(request->nodesToBrowseSize <= 0) {
+    if(request->nodesToBrowseSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
-   size_t size = request->nodesToBrowseSize;
 
-   response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
+    size_t size = request->nodesToBrowseSize;
+    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-
-    /* ### Begin External Namespaces */
+    response->resultsSize = size;
+    
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
     for(size_t j = 0; j < server->externalNamespacesSize; j++) {
         size_t indexSize = 0;
-        for(size_t i = 0;i < size;i++) {
+        for(size_t i = 0; i < size; i++) {
             if(request->nodesToBrowse[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
                 continue;
             isExternal[i] = UA_TRUE;
@@ -303,19 +337,14 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
         ens->browseNodes(ens->ensHandle, &request->requestHeader, request->nodesToBrowse, indices, indexSize,
                          request->requestedMaxReferencesPerNode, response->results, response->diagnosticInfos);
     }
-    /* ### End External Namespaces */
+#endif
 
-    response->resultsSize = size;
     for(size_t i = 0; i < size; i++) {
-        if(!isExternal[i]) {
-            struct ContinuationPointEntry *cp = UA_NULL;
-            browse(server->nodestore, &cp, &request->nodesToBrowse[i],
+#ifdef UA_EXTERNAL_NAMESPACES
+        if(!isExternal[i])
+#endif
+            browse(session, server->nodestore, UA_NULL, &request->nodesToBrowse[i],
                    request->requestedMaxReferencesPerNode, &response->results[i]);
-            if(cp) {
-                LIST_INSERT_HEAD(&session->continuationPoints, cp, pointers);
-                UA_ByteString_copy(&cp->identifier, &response->results[i].continuationPoint);
-            }
-        }
     }
 }
 
@@ -327,6 +356,7 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
    }
    size_t size = request->continuationPointsSize;
    if(!request->releaseContinuationPoints) {
+       /* continue with the cp */
        response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
        if(!response->results) {
            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
@@ -334,31 +364,34 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
        }
        response->resultsSize = size;
        for(size_t i = 0; i < size; i++) {
-           struct ContinuationPointEntry *cp = UA_NULL;
-           struct ContinuationPointEntry *search_cp;
-           LIST_FOREACH(search_cp, &session->continuationPoints, pointers) {
-               if(UA_ByteString_equal(&search_cp->identifier, &request->continuationPoints[i])) {
-                   cp = search_cp;
+           struct ContinuationPointEntry *cp;
+           LIST_FOREACH(cp, &session->continuationPoints, pointers) {
+               if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
+                   browse(session, server->nodestore, cp, UA_NULL, 0, &response->results[i]);
                    break;
                }
            }
            if(!cp)
                response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
-           else
-               browse(server->nodestore, &cp, UA_NULL, 0, &response->results[i]);
        }
    } else {
+       /* remove the cp */
+       response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
+       if(!response->results) {
+           response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
+           return;
+       }
+       response->resultsSize = size;
        for(size_t i = 0; i < size; i++) {
            struct ContinuationPointEntry *cp = UA_NULL;
            LIST_FOREACH(cp, &session->continuationPoints, pointers) {
                if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
-                   UA_ByteString_deleteMembers(&cp->identifier);
-                   UA_BrowseDescription_deleteMembers(&cp->browseDescription);
-                   LIST_REMOVE(cp, pointers);
-                   UA_free(cp);
+                   removeCp(cp, session);
                    break;
                }
            }
+           if(!cp)
+               response->results[i].statusCode = UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
        }
    }
 }
@@ -384,8 +417,11 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
         all_refs = UA_TRUE;
     else if(!elem->includeSubtypes)
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
-    else
+    else {
         retval = findsubtypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+    }
 
     for(UA_Int32 i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
         UA_Boolean match = all_refs;
@@ -493,7 +529,7 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
         return;
     }
 
-    /* ### Begin External Namespaces */
+#ifdef UA_EXTERNAL_NAMESPACES
     UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
@@ -512,11 +548,13 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
     	ens->translateBrowsePathsToNodeIds(ens->ensHandle, &request->requestHeader, request->browsePaths,
     			indices, indexSize, response->results, response->diagnosticInfos);
     }
-    /* ### End External Namespaces */
+#endif
 
     response->resultsSize = size;
-    for(size_t i = 0; i < size; i++){
+    for(size_t i = 0; i < size; i++) {
+#ifdef UA_EXTERNAL_NAMESPACES
     	if(!isExternal[i])
+#endif
     		translateBrowsePath(server, session, &request->browsePaths[i], &response->results[i]);
     }
 }
@@ -525,13 +563,15 @@ void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_Regi
                            UA_RegisterNodesResponse *response) {
 	//TODO: hang the nodeids to the session if really needed
 	response->responseHeader.timestamp = UA_DateTime_now();
-	response->registeredNodeIdsSize = request->nodesToRegisterSize;
-	response->registeredNodeIds = request->nodesToRegister;
-	if(request->nodesToRegisterSize==0)
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-	if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) ||
-       !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken))
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
+    if(request->nodesToRegisterSize <= 0)
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
+    else {
+        response->responseHeader.serviceResult =
+            UA_Array_copy(request->nodesToRegister, (void**)&response->registeredNodeIds, &UA_TYPES[UA_TYPES_NODEID],
+                request->nodesToRegisterSize);
+        if(response->responseHeader.serviceResult == UA_STATUSCODE_GOOD)
+            response->registeredNodeIdsSize = request->nodesToRegisterSize;
+    }
 }
 
 void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_UnregisterNodesRequest *request,
@@ -540,7 +580,4 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_Un
 	response->responseHeader.timestamp = UA_DateTime_now();
 	if(request->nodesToUnregisterSize==0)
 		response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
-	if(UA_NodeId_equal(&request->requestHeader.authenticationToken, &UA_NODEID_NULL) ||
-       !UA_NodeId_equal(&request->requestHeader.authenticationToken, &session->authenticationToken))
-		response->responseHeader.serviceResult = UA_STATUSCODE_BADSESSIONIDINVALID;
 }

+ 38 - 79
src/server/ua_session_manager.c

@@ -2,12 +2,6 @@
 #include "ua_statuscodes.h"
 #include "ua_util.h"
 
-/**
- The functions in this file are not thread-safe. For multi-threaded access, a
- second implementation should be provided. See for example, how a nodestore
- implementation is choosen based on whether multithreading is enabled or not.
- */
-
 UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt32 maxSessionCount,
                                     UA_UInt32 maxSessionLifeTime, UA_UInt32 startSessionId) {
     LIST_INIT(&sessionManager->sessions);
@@ -19,75 +13,44 @@ UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt3
 }
 
 void UA_SessionManager_deleteMembers(UA_SessionManager *sessionManager) {
-    session_list_entry *current, *next = LIST_FIRST(&sessionManager->sessions);
-    while(next) {
-        current = next;
-        next = LIST_NEXT(current, pointers);
+    session_list_entry *current;
+    while((current = LIST_FIRST(&sessionManager->sessions))) {
         LIST_REMOVE(current, pointers);
-        //if(current->session.channel)
-        //    current->session.channel->session = UA_NULL; // the channel is no longer attached to a session
-        UA_Session_deleteMembers(&current->session);
+        UA_Session_deleteMembersCleanup(&current->session);
         UA_free(current);
     }
 }
 
-UA_StatusCode
-UA_SessionManager_getSessionById(UA_SessionManager *sessionManager, const UA_NodeId *sessionId,
-                                 UA_Session **session)
-{
-    if(sessionManager == UA_NULL) {
-        *session = UA_NULL;
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    session_list_entry *current = UA_NULL;
-    LIST_FOREACH(current, &sessionManager->sessions, pointers) {
-        if(UA_NodeId_equal(&current->session.sessionId, sessionId))
-            break;
-    }
-
-    if(!current) {
-        *session = UA_NULL;
-        return UA_STATUSCODE_BADINTERNALERROR;
+void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sessionManager, UA_DateTime now) {
+    session_list_entry *sentry = LIST_FIRST(&sessionManager->sessions);
+    while(sentry) {
+        if(sentry->session.validTill < now) {
+            session_list_entry *next = LIST_NEXT(sentry, pointers);
+            LIST_REMOVE(sentry, pointers);
+            UA_Session_deleteMembersCleanup(&sentry->session);
+            UA_free(sentry);
+            sessionManager->currentSessionCount--;
+            sentry = next;
+        } else {
+            sentry = LIST_NEXT(sentry, pointers);
+        }
     }
-
-    // Lifetime handling is not done here, but in a regular cleanup by the
-    // server. If the session still exists, then it is valid.
-    *session = &current->session;
-    return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode
-UA_SessionManager_getSessionByToken(UA_SessionManager *sessionManager, const UA_NodeId *token,
-                                    UA_Session **session)
-{
-    if(sessionManager == UA_NULL) {
-        *session = UA_NULL;
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
+UA_Session * UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token) {
     session_list_entry *current = UA_NULL;
     LIST_FOREACH(current, &sessionManager->sessions, pointers) {
         if(UA_NodeId_equal(&current->session.authenticationToken, token))
             break;
     }
-
-    if(!current) {
-        *session = UA_NULL;
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    // Lifetime handling is not done here, but in a regular cleanup by the
-    // server. If the session still exists, then it is valid.
-    *session = &current->session;
-    return UA_STATUSCODE_GOOD;
+    if(!current || UA_DateTime_now() > current->session.validTill)
+        return UA_NULL;
+    return &current->session;
 }
 
-/** Creates and adds a session. */
-UA_StatusCode
-UA_SessionManager_createSession(UA_SessionManager *sessionManager, UA_SecureChannel *channel,
-                                const UA_CreateSessionRequest *request, UA_Session **session)
-{
+/** Creates and adds a session. But it is not yet attached to a secure channel. */
+UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager, UA_SecureChannel *channel,
+                                              const UA_CreateSessionRequest *request, UA_Session **session) {
     if(sessionManager->currentSessionCount >= sessionManager->maxSessionCount)
         return UA_STATUSCODE_BADTOOMANYSESSIONS;
 
@@ -95,37 +58,33 @@ UA_SessionManager_createSession(UA_SessionManager *sessionManager, UA_SecureChan
     if(!newentry)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
+    sessionManager->currentSessionCount++;
     UA_Session_init(&newentry->session);
     newentry->session.sessionId = UA_NODEID_NUMERIC(1, sessionManager->lastSessionId++);
-    newentry->session.authenticationToken = UA_NODEID_NUMERIC(1, sessionManager->lastSessionId);
-    newentry->session.channel = channel;
-    newentry->session.timeout =
-        (request->requestedSessionTimeout <= sessionManager->maxSessionLifeTime &&
-         request->requestedSessionTimeout>0) ?
-        request->requestedSessionTimeout : sessionManager->maxSessionLifeTime;
-    UA_Session_setExpirationDate(&newentry->session);
-
-    sessionManager->currentSessionCount++;
+    UA_UInt32 randSeed = sessionManager->lastSessionId;
+    newentry->session.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random(&randSeed));
+    if(request->requestedSessionTimeout <= sessionManager->maxSessionLifeTime &&
+       request->requestedSessionTimeout > 0)
+        newentry->session.timeout = request->requestedSessionTimeout;
+    else
+        newentry->session.timeout = sessionManager->maxSessionLifeTime; // todo: remove when the CTT is fixed
+    UA_Session_updateLifetime(&newentry->session);
     LIST_INSERT_HEAD(&sessionManager->sessions, newentry, pointers);
     *session = &newentry->session;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode
-UA_SessionManager_removeSession(UA_SessionManager *sessionManager, const UA_NodeId *sessionId)
-{
-    session_list_entry *current = UA_NULL;
+UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager, const UA_NodeId *token) {
+    session_list_entry *current;
     LIST_FOREACH(current, &sessionManager->sessions, pointers) {
-        if(UA_NodeId_equal(&current->session.sessionId, sessionId))
+        if(UA_NodeId_equal(&current->session.authenticationToken, token))
             break;
     }
-
     if(!current)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
+        return UA_STATUSCODE_BADSESSIONIDINVALID;
     LIST_REMOVE(current, pointers);
-    UA_SecureChannel_detachSession(current->session.channel);
-    UA_Session_deleteMembers(&current->session);
+    UA_Session_deleteMembersCleanup(&current->session);
     UA_free(current);
+    sessionManager->currentSessionCount--;
     return UA_STATUSCODE_GOOD;
 }

+ 8 - 13
src/server/ua_session_manager.h

@@ -7,8 +7,8 @@
 #include "ua_session.h"
 
 typedef struct session_list_entry {
-    UA_Session session;
     LIST_ENTRY(session_list_entry) pointers;
+    UA_Session session;
 } session_list_entry;
 
 typedef struct UA_SessionManager {
@@ -24,20 +24,15 @@ UA_StatusCode UA_SessionManager_init(UA_SessionManager *sessionManager, UA_UInt3
 
 void UA_SessionManager_deleteMembers(UA_SessionManager *sessionManager);
 
-UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
-                                              UA_SecureChannel *channel, const UA_CreateSessionRequest *request, UA_Session **session);
+void UA_SessionManager_cleanupTimedOut(UA_SessionManager *sessionManager, UA_DateTime now);
 
-UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager,
-                                              const UA_NodeId *sessionId);
-
-/** Finds the session which is identified by the sessionId */
-UA_StatusCode UA_SessionManager_getSessionById(UA_SessionManager *sessionManager,
-                                               const UA_NodeId *sessionId, UA_Session **session);
+UA_StatusCode UA_SessionManager_createSession(UA_SessionManager *sessionManager,
+                                              UA_SecureChannel *channel, const UA_CreateSessionRequest *request,
+                                              UA_Session **session);
 
-UA_StatusCode UA_SessionManager_getSessionByToken(UA_SessionManager *sessionManager,
-                                                  const UA_NodeId *token, UA_Session **session);
+UA_StatusCode UA_SessionManager_removeSession(UA_SessionManager *sessionManager, const UA_NodeId *token);
 
-//UA_Int32 UA_SessionManager_updateSessions();
-//UA_Int32 UA_SessionManager_generateToken(UA_Session session, UA_Int32 requestedLifeTime, SecurityTokenRequestType requestType, UA_ChannelSecurityToken* newToken);
+/** Finds the session which is identified by the authentication token */
+UA_Session * UA_SessionManager_getSession(UA_SessionManager *sessionManager, const UA_NodeId *token);
 
 #endif /* UA_SESSION_MANAGER_H_ */

+ 28 - 2
src/ua_connection.c

@@ -1,8 +1,7 @@
 #include "ua_util.h"
 #include "ua_connection.h"
 #include "ua_types_encoding_binary.h"
-
-const char *UA_LoggerCategoryNames[4] = {"communication", "server", "client", "userland"};
+#include "ua_securechannel.h"
 
 // max message size is 64k
 const UA_ConnectionConfig UA_ConnectionConfig_standard =
@@ -20,6 +19,8 @@ void UA_Connection_init(UA_Connection *connection) {
     connection->write = UA_NULL;
     connection->close = UA_NULL;
     connection->recv = UA_NULL;
+    connection->getBuffer = UA_NULL;
+    connection->releaseBuffer = UA_NULL;
 }
 
 void UA_Connection_deleteMembers(UA_Connection *connection) {
@@ -98,3 +99,28 @@ UA_ByteString UA_Connection_completeMessages(UA_Connection *connection, UA_ByteS
     }
     return current;
 }
+
+void UA_Connection_detachSecureChannel(UA_Connection *connection) {
+#ifdef UA_MULTITHREADING
+    UA_SecureChannel *channel = connection->channel;
+    if(channel)
+        uatomic_cmpxchg(&channel->connection, connection, UA_NULL);
+    uatomic_set(&connection->channel, UA_NULL);
+#else
+    if(connection->channel)
+        connection->channel->connection = UA_NULL;
+    connection->channel = UA_NULL;
+#endif
+}
+
+void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
+#ifdef UA_MULTITHREADING
+    if(uatomic_cmpxchg(&channel->connection, UA_NULL, connection) == UA_NULL)
+        uatomic_set(&connection->channel, channel);
+#else
+    if(channel->connection != UA_NULL)
+        return;
+    channel->connection = connection;
+    connection->channel = channel;
+#endif
+}

+ 51 - 21
src/ua_securechannel.c

@@ -1,5 +1,6 @@
 #include "ua_util.h"
 #include "ua_securechannel.h"
+#include "ua_session.h"
 #include "ua_statuscodes.h"
 
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
@@ -12,15 +13,25 @@ void UA_SecureChannel_init(UA_SecureChannel *channel) {
     channel->requestId = 0;
     channel->sequenceNumber = 0;
     channel->connection = UA_NULL;
-    channel->session    = UA_NULL;
+    LIST_INIT(&channel->sessions);
 }
 
-void UA_SecureChannel_deleteMembers(UA_SecureChannel *channel) {
+void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&channel->serverAsymAlgSettings);
     UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&channel->clientAsymAlgSettings);
     UA_ByteString_deleteMembers(&channel->clientNonce);
     UA_ChannelSecurityToken_deleteMembers(&channel->securityToken);
+    UA_Connection *c = channel->connection;
+    if(c) {
+        UA_Connection_detachSecureChannel(c);
+        c->close(c);
+    }
+    /* just remove the pointers and free the linked list (not the sessions) */
+    struct SessionEntry *se;
+    while((se = LIST_FIRST(&channel->sessions))) {
+        UA_SecureChannel_detachSession(channel, se->session); /* the se is deleted inside */
+    }
 }
 
 //TODO implement real nonce generator - DUMMY function
@@ -48,28 +59,47 @@ UA_StatusCode UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, U
     return UA_STATUSCODE_GOOD;
 }
 
-/* these need ua_securechannel.h */
-void UA_Connection_detachSecureChannel(UA_Connection *connection) {
+void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session) {
+    struct SessionEntry *se = UA_malloc(sizeof(struct SessionEntry));
+    if(!se)
+        return;
+    se->session = session;
 #ifdef UA_MULTITHREADING
-    UA_SecureChannel *channel = connection->channel;
-    if(channel)
-        uatomic_cmpxchg(&channel->connection, connection, UA_NULL);
-    uatomic_set(&connection->channel, UA_NULL);
+    if(uatomic_cmpxchg(&session->channel, UA_NULL, channel) != UA_NULL) {
+        UA_free(se);
+        return;
+    }
 #else
-    if(connection->channel)
-        connection->channel->connection = UA_NULL;
-    connection->channel = UA_NULL;
+    if(session->channel != UA_NULL) {
+        UA_free(se);
+        return;
+    }
+    session->channel = channel;
 #endif
+    LIST_INSERT_HEAD(&channel->sessions, se, pointers);
 }
 
-void UA_Connection_attachSecureChannel(UA_Connection *connection, UA_SecureChannel *channel) {
-#ifdef UA_MULTITHREADING
-    if(uatomic_cmpxchg(&channel->connection, UA_NULL, connection) == UA_NULL)
-        uatomic_set(&connection->channel, channel);
-#else
-    if(channel->connection != UA_NULL)
-        return;
-    channel->connection = connection;
-    connection->channel = channel;
-#endif
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel, UA_Session *session) {
+    if(session)
+        session->channel = UA_NULL;
+    struct SessionEntry *se;
+    LIST_FOREACH(se, &channel->sessions, pointers) {
+        if(se->session != session)
+            continue;
+        LIST_REMOVE(se, pointers);
+        UA_free(se);
+        break;
+    }
+}
+
+UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *token) {
+    struct SessionEntry *se;
+    LIST_FOREACH(se, &channel->sessions, pointers) {
+        if(UA_NodeId_equal(&se->session->authenticationToken, token))
+            break;
+    }
+    if(se)
+        return se->session;
+    else
+        return UA_NULL;
 }

+ 10 - 3
src/ua_securechannel.h

@@ -1,6 +1,7 @@
 #ifndef UA_SECURECHANNEL_H_
 #define UA_SECURECHANNEL_H_
 
+#include "queue.h"
 #include "ua_types_generated.h"
 #include "ua_transport_generated.h"
 #include "ua_connection.h"
@@ -14,6 +15,11 @@
 struct UA_Session;
 typedef struct UA_Session UA_Session;
 
+struct SessionEntry {
+    LIST_ENTRY(SessionEntry) pointers;
+    UA_Session *session; // Just a pointer. The session is held in the session manager or the client
+};
+
 struct UA_SecureChannel {
     UA_MessageSecurityMode  securityMode;
     UA_ChannelSecurityToken securityToken; // the channelId is contained in the securityToken
@@ -24,18 +30,19 @@ struct UA_SecureChannel {
     UA_UInt32      requestId;
     UA_UInt32      sequenceNumber;
     UA_Connection *connection;
-    UA_Session    *session;
+    LIST_HEAD(session_pointerlist, SessionEntry) sessions;
 };
 
 void UA_SecureChannel_init(UA_SecureChannel *channel);
-void UA_SecureChannel_deleteMembers(UA_SecureChannel *channel);
+void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel);
 
 UA_StatusCode UA_SecureChannel_generateNonce(UA_ByteString *nonce);
 UA_StatusCode UA_SecureChannel_updateRequestId(UA_SecureChannel *channel, UA_UInt32 requestId);
 UA_StatusCode UA_SecureChannel_updateSequenceNumber(UA_SecureChannel *channel, UA_UInt32 sequenceNumber);
 
 void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session);
-void UA_SecureChannel_detachSession(UA_SecureChannel *channel);
+void UA_SecureChannel_detachSession(UA_SecureChannel *channel, UA_Session *session);
+UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *token);
 
 /** @} */
 

+ 7 - 65
src/ua_session.c

@@ -30,14 +30,6 @@ UA_Session adminSession = {
     .timeout = UA_INT64_MAX, .validTill = UA_INT64_MAX, .channel = UA_NULL,
     .continuationPoints = {UA_NULL}};
 
-/* TODO: Nobody seems to call this function right now */
-static UA_StatusCode UA_Session_generateToken(UA_NodeId *newToken, UA_UInt32 *seed) {
-    newToken->namespaceIndex = 0; // where else?
-    newToken->identifierType = UA_NODEIDTYPE_GUID;
-    newToken->identifier.guid = UA_Guid_random(seed);
-    return UA_STATUSCODE_GOOD;
-}
-
 void UA_Session_init(UA_Session *session) {
     UA_ApplicationDescription_init(&session->clientDescription);
     session->activated = UA_FALSE;
@@ -49,79 +41,29 @@ void UA_Session_init(UA_Session *session) {
     session->timeout = 0;
     UA_DateTime_init(&session->validTill);
     session->channel = UA_NULL;
-    session->continuationPoints = (struct ContinuationPointList){UA_NULL};
     #ifdef ENABLESUBSCRIPTIONS
         SubscriptionManager_init(session);
     #endif
+    session->availableContinuationPoints = MAXCONTINUATIONPOINTS;
+    LIST_INIT(&session->continuationPoints);
 }
 
-void UA_Session_deleteMembers(UA_Session *session) {
+void UA_Session_deleteMembersCleanup(UA_Session *session) {
     UA_ApplicationDescription_deleteMembers(&session->clientDescription);
     UA_NodeId_deleteMembers(&session->authenticationToken);
     UA_NodeId_deleteMembers(&session->sessionId);
     UA_String_deleteMembers(&session->sessionName);
-    session->channel = UA_NULL;
     struct ContinuationPointEntry *cp;
     while((cp = LIST_FIRST(&session->continuationPoints))) {
+        LIST_REMOVE(cp, pointers);
         UA_ByteString_deleteMembers(&cp->identifier);
         UA_BrowseDescription_deleteMembers(&cp->browseDescription);
-        LIST_REMOVE(cp, pointers);
         UA_free(cp);
     }
-}
-
-UA_StatusCode UA_Session_setExpirationDate(UA_Session *session) {
-    if(!session)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    session->validTill = UA_DateTime_now() + session->timeout * 10000; //timeout in ms
-    return UA_STATUSCODE_GOOD;
-}
-
-UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pendingLifetime_ms) {
-    if(!session)
-        return UA_STATUSCODE_BADINTERNALERROR;
-
-    *pendingLifetime_ms = (session->validTill - UA_DateTime_now())/10000; //difference in ms
-    return UA_STATUSCODE_GOOD;
-}
-
-void UA_Session_detachSecureChannel(UA_Session *session) {
-#ifdef UA_MULTITHREADING
-    UA_SecureChannel *channel = session->channel;
-    if(channel)
-        uatomic_cmpxchg(&channel->session, session, UA_NULL);
-    uatomic_set(&session->channel, UA_NULL);
-#else
     if(session->channel)
-        session->channel->session = UA_NULL;
-    session->channel = UA_NULL;
-#endif
-}
-
-/* these functions are here, since they need to include ua_session.h */
-void UA_SecureChannel_attachSession(UA_SecureChannel *channel, UA_Session *session) {
-#ifdef UA_MULTITHREADING
-    if(uatomic_cmpxchg(&session->channel, UA_NULL, channel) == UA_NULL)
-        uatomic_set(&channel->session, session);
-#else
-    if(session->channel != UA_NULL)
-        return;
-    session->channel = channel;
-    channel->session = session;
-#endif
+        UA_SecureChannel_detachSession(session->channel, session);
 }
 
-void UA_SecureChannel_detachSession(UA_SecureChannel *channel) {
-#ifdef UA_MULTITHREADING
-    UA_Session *session = channel->session;
-    if(session)
-        uatomic_cmpxchg(&session->channel, channel, UA_NULL);
-    uatomic_set(&channel->session, UA_NULL);
-#else
-    if(channel == NULL) return;
-    if(channel->session)
-        channel->session->channel = UA_NULL;
-    channel->session = UA_NULL;
-#endif
+void UA_Session_updateLifetime(UA_Session *session) {
+    session->validTill = UA_DateTime_now() + session->timeout * 10000; //timeout in ms
 }

+ 7 - 10
src/ua_session.h

@@ -1,9 +1,11 @@
 #ifndef UA_SESSION_H_
 #define UA_SESSION_H_
 
+#include "queue.h"
 #include "ua_types.h"
 #include "ua_securechannel.h"
-#include "queue.h"
+
+#define MAXCONTINUATIONPOINTS 5
 
 #ifdef ENABLESUBSCRIPTIONS
 #include "server/ua_subscription_manager.h"
@@ -37,6 +39,7 @@ struct UA_Session {
         UA_SubscriptionManager subscriptionManager;
     #endif
     UA_SecureChannel *channel;
+    UA_UInt16 availableContinuationPoints;
     LIST_HEAD(ContinuationPointList, ContinuationPointEntry) continuationPoints;
 };
 
@@ -44,16 +47,10 @@ extern UA_Session anonymousSession; ///< If anonymous access is allowed, this se
 extern UA_Session adminSession; ///< Local access to the services (for startup and maintenance) uses this Session with all possible access rights (Session ID: 1)
 
 void UA_Session_init(UA_Session *session);
-void UA_Session_deleteMembers(UA_Session *session);
-
-/** If any activity on a session happens, the timeout must be extended */
-UA_StatusCode UA_Session_updateLifetime(UA_Session *session);
-/** Set up the point in time till the session is valid */
-UA_StatusCode UA_Session_setExpirationDate(UA_Session *session);
-/** Gets the sessions pending lifetime (calculated from the timeout which was set) */
-UA_StatusCode UA_Session_getPendingLifetime(UA_Session *session, UA_Double *pendingLifetime);
+void UA_Session_deleteMembersCleanup(UA_Session *session);
 
-void UA_Session_detachSecureChannel(UA_Session *session);
+/** If any activity on a session happens, the timeout is extended */
+void UA_Session_updateLifetime(UA_Session *session);
 
 /** @} */
 

+ 108 - 40
src/ua_types.c

@@ -104,7 +104,7 @@ UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-UA_String UA_String_fromChars(char const *src) {
+UA_String UA_String_fromChars(char const src[]) {
     UA_String str;
     size_t length = strlen(src);
     if(length == 0) {
@@ -122,25 +122,25 @@ UA_String UA_String_fromChars(char const *src) {
     return str;
 }
 
-#define UA_STRING_COPYPRINTF_BUFSIZE 1024
-UA_StatusCode UA_String_copyprintf(char const *fmt, UA_String *dst, ...) {
-    char src[UA_STRING_COPYPRINTF_BUFSIZE];
+#define UA_STRING_ALLOCPRINTF_BUFSIZE 1024
+UA_StatusCode UA_String_copyprintf(char const fmt[], UA_String *dst, ...) {
+    char src[UA_STRING_ALLOCPRINTF_BUFSIZE];
     va_list ap;
     va_start(ap, dst);
-#if defined(__GNUC__) || defined(__clang__)
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
     // vsnprintf should only take a literal and no variable to be secure
-    UA_Int32 len = vsnprintf(src, UA_STRING_COPYPRINTF_BUFSIZE, fmt, ap);
-#if defined(__GNUC__) || defined(__clang__)
+    UA_Int32 len = vsnprintf(src, UA_STRING_ALLOCPRINTF_BUFSIZE, fmt, ap);
+#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 || defined(__clang__))
 #pragma GCC diagnostic pop
 #endif
     va_end(ap);
     if(len < 0)  // FIXME: old glibc 2.0 would return -1 when truncated
         return UA_STATUSCODE_BADINTERNALERROR;
     // since glibc 2.1 vsnprintf returns the len that would have resulted if buf were large enough
-    len = ( len > UA_STRING_COPYPRINTF_BUFSIZE ? UA_STRING_COPYPRINTF_BUFSIZE : len );
+    len = ( len > UA_STRING_ALLOCPRINTF_BUFSIZE ? UA_STRING_ALLOCPRINTF_BUFSIZE : len );
     if(!(dst->data = UA_malloc((UA_UInt32)len)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
     UA_memcpy((void *)dst->data, src, (UA_UInt32)len);
@@ -333,11 +333,6 @@ UA_StatusCode UA_NodeId_copy(UA_NodeId const *src, UA_NodeId *dst) {
     return retval;
 }
 
-static UA_Boolean UA_NodeId_isBasicType(UA_NodeId const *id) {
-    return id ->namespaceIndex == 0 && 1 <= id ->identifier.numeric &&
-        id ->identifier.numeric <= 25;
-}
-
 void UA_NodeId_deleteMembers(UA_NodeId *p) {
     switch(p->identifierType) {
     case UA_NODEIDTYPE_STRING:
@@ -401,6 +396,62 @@ UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
     return UA_TRUE;
 }
 
+UA_NodeId UA_NodeId_fromInteger(UA_UInt16 nsIndex, UA_Int32 identifier) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_NUMERIC,
+                         .identifier.numeric = identifier };
+}
+
+UA_NodeId UA_NodeId_fromCharString(UA_UInt16 nsIndex, char identifier[]) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
+                         .identifier.string = UA_STRING(identifier) };
+}
+
+UA_NodeId UA_NodeId_fromCharStringCopy(UA_UInt16 nsIndex, char const identifier[]) {
+    return (UA_NodeId) {.namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
+                        .identifier.string = UA_STRING_ALLOC(identifier) };
+}
+
+UA_NodeId UA_NodeId_fromString(UA_UInt16 nsIndex, UA_String identifier) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
+                         .identifier.string = identifier };
+}
+
+UA_NodeId UA_NodeId_fromStringCopy(UA_UInt16 nsIndex, UA_String identifier) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_STRING;
+    UA_String_copy(&identifier, &id.identifier.string);
+    return id;
+}
+
+UA_NodeId UA_NodeId_fromGuid(UA_UInt16 nsIndex, UA_Guid identifier) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_GUID,
+                         .identifier.guid = identifier };
+}
+
+UA_NodeId UA_NodeId_fromCharByteString(UA_UInt16 nsIndex, char identifier[]) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
+                         .identifier.byteString = UA_STRING(identifier) };
+}
+
+UA_NodeId UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char const identifier[]) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
+                         .identifier.byteString = UA_STRING_ALLOC(identifier) };
+}
+
+UA_NodeId UA_NodeId_fromByteString(UA_UInt16 nsIndex, UA_ByteString identifier) {
+    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
+                         .identifier.byteString = identifier };
+}
+
+UA_NodeId UA_NodeId_fromByteStringCopy(UA_UInt16 nsIndex, UA_ByteString identifier) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    UA_ByteString_copy(&identifier, &id.identifier.byteString);
+    return id;
+}
+
 /* ExpandedNodeId */
 UA_TYPE_NEW_DEFAULT(UA_ExpandedNodeId)
 UA_TYPE_DELETE_DEFAULT(UA_ExpandedNodeId)
@@ -566,15 +617,16 @@ void UA_Variant_deleteMembers(UA_Variant *p) {
     if(p->arrayDimensions) {
         UA_free(p->arrayDimensions);
         p->arrayDimensions = UA_NULL;
+        p->arrayDimensionsSize = -1;
     }
 }
 
 UA_StatusCode UA_Variant_copy(UA_Variant const *src, UA_Variant *dst) {
     UA_Variant_init(dst);
-    UA_Int32 tmp = src->arrayLength;
-    if(src->arrayLength == -1 && src->data)
-        tmp = 1;
-    UA_StatusCode retval = UA_Array_copy(src->data, &dst->data, src->type, tmp);
+    UA_Int32 elements = src->arrayLength;
+    if(UA_Variant_isScalar(src))
+        elements = 1;
+    UA_StatusCode retval = UA_Array_copy(src->data, &dst->data, src->type, elements);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Variant_deleteMembers(dst);
         UA_Variant_init(dst);
@@ -597,7 +649,7 @@ UA_StatusCode UA_Variant_copy(UA_Variant const *src, UA_Variant *dst) {
     return retval;
 }
 
-UA_Boolean UA_Variant_isScalar(UA_Variant *v) {
+UA_Boolean UA_Variant_isScalar(const UA_Variant *v) {
     return (v->data != UA_NULL && v->arrayLength == -1);
 }
 
@@ -628,7 +680,7 @@ testRangeWithVariant(const UA_Variant *v, const UA_NumericRange range, size_t *t
     /* Test the integrity of the range */
     size_t count = 1;
     if(range.dimensionsSize != dims_count)
-        return UA_STATUSCODE_BADINTERNALERROR;
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
     for(UA_Int32 i = 0; i < dims_count; i++) {
         if(range.dimensions[i].min > range.dimensions[i].max)
             return UA_STATUSCODE_BADINDEXRANGEINVALID;
@@ -714,47 +766,63 @@ UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Variant_setRange(UA_Variant *v, void *data, const UA_NumericRange range) {
+UA_StatusCode UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
+                                  const UA_NumericRange range) {
     size_t count, block_size, block_distance, first_elem;
     UA_StatusCode retval = testRangeWithVariant(v, range, &count, &block_size, &block_distance, &first_elem);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
+    if((UA_Int32)count != dataArraySize)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
     size_t block_count = count / block_size;
     size_t elem_size = v->type->memSize;
     uintptr_t nextdst = (uintptr_t)v->data + (first_elem * elem_size);
-    uintptr_t nextsrc = (uintptr_t)data;
-    if(v->type->fixedSize) {
-        for(size_t i = 0; i < block_count; i++) {
-            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
-            nextdst += block_size * elem_size;
-            nextsrc += block_distance * elem_size;
-        }
-    } else {
-        for(size_t i = 0; i < block_count; i++) {
+    uintptr_t nextsrc = (uintptr_t)dataArray;
+    for(size_t i = 0; i < block_count; i++) {
+        if(!v->type->fixedSize) {
             for(size_t j = 0; j < block_size; j++) {
                 UA_deleteMembers((void*)nextdst, v->type);
                 nextdst += elem_size;
             }
             nextdst -= block_size * elem_size;
-            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
-            nextdst += block_size * elem_size;
-            nextsrc += block_distance * elem_size;
         }
+        memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+        nextsrc += block_size * elem_size;
+        nextdst += block_distance * elem_size;
     }
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Variant_setCopyRange(const UA_Variant *src, UA_Variant *dst, void *data,
-                                      const UA_NumericRange range) {
-    // todo: don't copy the entire variant but only the retained parts
-    UA_StatusCode retval = UA_Variant_copy(src, dst);
+UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
+                                                const UA_NumericRange range) {
+    size_t count, block_size, block_distance, first_elem;
+    UA_StatusCode retval = testRangeWithVariant(v, range, &count, &block_size, &block_distance, &first_elem);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    retval = UA_Variant_setRange(dst, data, range);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Variant_deleteMembers(dst);
-        return retval;
+    if((UA_Int32)count != dataArraySize)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+
+    size_t block_count = count / block_size;
+    size_t elem_size = v->type->memSize;
+    uintptr_t nextdst = (uintptr_t)v->data + (first_elem * elem_size);
+    uintptr_t nextsrc = (uintptr_t)dataArray;
+    if(v->type->fixedSize) {
+        for(size_t i = 0; i < block_count; i++) {
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * block_size);
+            nextsrc += block_size * elem_size;
+            nextdst += block_distance * elem_size;
+        }
+    } else {
+        for(size_t i = 0; i < block_count; i++) {
+            for(size_t j = 0; j < block_size; j++) {
+                UA_deleteMembers((void*)nextdst, v->type);
+                UA_copy((void*)nextsrc, (void*)nextdst, v->type);
+                nextdst += elem_size;
+                nextsrc += elem_size;
+            }
+            nextdst += (block_distance - block_size) * elem_size;
+        }
     }
     return UA_STATUSCODE_GOOD;
 }

+ 1 - 1
src/ua_types_encoding_binary.c

@@ -1080,7 +1080,7 @@ UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *ds
         if(member->isArray) {
             ptr += (member->padding >> 3);
             UA_Int32 *noElements = (UA_Int32*)ptr;
-            UA_Int32 tempNoElements;
+            UA_Int32 tempNoElements = 0;
             retval |= UA_Int32_decodeBinary(src, offset, &tempNoElements);
             if(retval)
                 continue;

+ 15 - 1
src/ua_util.h

@@ -75,7 +75,21 @@
 # undef SLIST_ENTRY
 # define RAND(SEED) (UA_UInt32)rand()
 #else
-# include <endian.h>
+# if !(defined htole16 && defined htole32 && defined htole64 && defined le16toh && defined le32toh && defined le64toh)
+#  include <endian.h>
+#  if !(defined htole16 && defined htole32 && defined htole64 && defined le16toh && defined le32toh && defined le64toh)
+#   if ( __BYTE_ORDER != __LITTLE_ENDIAN )
+#    error "Host byte order is not little-endian and no appropriate conversion functions are defined. (Have a look at ua_config.h)"
+#   else
+#    define htole16(x) x
+#    define htole32(x) x
+#    define htole64(x) x
+#    define le16toh(x) x
+#    define le32toh(x) x
+#    define le64toh(x) x
+#   endif
+#  endif
+# endif
 # include <sys/time.h>
 # define RAND(SEED) (UA_UInt32)rand_r(SEED)
 #endif

+ 19 - 17
tests/CMakeLists.txt

@@ -9,7 +9,9 @@ find_package(Threads REQUIRED)
 set(LIBS ${CHECK_LIBRARIES})
 if(NOT WIN32)
     list(APPEND LIBS rt pthread)
-endif(NOT WIN32)
+else()
+    list(APPEND LIBS ws2_32)
+endif()
 if(MULTITHREADING)
     list(APPEND LIBS urcu-cds urcu urcu-common)
 endif(MULTITHREADING)
@@ -47,21 +49,21 @@ add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
 
 # test with canned interactions from files
 
-add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
-                   PRE_BUILD
-                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/hex2bin.py ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex
-                                  ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_CreateActivateSession.hex
-                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/hex2bin.py
-                           ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex)
+#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin
+#                   PRE_BUILD
+#                   COMMAND python ${PROJECT_SOURCE_DIR}/tools/hex2bin.py ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex
+#                                  ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_CreateActivateSession.hex
+#                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/hex2bin.py
+#                           ${CMAKE_CURRENT_SOURCE_DIR}/dumps/client_HELOPN.hex)
 
-set(check_fileinput_source check_server_interaction_fileinput.c testing_networklayers.c $<TARGET_OBJECTS:open62541-object>)
-if(NOT ENABLE_AMALGAMATION)
-    list(APPEND check_fileinput_source ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c)
-endif()
-add_executable(check_server_interaction_fileinput ${check_fileinput_source})
+#set(check_fileinput_source check_server_interaction_fileinput.c testing_networklayers.c $<TARGET_OBJECTS:open62541-object>)
+#if(NOT ENABLE_AMALGAMATION)
+#    list(APPEND check_fileinput_source ${PROJECT_SOURCE_DIR}/examples/logger_stdout.c)
+#endif()
+#add_executable(check_server_interaction_fileinput ${check_fileinput_source})
 
-target_include_directories(check_server_interaction_fileinput PRIVATE ${PROJECT_SOURCE_DIR}/examples)
-target_include_directories(check_server_interaction_fileinput PRIVATE ${PROJECT_BINARY_DIR})
-target_link_libraries(check_server_interaction_fileinput ${LIBS})
-add_test(server_interaction_fileinput ${CMAKE_CURRENT_BINARY_DIR}/check_server_interaction_fileinput ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin)
-add_custom_target(server_interaction_fileinput ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin)
+#target_include_directories(check_server_interaction_fileinput PRIVATE ${PROJECT_SOURCE_DIR}/examples)
+#target_include_directories(check_server_interaction_fileinput PRIVATE ${PROJECT_BINARY_DIR})
+#target_link_libraries(check_server_interaction_fileinput ${LIBS})
+#add_test(server_interaction_fileinput ${CMAKE_CURRENT_BINARY_DIR}/check_server_interaction_fileinput ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin)
+#add_custom_target(server_interaction_fileinput ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/client_HELOPN.bin)

+ 16 - 4
tests/testing_networklayers.c

@@ -53,10 +53,9 @@ NetworkLayer_FileInput_getWork(NetworkLayer_FileInput *layer, UA_WorkItem **work
         
     *workItems = malloc(sizeof(UA_WorkItem));
     UA_WorkItem *work = *workItems;
-    work->type = UA_WORKITEMTYPE_BINARYNETWORKMESSAGE;
-    work->work.binaryNetworkMessage.connection = &layer->connection;
-    work->work.binaryNetworkMessage.message = (UA_ByteString){.length = bytes_read, .data = (UA_Byte*)buf};
-
+    work->type = UA_WORKITEMTYPE_BINARYMESSAGE;
+    work->work.binaryMessage.connection = &layer->connection;
+    work->work.binaryMessage.message = (UA_ByteString){.length = bytes_read, .data = (UA_Byte*)buf};
     return 1;
 }
 
@@ -70,6 +69,17 @@ static void NetworkLayer_FileInput_delete(NetworkLayer_FileInput *layer) {
 	free(layer);
 }
 
+static UA_StatusCode NetworkLayer_FileInput_getBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
+    buf->data = malloc(minSize);
+    if(!buf->data)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    buf->length = minSize;
+    return UA_STATUSCODE_GOOD;
+}
+
+static void NetworkLayer_FileInput_releaseBuffer(UA_Connection *connection, UA_ByteString *buf) {
+    UA_ByteString_deleteMembers(buf);
+}
 
 UA_ServerNetworkLayer
 ServerNetworkLayerFileInput_new(UA_UInt32 files, char **filenames, void(*readCallback)(void),
@@ -82,6 +92,8 @@ ServerNetworkLayerFileInput_new(UA_UInt32 files, char **filenames, void(*readCal
     layer->connection.channel = (void*)0;
     layer->connection.close = (void (*)(UA_Connection*))closeCallback;
     layer->connection.write = (UA_StatusCode (*)(UA_Connection*, const UA_ByteString*))writeCallback;
+    layer->connection.releaseBuffer = NetworkLayer_FileInput_releaseBuffer;
+    layer->connection.getBuffer = NetworkLayer_FileInput_getBuffer;
 
     layer->files = files;
     layer->filenames = filenames;

+ 0 - 20
tools/.deployDoxygen.sh

@@ -1,20 +0,0 @@
-#!/bin/bash
-
-#
-# This script is run by travis-ci and pushes generated
-# Doxygen docs to open62541-www
-#
-
-git fetch origin coverity_scan
-git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/acplt/open62541-www
-cd open62541-www
-git rm -rf doc
-cp -r ../build/doc ./
-git add -A doc/*
-git config --global user.email "open62541-travis-ci@users.noreply.github.com"
-git config --global user.name "Open62541 travis-ci"
-git config --global push.default simple
-git commit -am "doxygen updated by travis-ci"
-git push https://$GITAUTH@github.com/acplt/open62541-www
-cd ..
-rm -rf open62541-www

+ 59 - 0
tools/.deployGH.sh

@@ -0,0 +1,59 @@
+#!/bin/bash
+
+#
+# This script is run by travis-ci and pushes generated
+# Doxygen docs and single-source distributions to open62541-www
+#
+TAGSTOSAVE=50
+
+#current tag info
+TAG="$(git rev-parse --short=10 HEAD)"
+BRANCH="$(git log --pretty=format:"%d" --date=iso --abbrev=10 --all -1)"
+DATE="$(git log --pretty=format:"%ad" --date=iso --abbrev=10 --all -1)"
+COMMENT="$(git log --pretty=format:"%s" --date=iso --abbrev=10 --all -1)"
+
+git fetch origin coverity_scan
+git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/acplt/open62541-www
+cd open62541-www
+#hanndle releases
+cd releases
+if [ ! -e "$TAG.zip" ]; then
+#add the first line
+echo "<tr><td><a href='./$TAG.zip'>$TAG</a></td><td>$BRANCH</td><td>$DATE</td><td>$COMMENT</td></tr>" | cat - rawtable.txt > temp && mv temp rawtable.txt
+
+#keep top lines only
+head "-$TAGSTOSAVE" rawtable.txt > temp && mv temp rawtable.txt
+
+#compose the index file
+cat head.txt rawtable.txt foot.txt > index.html
+
+#create a zip for single-file release and copy the files
+cp ../../open62541.c .
+cp ../../open62541.h .
+zip -r "$TAG.zip" open62541.c open62541.h
+rm open62541.c
+rm open62541.h
+git add "$TAG.zip"
+
+echo "$TAG.zip" | cat - raw.txt > temp && mv temp raw.txt
+
+LINETOSTART=$((TAGSTOSAVE+1))
+#remove obsolete zips
+tail -n +"$LINETOSTART" raw.txt | xargs git rm
+
+#remove obsolete zips from list
+head "-$TAGSTOSAVE" raw.txt > temp && mv temp raw.txt
+fi
+cd ..
+
+#handle doc
+git rm -r -f doc
+cp -r ../doc ./
+git add -A doc/*
+git config --global user.email "open62541-travis-ci@users.noreply.github.com"
+git config --global user.name "Open62541 travis-ci"
+git config --global push.default simple
+git commit -am "gh-pages updated by travis-ci"
+git push https://$GITAUTH@github.com/acplt/open62541-www
+cd ..
+rm -rf open62541-www

+ 13 - 5
tools/amalgamate.py

@@ -1,8 +1,10 @@
 from __future__ import print_function
 import re
 import argparse
+import os.path
 
 parser = argparse.ArgumentParser()
+parser.add_argument('version', help='version to include')
 parser.add_argument('outfile', help='outfile w/o extension')
 parser.add_argument('inputs', nargs='*', action='store', help='input filenames')
 args = parser.parse_args()
@@ -30,8 +32,13 @@ for fname in args.inputs:
                     includes.append(inc)
 
 file = open(args.outfile, 'w')
-file.write('''/*
- * Copyright (C) 2014 the contributors as stated in the AUTHORS file
+file.write('''/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES 
+ * visit http://open62541.org/ for information about this software
+ * Git-Revision: %s
+ */
+ 
+ /*
+ * Copyright (C) 2015 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
@@ -43,9 +50,7 @@ file.write('''/*
  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
  * details.
- */
-
-/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES */\n\n''')
+ */\n\n''' % args.version)
 
 if not is_c:
     file.write('''#ifndef %s
@@ -73,6 +78,7 @@ else:
 for fname in args.inputs:
     if not "util.h" in fname:
         with open(fname) as infile:
+            file.write("/*********************************** amalgamated original file \"" + fname + "\" ***********************************/\n")
             for line in infile:
                 inc_res = include_re.match(line)
                 guard_res = guard_re.match(line)
@@ -87,3 +93,5 @@ if not is_c:
 
 #endif /* %s */''' % (outname.upper() + "_H_"))
 file.close()
+
+print ("The size of "+args.outfile+" is "+ str(os.path.getsize(args.outfile)))

+ 6 - 2
tools/certs/create_self-signed.py

@@ -40,7 +40,7 @@ os.system("""openssl x509 -req \
 	-out localhost.crt \
 	-extensions v3_ca \
 	-extfile localhost.cnf""")
-os.system("""openssl x509 -in localhost.crt -outform der -out localhost.der""")
+os.system("""openssl x509 -in localhost.crt -outform der -out server_cert.der""")
 #we will need these files later
 os.remove("localhost.key") #we will need it later
 os.remove("localhost.crt")
@@ -48,5 +48,9 @@ os.remove("localhost.csr")
 os.remove("ca.key")
 os.remove("ca.srl")
 
-shutil.move("localhost.der", sys.argv[1])
+if os.path.isfile(os.path.join(sys.argv[1], "server_cert.der")):
+	os.remove(os.path.join(sys.argv[1], "server_cert.der"))
+shutil.move("server_cert.der", sys.argv[1])
+if os.path.isfile(os.path.join(sys.argv[1], "ca.crt")):
+	os.remove(os.path.join(sys.argv[1], "ca.crt"))
 shutil.move("ca.crt", sys.argv[1])

+ 3 - 3
tools/generate_datatypes.py

@@ -161,7 +161,7 @@ class EnumerationType(object):
 
     def typedef_c(self):
         return "typedef enum { \n    " + \
-            ",\n    ".join(map(lambda (key,value) : key.upper() + " = " + value,self.elements.iteritems())) + \
+            ",\n    ".join(map(lambda kv : kv[0].upper() + " = " + kv[1], self.elements.iteritems())) + \
             "\n} " + self.name + ";"
 
     def typelayout_c(self, namespace_0, description, outname):
@@ -311,7 +311,7 @@ class StructType(object):
         return layout + "}"
 
     def functions_c(self, typeTableName):
-        return '''#define %s_new() UA_new(%s)
+        return '''#define %s_new() (%s*)UA_new(%s)
 #define %s_init(p) UA_init(p, %s)
 #define %s_delete(p) UA_delete(p, %s)
 #define %s_deleteMembers(p) UA_deleteMembers(p, %s)
@@ -319,7 +319,7 @@ class StructType(object):
 #define %s_calcSizeBinary(p) UA_calcSizeBinary(p, %s)
 #define %s_encodeBinary(src, dst, offset) UA_encodeBinary(src, %s, dst, offset)
 #define %s_decodeBinary(src, offset, dst) UA_decodeBinary(src, offset, dst, %s)''' % \
-    tuple(itertools.chain(*itertools.repeat([self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 8)))
+    tuple([self.name] + list(itertools.chain(*itertools.repeat([self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 8))))
 
 def parseTypeDefinitions(xmlDescription, existing_types = OrderedDict()):
     '''Returns an ordered dict that maps names to types. The order is such that

文件差异内容过多而无法显示
+ 0 - 23008
tools/schema/Opc.Ua.NodeSet2.xml