Quellcode durchsuchen

Merge branch 'master' into dev

Conflicts:
	examples/client.c
	examples/networklayer_tcp.c
	src/client/ua_client.c
	src/server/ua_server_binary.c
	src/server/ua_services_nodemanagement.c
	tools/generate_datatypes.py
Julius Pfrommer vor 9 Jahren
Ursprung
Commit
21944d61fd
47 geänderte Dateien mit 4402 neuen und 2762 gelöschten Zeilen
  1. 4 1
      .travis.yml
  2. 36 19
      CMakeLists.txt
  3. 3 1
      README.md
  4. 34 0
      appveyor.yml
  5. 11 0
      cmake/FindSphinx.cmake
  6. 3 3
      doc/Doxyfile.in
  7. 288 0
      doc/conf.py
  8. 219 0
      doc/datatypes.rst
  9. 137 0
      doc/index.rst
  10. 0 0
      doc/style/doxygen.css
  11. 88 9
      examples/client.c
  12. 2 6
      examples/logger_stdout.h
  13. 101 181
      examples/networklayer_tcp.c
  14. 1 6
      examples/networklayer_tcp.h
  15. 46 100
      examples/server.c
  16. 6 8
      examples/server_simple.c
  17. 23 2
      include/ua_client.h
  18. 45 28
      include/ua_config.h.in
  19. 1 1
      include/ua_connection.h
  20. 17 32
      include/ua_server.h
  21. 69 98
      include/ua_types.h
  22. 254 64
      src/client/ua_client.c
  23. 10 6
      src/server/ua_nodestore.c
  24. 9 2
      src/server/ua_nodestore_concurrent.c
  25. 28 14
      src/server/ua_securechannel_manager.c
  26. 77 60
      src/server/ua_server.c
  27. 6 1
      src/server/ua_server_addressspace.c
  28. 65 38
      src/server/ua_server_binary.c
  29. 5 8
      src/server/ua_server_worker.c
  30. 8 2
      src/server/ua_services.h
  31. 34 49
      src/server/ua_services_attribute.c
  32. 9 8
      src/server/ua_services_nodemanagement.c
  33. 8 6
      src/server/ua_services_session.c
  34. 2 2
      src/ua_connection.c
  35. 30 16
      src/ua_securechannel.c
  36. 2 0
      src/ua_securechannel.h
  37. 35 29
      src/ua_types.c
  38. 580 494
      src/ua_types_encoding_binary.c
  39. 28 16
      src/ua_types_encoding_binary.h
  40. 17 48
      src/ua_util.h
  41. 4 0
      tests/CMakeLists.txt
  42. 1334 1333
      tests/check_builtin.c
  43. 8 5
      tests/check_memory.c
  44. 694 0
      tests/check_services_attributes.c
  45. 0 21
      tests/check_stack.c
  46. 15 37
      tools/amalgamate.py
  47. 6 8
      tools/generate_datatypes.py

+ 4 - 1
.travis.yml

@@ -26,6 +26,9 @@ before_install:
 - sudo dpkg -i liburcu2_0.8.5-1ubuntu1_amd64.deb
 - sudo dpkg -i liburcu-dev_0.8.5-1ubuntu1_amd64.deb
 - sudo pip install cpp-coveralls
+- sudo pip install sphinx
+- sudo pip install breathe
+- sudo pip install sphinx_rtd_theme
 script:
 - echo "Checking the applicability of patches"
 - # ./tools/.checkPorts.sh
@@ -88,7 +91,7 @@ script:
 - echo "Debug build and unit tests (64 bit)"
 - cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_DEMO_NODESET=ON -DBUILD_UNIT_TESTS=ON -DENABLE_COVERAGE=ON ..
 - make && make test
-- coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../
+- (coveralls --gcov /usr/bin/gcov-4.8 -E '.*\.h' -E '.*CMakeCXXCompilerId\.cpp' -E '.*CMakeCCompilerId\.c' -r ../ ; exit 0)
 - cd .. 
 after_success:
 - ./tools/.deployGH.sh

+ 36 - 19
CMakeLists.txt

@@ -18,13 +18,13 @@ 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")
+        set( GIT_COMMIT_ID "unknown--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!)")
+    set( GIT_COMMIT_ID "unknown--no-git-found")
     message( WARNING "Git not found. Build will not contain git revision info." )
 endif()
 add_definitions("-DVERSION=${GIT_COMMIT_ID}")
@@ -40,7 +40,7 @@ if(NOT CMAKE_BUILD_TYPE)
 endif()
 
 # compiler flags
-if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     add_definitions(-std=c99 -pipe -Wall -Wextra -Werror -Wformat -Wno-unused-parameter
                     -Wno-unused-function -Wno-unused-label -Wpointer-arith -Wreturn-type -Wsign-compare -Wmultichar
                     -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes # -Wshadow -Wconversion
@@ -194,6 +194,12 @@ if(ENABLE_EXTERNAL_NAMESPACES)
     add_definitions(-DUA_EXTERNAL_NAMESPACES)
 endif()
 
+## enable dynamic nodeset
+option(ENABLE_ADDNODES "Enable dynamic addition of nodes" ON)
+if(ENABLE_ADDNODES)
+    add_definitions(-DENABLE_ADDNODES )
+endif()
+
 ## set the precompiler flags
 configure_file("include/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
 
@@ -225,16 +231,16 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/open62541.c
                PRE_BUILD
 
                COMMAND ${PYTHON_EXECUTABLE} ${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}
+                                            ${internal_headers} ${PROJECT_SOURCE_DIR}/src/server/ua_nodestore_hash.inc ${lib_sources}
 
-               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})
+               DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/amalgamate.py ${internal_headers} ${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)
-    add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c)
+    add_custom_target(amalgamation ALL DEPENDS ${PROJECT_BINARY_DIR}/open62541.h ${PROJECT_BINARY_DIR}/open62541.c)
+    add_library(open62541-object OBJECT ${PROJECT_BINARY_DIR}/open62541.c ${PROJECT_BINARY_DIR}/open62541.h)
     include_directories(${PROJECT_BINARY_DIR})
 else()
-    add_definitions(-DNOT_AMALGATED)
+    add_definitions(-DUA_NO_AMALGAMATION)
     add_library(open62541-object OBJECT ${lib_sources} ${internal_headers} ${exported_headers})
     include_directories(${PROJECT_SOURCE_DIR}/include)
     include_directories(${PROJECT_SOURCE_DIR}/deps)
@@ -272,7 +278,10 @@ if(BUILD_EXAMPLESERVER)
     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(ENABLE_AMALGAMATION)
+       ADD_DEPENDENCIES(server amalgamation) 
+    endif()
+    target_link_libraries(server open62541)
     if(WIN32)
         target_link_libraries(server_static ws2_32)
         target_link_libraries(server_simple ws2_32)
@@ -308,7 +317,10 @@ if(BUILD_EXAMPLECLIENT)
 	add_definitions(-DBENCHMARK)
     set(client_source $<TARGET_OBJECTS:open62541-object>)
 	add_executable(client_static examples/client.c ${client_source})
-	add_executable(client examples/client.c)
+    add_executable(client examples/client.c)
+	if(ENABLE_AMALGAMATION)
+	   ADD_DEPENDENCIES(client amalgamation) 
+	endif()
 	target_link_libraries(client open62541)
     if(WIN32)
         target_link_libraries(client_static ws2_32)
@@ -332,20 +344,25 @@ endif()
 # build unit tests
 option(BUILD_UNIT_TESTS "Run unit tests after building" OFF)
 if(BUILD_UNIT_TESTS)
+	add_definitions(-DBUILD_UNIT_TESTS)
     enable_testing()
     add_subdirectory(tests)
 endif()
 
 # build documentation
-option(BUILD_DOCUMENTATION "Generate doxygen documentation" OFF)
+option(BUILD_DOCUMENTATION "Generate doxygen/sphinx documentation" OFF)
 if(BUILD_DOCUMENTATION)
-    find_package(Doxygen)
-        if(NOT DOXYGEN_FOUND)
-            message(FATAL_ERROR "Doxygen is not installed or not properly configured")
-        endif(NOT DOXYGEN_FOUND)
-    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
-    add_custom_target(doc
-                      ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
-                      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+    find_package(Doxygen REQUIRED)
+    find_package(Sphinx REQUIRED)
+    configure_file(${PROJECT_SOURCE_DIR}/doc/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile @ONLY)
+    configure_file(${PROJECT_SOURCE_DIR}/doc/conf.py ${PROJECT_BINARY_DIR}/conf.py @ONLY)
+    add_custom_target(doxygen
+                      ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile
+                      WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
                       COMMENT "Generating API documentation with Doxygen")
+    add_custom_target(doc ${SPHINX_EXECUTABLE}
+      -b html -c "${PROJECT_BINARY_DIR}"
+      "${PROJECT_SOURCE_DIR}/doc" "${PROJECT_BINARY_DIR}/doc/sphinx"
+      DEPENDS doxygen
+      COMMENT "Building HTML documentation with Sphinx")
 endif()

+ 3 - 1
README.md

@@ -7,6 +7,7 @@ open62541 is licensed under the LGPL + static linking exception. That means **op
 
 [![Ohloh Project Status](https://www.ohloh.net/p/open62541/widgets/project_thin_badge.gif)](https://www.ohloh.net/p/open62541)
 [![Build Status](https://travis-ci.org/acplt/open62541.png?branch=master)](https://travis-ci.org/acplt/open62541)
+[![MSVS build status](https://ci.appveyor.com/api/projects/status/w2geggs5s28gfu6t/branch/master?svg=true)](https://ci.appveyor.com/project/Stasik0/open62541/branch/master)
 [![Coverage Status](https://coveralls.io/repos/acplt/open62541/badge.png?branch=master)](https://coveralls.io/r/acplt/open62541?branch=master)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/1864/badge.svg)](https://scan.coverity.com/projects/1864)
 
@@ -19,7 +20,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)
+Automated builds of 50 last single-file distributions are available [here](http://open62541.org/releases).
+Automatically compiled MSVC binaries are available [here](https://ci.appveyor.com/project/Stasik0/open62541/build/artifacts).
 
 ### 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:

+ 34 - 0
appveyor.yml

@@ -0,0 +1,34 @@
+version: '{build}'
+os: Visual Studio 2015 RC
+clone_folder: c:\projects\open62541
+before_build:
+- python -m pip install lxml
+build_script:
+- cd c:\projects\open62541
+- md build
+- cd build
+- cmake -DBUILD_EXAMPLESERVER:BOOL=ON -DBUILD_EXAMPLECLIENT:BOOL=ON -G"Visual Studio 12 2013" ..
+- msbuild open62541.sln
+- echo "Testing amalgamation"
+- cd ..
+- rd /s /q build
+- md build
+- cd build
+- cmake -DBUILD_EXAMPLESERVER:BOOL=ON -DBUILD_EXAMPLECLIENT:BOOL=ON -DENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013" ..
+- msbuild open62541.sln 
+- cp C:\projects\open62541\build\open62541.c C:\projects\open62541\build\Debug\open62541.c
+- cp C:\projects\open62541\build\open62541.h C:\projects\open62541\build\Debug\open62541.h
+- cd ..
+- echo "Win 64 build"
+- md build64
+- cd build64
+- cmake -DBUILD_EXAMPLESERVER:BOOL=ON -DBUILD_EXAMPLECLIENT:BOOL=ON -DENABLE_AMALGAMATION:BOOL=ON -G"Visual Studio 12 2013 Win64" ..
+- msbuild open62541.sln 
+- cp C:\projects\open62541\build64\open62541.c C:\projects\open62541\build64\Debug\open62541.c
+- cp C:\projects\open62541\build64\open62541.h C:\projects\open62541\build64\Debug\open62541.h
+- cd ..
+after_build:
+- 7z a open62541-win32.zip %APPVEYOR_BUILD_FOLDER%\build\Debug\*
+- 7z a open62541-win64.zip %APPVEYOR_BUILD_FOLDER%\build64\Debug\*
+- appveyor PushArtifact open62541-win32.zip
+- appveyor PushArtifact open62541-win64.zip

+ 11 - 0
cmake/FindSphinx.cmake

@@ -0,0 +1,11 @@
+find_program(SPHINX_EXECUTABLE NAMES sphinx-build
+  HINTS
+  $ENV{SPHINX_DIR}
+  PATH_SUFFIXES bin
+  DOC "Sphinx documentation generator")
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(Sphinx DEFAULT_MSG SPHINX_EXECUTABLE)
+
+mark_as_advanced(SPHINX_EXECUTABLE)

+ 3 - 3
doc/Doxyfile.in

@@ -1799,7 +1799,7 @@ MAN_LINKS              = NO
 # captures the structure of the code including all documentation.
 # The default value is: NO.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
 # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -1911,7 +1911,7 @@ MACRO_EXPANSION        = YES
 # The default value is: NO.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-EXPAND_ONLY_PREDEF     = NO
+EXPAND_ONLY_PREDEF     = YES
 
 # If the SEARCH_INCLUDES tag is set to YES the includes files in the
 # INCLUDE_PATH will be searched if a #include is found.
@@ -1943,7 +1943,7 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = 
+PREDEFINED             = UA_EXPORT= UA_BITFIELD(SIZE)=
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The

+ 288 - 0
doc/conf.py

@@ -0,0 +1,288 @@
+# -*- coding: utf-8 -*-
+#
+# open62541 documentation build configuration file, created by
+# sphinx-quickstart on Sat May 23 19:39:37 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+import shlex
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['breathe']
+
+breathe_projects = { "open62541-breathe": "doc/xml/" }
+breathe_default_project = "open62541-breathe"
+breathe_domain_by_extension = {"h" : "c", "c" : "c"}
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'open62541'
+copyright = u'2015, The open62541 authors'
+author = u'The open62541 authors'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0'
+# The full version, including alpha/beta/rc tags.
+release = '1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+#   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'open62541doc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+  (master_doc, 'open62541.tex', u'open62541 Documentation',
+   u'The open62541 authors', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'open62541', u'open62541 Documentation',
+     [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  (master_doc, 'open62541', u'open62541 Documentation',
+   author, 'open62541', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False

+ 219 - 0
doc/datatypes.rst

@@ -0,0 +1,219 @@
+Data Types
+==========
+
+Generic Data Type Handling
+--------------------------
+
+All data types are combinations of the 25 builtin data types show below. Types
+are described in the UA_DataType structure.
+
+.. doxygenstruct:: UA_DataType
+   :members:
+
+.. c:var:: const UA_DataType UA_TYPES[UA_TYPES_COUNT]
+
+  The datatypes defined in the standard are stored in the ``UA_TYPES`` array.
+  A typical function call is ``UA_Array_new(&data_ptr, 20, &UA_TYPES[UA_TYPES_STRING])``.
+
+.. doxygenfunction:: UA_new
+.. doxygenfunction:: UA_init
+.. doxygenfunction:: UA_copy
+.. doxygenfunction:: UA_deleteMembers
+.. doxygenfunction:: UA_delete
+
+For all datatypes, there are also macros with syntactic sugar over calling the
+generic functions with a pointer into the ``UA_TYPES`` array.
+
+.. c:function:: <typename>_new()
+
+  Allocates the memory for the type and runs _init on the returned variable.
+  Returns null if no memory could be allocated.
+
+.. c:function:: <typename>_init(<typename> *value)
+
+  Sets all members of the type to a default value, usually zero. Arrays (e.g.
+  for strings) are set to a length of -1.
+
+.. c:function:: <typename>_copy(<typename> *src, <typename> *dst)
+
+  Copies a datatype. This performs a deep copy iterating over the members.
+  Copying into variants with an external data source is not permitted. If
+  copying fails, a deleteMembers is performed and an error code returned.
+
+.. c:function:: <typename>_deleteMembers(<typename> *value)
+
+   Frees the memory of dynamically sized members of a datatype (e.g. arrays).
+
+.. c:function:: <typename>_delete(<typename> *value)
+
+   Frees the memory of the datatype and its dynamically allocated members.
+
+Array Handling
+--------------
+   
+.. doxygenfunction:: UA_Array_new
+.. doxygenfunction:: UA_Array_copy
+.. doxygenfunction:: UA_Array_delete
+
+Builtin Data Types
+------------------
+
+Number-Types
+^^^^^^^^^^^^
+
+.. doxygentypedef:: UA_Boolean
+.. doxygentypedef:: UA_SByte
+.. doxygentypedef:: UA_Byte
+.. doxygentypedef:: UA_Int16
+.. doxygentypedef:: UA_UInt16
+.. doxygentypedef:: UA_Int32
+.. doxygentypedef:: UA_UInt32
+.. doxygentypedef:: UA_Int64
+.. doxygentypedef:: UA_UInt64
+.. doxygentypedef:: UA_Float
+.. doxygentypedef:: UA_Double
+
+UA_String
+^^^^^^^^^
+.. doxygenstruct:: UA_String
+  :members:
+
+.. c:macro:: UA_STRING_NULL
+
+  The empty string
+
+.. c:macro:: UA_STRING(CHARS)
+     
+  Creates an UA_String from an array of ``char``. The characters are not copied
+  on the heap. Instead, the string points into the existing array.
+
+.. c:macro:: UA_STRING_ALLOC(CHARS)
+     
+  Creates an UA_String from an array of ``char``. The characters are copied on
+  the heap.
+
+.. doxygenfunction:: UA_String_equal
+.. doxygenfunction:: UA_String_copyprintf
+
+UA_DateTime
+^^^^^^^^^^^
+.. doxygentypedef:: UA_DateTime
+.. doxygenfunction:: UA_DateTime_now(void)
+.. doxygenfunction:: UA_DateTime_toString
+.. doxygenfunction:: UA_DateTime_toStruct
+
+UA_Guid
+^^^^^^^
+.. doxygenstruct:: UA_Guid
+.. doxygenfunction:: UA_Guid_equal
+.. doxygenfunction:: UA_Guid_random
+   
+UA_ByteString
+^^^^^^^^^^^^^
+Bytestring are just a redefinition of strings. The semantic difference is that
+ByteStrings may hold non-UTF8 data.
+
+.. doxygentypedef:: UA_ByteString
+
+.. c:macro:: UA_BYTESTRING_NULL
+
+   The empty ByteString
+
+.. c:function:: UA_Boolean UA_ByteString_equal(const UA_ByteString *s1, const UA_ByteString *s2)
+
+   Compares two ByteStrings.
+   
+UA_XmlElement
+^^^^^^^^^^^^^
+XmlElements are just a redefinition of strings.
+
+.. doxygentypedef:: UA_XmlElement
+
+UA_NodeId
+^^^^^^^^^
+.. doxygenstruct:: UA_NodeId
+  :members:
+
+.. doxygenfunction:: UA_NodeId_equal
+.. doxygenfunction:: UA_NodeId_isNull
+
+.. c:macro:: UA_NODEID_NULL
+
+  The null NodeId
+
+.. c:macro:: UA_NODEID_NUMERIC(NSID, NUMERICID)
+.. c:macro:: UA_NODEID_STRING(NSID, CHARS)
+.. c:macro:: UA_NODEID_STRING_ALLOC(NSID, CHARS)
+.. c:macro:: UA_NODEID_GUID(NSID, GUID)
+.. c:macro:: UA_NODEID_BYTESTRING(NSID, CHARS)
+.. c:macro:: UA_NODEID_BYTESTRING_ALLOC(NSID, CHARS)
+
+UA_ExpandedNodeId
+^^^^^^^^^^^^^^^^^
+.. doxygenstruct:: UA_ExpandedNodeId
+  :members:
+
+.. doxygenfunction:: UA_ExpandedNodeId_isNull
+.. c:macro:: UA_EXPANDEDNODEID_NUMERIC(NSID, NUMERICID)
+  
+UA_StatusCode
+^^^^^^^^^^^^^
+Many functions in open62541 return either ``UA_STATUSCODE_GOOD`` or an error code.
+
+.. doxygentypedef:: UA_StatusCode
+
+UA_QualifiedName
+^^^^^^^^^^^^^^^^   
+.. doxygenstruct:: UA_QualifiedName
+  :members:
+
+.. c:macro:: UA_QUALIFIEDNAME(NSID, CHARS)
+.. c:macro:: UA_QUALIFIEDNAME_ALLOC(NSID, CHARS)
+  
+UA_LocalizedText
+^^^^^^^^^^^^^^^^
+.. doxygenstruct:: UA_LocalizedText
+  :members:
+
+.. c:macro:: UA_LOCALIZEDTEXT(LOCALE, TEXT)
+   Takes two arrays of ``char`` as the input.
+
+.. c:macro:: UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT)
+  
+UA_ExtensionObject
+^^^^^^^^^^^^^^^^^^
+
+.. doxygenstruct:: UA_ExtensionObject
+  :members:
+
+UA_DataValue
+^^^^^^^^^^^^
+
+.. doxygenstruct:: UA_DataValue
+  :members:
+  :undoc-members:
+
+UA_Variant
+^^^^^^^^^^
+
+.. doxygenstruct:: UA_Variant
+  :members:
+
+.. doxygenfunction:: UA_Variant_isScalar
+.. doxygenfunction:: UA_Variant_setScalar
+.. doxygenfunction:: UA_Variant_setScalarCopy
+.. doxygenfunction:: UA_Variant_setArray
+.. doxygenfunction:: UA_Variant_setArrayCopy
+
+.. doxygenstruct:: UA_NumericRange
+   :undoc-members:
+
+.. doxygenfunction:: UA_Variant_setRange
+.. doxygenfunction:: UA_Variant_setRangeCopy
+        
+UA_DiagnosticInfo
+^^^^^^^^^^^^^^^^^
+
+.. doxygenstruct:: UA_DiagnosticInfo
+  :members:
+  :undoc-members:

+ 137 - 0
doc/index.rst

@@ -0,0 +1,137 @@
+Welcome to open62541's documentation!
+=====================================
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+
+   datatypes
+
+`OPC UA <http://en.wikipedia.org/wiki/OPC_Unified_Architecture>`_ (short for OPC
+Unified Architecture) is a protocol for industrial communication and has been
+standardized in the IEC62541. At its core, OPC UA defines a set of services to
+interact with a server-side object-oriented information model. Besides the
+service-calls initiated by the client, push-notification of remote events (such
+as data changes) can be negotiated with the server. The client/server
+interaction is mapped either to a binary encoding and TCP-based transmission or
+to SOAP-based webservices. As of late, OPC UA is marketed as the one standard
+for non-realtime industrial communication.
+
+We believe that it is best to understand OPC UA *from the inside out*, building
+upon conceptually simple first principles. After establishing a first
+understanding, we go on explaining how these principles are realized in detail.
+Examples are given based on the *open62541* implementation of the
+standard.
+
+OPC UA, a collection of services
+--------------------------------
+
+In OPC-UA, all communication is based on service calls, each consisting of a request and a response
+message. Be careful to note the difference between services and methods. Services are pre-defined in
+the standard and cannot be changed. But you can use the *Call* service to invoke user-defined
+methods on the server.
+
+For completeness, the following tables contain all services defined in the standard. Do not bother
+with their details yet. We will introduce the different services later in the text. In open62541,
+each service is implemented in a single function. See the \ref services section for details.
+
+**Establishing communication**
+
++-----------------------------+-----------------------------+------------------------------+
+| Discovery Service Set       | SecureChannel Service Set   | Session Service Set          |
++=============================+=============================+==============================+
+| FindServers                 | OpenSecureChannel           | CreateSession                |
++-----------------------------+-----------------------------+------------------------------+
+| GetEndpoints                | CloseSecureChannel          | ActivateSession              |
++-----------------------------+-----------------------------+------------------------------+
+| RegisterServer              | CloseSession                |                              |
++-----------------------------+-----------------------------+------------------------------+
+| Cancel                      |                             |                              |
++-----------------------------+-----------------------------+------------------------------+
+
+**Interaction with the information model**
+
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+| Attribute Service Set       | View Service Set              | Method Service Set           | NodeManagement Service Set   | Query Service Set    |
++=============================+===============================+==============================+==============================+======================+
+| Read                        | Browse                        | Call                         | AddNodes                     | QueryFirst           |
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+| HistoryRead                 | BrowseNext                    |                              | AddReferences                | QueryNext            |
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+| Write                       | TranslateBrowsePathsToNodeids |                              | DeleteNodes                  |                      |
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+| HistoryUpdate               | RegisterNodes                 |                              | DeleteReferences             |                      |
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+|                             | UnregisterNodes               |                              |                              |                      |
++-----------------------------+-------------------------------+------------------------------+------------------------------+----------------------+
+
+**Notifications**
+
++-----------------------------+-------------------------------+
+| MonitoredItem Service Set   | Subscription Service Set      |
++=============================+===============================+
+| CreateMonitoredItems        | CreateSubscription            |
++-----------------------------+-------------------------------+
+| ModifyMonitoreditems        | ModifySubscription            |
++-----------------------------+-------------------------------+
+| SetMonitoringMode           | SetPublishingMode             |
++-----------------------------+-------------------------------+
+| SetTriggering               | Publish                       |
++-----------------------------+-------------------------------+
+| DeleteMonitoredItems        | Republish                     |
++-----------------------------+-------------------------------+
+|                             | TransferSubscription          |
++-----------------------------+-------------------------------+
+|                             | DeleteSubscription            |
++-----------------------------+-------------------------------+
+
+OPC UA, a web of nodes
+----------------------
+
+The information model in each OPC UA server is a web of interconnected nodes.
+There are eight different types of nodes. Depending on its type, every node
+contains different attributes. Some attributes, such as the *NodeId* (unique
+identifier) and the *BrowseName*, are contained in all node types.
+
++-----------------------+-------------------+
+| ReferenceTypeNode     | MethodNode        |
++-----------------------+-------------------+
+| DataTypeNode          | ObjectTypeNode    |
++-----------------------+-------------------+
+| VariableTypeNode      | ObjectNode        |
++-----------------------+-------------------+
+| VariableNode          | ViewNode          |
++-----------------------+-------------------+
+                                                                                                            
+Nodes are interconnected by directed reference-triples of the form ``(nodeid,
+referencetype, target-nodeid)``. Therefore an OPC UA information model is
+easiest imagined as a *web of nodes*. Reference types can be
+
+- standard- or user-defined and
+- either non-hierarchical (e.g., indicating the type of a variable-node) or
+  hierarchical (e.g., indicating a parent-child relationship).
+
+OPC UA, a protocol
+------------------
+
+The OPC UA protocol (both binary and XML-based) is based on 25 *built-in*
+datatypes. In open62541, these are defined in ua_types.h.
+
+The builtin datatypes are combined to more complex structures. When the structure contains an array,
+then the size of the array is stored in an Int32 value just before the array itself. A size of -1
+indicates an undefined array. Positive sizes (and zero) have the usual semantics.
+
+Most importantly, every service has a request and a response message defined as such a data
+structure. The entire OPC UA protocol revolves around the exchange of these request and response
+messages. Their exact definitions can be looked up here:
+https://opcfoundation.org/UA/schemas/Opc.Ua.Types.bsd.xml. In open62541, we autogenerate the
+C-structs to handle the standard-defined structures automatically. See ua_types_generated.h for
+comparison.
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+

+ 0 - 0
doc/style/doxygen.css


+ 88 - 9
examples/client.c

@@ -1,11 +1,14 @@
-#ifdef NOT_AMALGATED
-    #include "ua_types.h"
-    #include "ua_client.h"
-    #include "ua_nodeids.h"
-    #include "networklayer_tcp.h"
-    #include "logger_stdout.h"
+#ifdef UA_NO_AMALGAMATION
+# include "ua_types.h"
+# include "ua_client.h"
+# include "ua_nodeids.h"
+# include "networklayer_tcp.h"
+# include "logger_stdout.h"
+# include "ua_types_encoding_binary.h"
 #else
-    #include "open62541.h"
+# include "open62541.h"
+# include <string.h>
+# include <stdlib.h>
 #endif
 
 #include <stdio.h>
@@ -113,11 +116,10 @@ int main(int argc, char *argv[]) {
     wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_INT32];
     wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion
     wReq.nodesToWrite[0].value.value.data = &value;
-
+    
     UA_WriteResponse wResp = UA_Client_write(client, &wReq);
     if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
             printf("the new value is: %i\n", value);
-
     UA_WriteRequest_deleteMembers(&wReq);
     UA_WriteResponse_deleteMembers(&wResp);
 
@@ -130,6 +132,83 @@ int main(int argc, char *argv[]) {
         printf("Subscription removed\n");
 #endif
     
+#ifdef ENABLE_ADDNODES 
+    /* Create a new object type node */
+    // New ReferenceType
+    UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client,
+        UA_EXPANDEDNODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times)
+        UA_QUALIFIEDNAME(0, "NewReference"),
+        UA_LOCALIZEDTEXT("en_US", "TheNewReference"),
+        UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist."),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        (UA_UInt32) 0, (UA_UInt32) 0, 
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy"));
+    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
+        printf("Created 'NewReference' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+    }
+    UA_AddNodesResponse_deleteMembers(adResp);
+    free(adResp);
+    
+    // New ObjectType
+    adResp = UA_Client_createObjectTypeNode(client,    
+        UA_EXPANDEDNODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times)
+        UA_QUALIFIEDNAME(0, "NewObjectType"),
+        UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"),
+        UA_LOCALIZEDTEXT("en_US", "Put innovative description here."),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        (UA_UInt32) 0, (UA_UInt32) 0, 
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
+        if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
+        printf("Created 'NewObjectType' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+    }
+    
+    // New Object
+    adResp = UA_Client_createObjectNode(client,    
+        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
+        UA_QUALIFIEDNAME(0, "TheNewGreatNodeBrowseName"),
+        UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"),
+        UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        (UA_UInt32) 0, (UA_UInt32) 0, 
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
+    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
+        printf("Created 'NewObject' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+    }
+    
+    UA_AddNodesResponse_deleteMembers(adResp);
+    free(adResp);
+    
+    // New Integer Variable
+    UA_Variant *theValue = UA_Variant_new();
+    UA_Int32 *theValueDate = UA_Int32_new();
+    *theValueDate = 1234;
+    theValue->type = &UA_TYPES[UA_TYPES_INT32];
+    theValue->data = theValueDate;
+    
+    adResp = UA_Client_createVariableNode(client,
+        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
+        UA_QUALIFIEDNAME(0, "VariableNode"),
+        UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"),
+        UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."),
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+        (UA_UInt32) 0, (UA_UInt32) 0, 
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+        UA_NODEID_NUMERIC(0, UA_NS0ID_INT32),
+        theValue);
+    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
+        printf("Created 'NewVariable' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+    }
+    UA_AddNodesResponse_deleteMembers(adResp);
+    free(adResp);
+    free(theValue);
+    /* Done creating a new node*/
+#endif
+
     UA_Client_disconnect(client);
     UA_Client_delete(client);
     return UA_STATUSCODE_GOOD;

+ 2 - 6
examples/logger_stdout.h

@@ -6,12 +6,8 @@
 #ifndef LOGGER_STDOUT_H_
 #define LOGGER_STDOUT_H_
 
-#ifdef NOT_AMALGATED
-    #include "ua_types.h"
-    #include "ua_log.h"
-#else
-    #include "open62541.h"
-#endif
+#include "ua_types.h"
+#include "ua_log.h"
 
 /** Initialises the logger for the current thread. */
 UA_EXPORT UA_Logger Logger_Stdout_new(void);

+ 101 - 181
examples/networklayer_tcp.c

@@ -3,11 +3,11 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 
-#ifdef NOT_AMALGATED
-# define _XOPEN_SOURCE 500 //some users need this for some reason
-# include <stdlib.h> // malloc, free
-# include <string.h> // memset
-#endif
+#include "networklayer_tcp.h"
+
+#include <stdlib.h> // malloc, free
+#include <string.h> // memset
+#include <errno.h>
 
 #ifdef _WIN32
 # include <malloc.h>
@@ -26,14 +26,10 @@
 # define CLOSESOCKET(S) close(S)
 #endif
 
-#include "networklayer_tcp.h" // UA_MULTITHREADING is defined in here
 #ifdef UA_MULTITHREADING
 # include <urcu/uatomic.h>
 #endif
 
-/* with a space, so the include is not removed during amalgamation */
-# include <errno.h>
-
 /****************************/
 /* Generic Socket Functions */
 /****************************/
@@ -61,44 +57,50 @@ static UA_StatusCode socket_write(UA_Connection *connection, UA_ByteString *buf,
     return UA_STATUSCODE_GOOD;
 }
 
+static void socket_close(UA_Connection *connection) {
+    connection->state = UA_CONNECTION_CLOSED;
+    shutdown(connection->sockfd,2);
+    CLOSESOCKET(connection->sockfd);
+}
+
 static UA_StatusCode socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeout) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-	if(response->data == NULL)
-        retval = connection->getBuffer(connection, response, connection->localConf.recvBufferSize);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
+    response->data = malloc(connection->localConf.recvBufferSize);
+    if(!response->data) {
+        UA_ByteString_init(response);
+        return UA_STATUSCODE_GOOD; /* not enough memory retry */
+    }
     struct timeval tmptv = {0, timeout * 1000};
     if(0 != setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tmptv, sizeof(struct timeval))){
+		free(response->data);
+        UA_ByteString_init(response);
+        socket_close(connection);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
-    int ret = recv(connection->sockfd, (char*)response->data, response->length, 0);
+    int ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
 	if(ret == 0) {
-		connection->releaseBuffer(connection, response);
-		connection->close(connection);
-		return UA_STATUSCODE_BADCONNECTIONCLOSED;
+		free(response->data);
+        UA_ByteString_init(response);
+        socket_close(connection);
+        return UA_CONNECTION_CLOSED; /* ret == 0 -> server has closed the connection */
 	} else if(ret < 0) {
+        free(response->data);
+        UA_ByteString_init(response);
 #ifdef _WIN32
 		if(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK) {
 #else
-		if (errno == EAGAIN) {
+		if(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
 #endif
-            return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+            return UA_STATUSCODE_GOOD; /* retry */
+        } else {
+            socket_close(connection);
+            return UA_STATUSCODE_BADCONNECTIONCLOSED;
         }
-        connection->releaseBuffer(connection, response);
-		connection->close(connection);
-        return UA_STATUSCODE_BADCONNECTIONCLOSED;
     }
     response->length = ret;
     *response = UA_Connection_completeMessages(connection, *response);
     return UA_STATUSCODE_GOOD;
 }
 
-static void socket_close(UA_Connection *connection) {
-    connection->state = UA_CONNECTION_CLOSED;
-    shutdown(connection->sockfd,2);
-    CLOSESOCKET(connection->sockfd);
-}
-
 static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
 #ifdef _WIN32
     u_long iMode = 1;
@@ -112,6 +114,11 @@ static UA_StatusCode socket_set_nonblocking(UA_Int32 sockfd) {
     return UA_STATUSCODE_GOOD;
 }
 
+static void FreeConnectionCallback(UA_Server *server, void *ptr) {
+    UA_Connection_deleteMembers((UA_Connection*)ptr);
+     free(ptr);
+ }
+
 /***************************/
 /* Server NetworkLayer TCP */
 /***************************/
@@ -152,7 +159,6 @@ typedef struct {
     /* config */
     UA_Logger *logger;
     UA_UInt32 port;
-    UA_String discoveryUrl;
     UA_ConnectionConfig conf; /* todo: rename to localconf. */
 
 #ifndef UA_MULTITHREADING
@@ -168,17 +174,11 @@ typedef struct {
         UA_Connection *connection;
         UA_Int32 sockfd;
     } *mappings;
-
-    /* to-be-deleted connections */
-    struct DeleteList {
-        struct DeleteList *next;
-        UA_Connection *connection;
-    } *deletes;
 } ServerNetworkLayerTCP;
 
-static UA_StatusCode ServerNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
+static UA_StatusCode ServerNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf) {
 #ifdef UA_MULTITHREADING
-    return UA_ByteString_newMembers(buf, minSize);
+    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
 #else
     ServerNetworkLayerTCP *layer = connection->handle;
     *buf = layer->buffer;
@@ -222,23 +222,8 @@ static void ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
         return;
     connection->state = UA_CONNECTION_CLOSED;
 #endif
-    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) {
-        d->next = layer->deletes;
-        if(uatomic_cmpxchg(&layer->deletes, d->next, d) == d->next)
-            break;
-    }
-#else
-    d->next = layer->deletes;
-    layer->deletes = d;
-#endif
+    shutdown(connection->sockfd, 2); /* only shut down here. this triggers the select, where the socket
+                                        is closed in the main thread */
 }
 
 /* call only from the single networking thread */
@@ -267,7 +252,8 @@ static UA_StatusCode ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, UA_Logger *logger) {
+static UA_StatusCode ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_Logger *logger) {
+    ServerNetworkLayerTCP *layer = nl->handle;
     layer->logger = logger;
 #ifdef _WIN32
     if((layer->serversockfd = socket(PF_INET, SOCK_STREAM,0)) == (UA_Int32)INVALID_SOCKET) {
@@ -301,51 +287,23 @@ static UA_StatusCode ServerNetworkLayerTCP_start(ServerNetworkLayerTCP *layer, U
     socket_set_nonblocking(layer->serversockfd);
     listen(layer->serversockfd, MAXBACKLOG);
     UA_LOG_INFO((*layer->logger), UA_LOGCATEGORY_COMMUNICATION, "Listening on %.*s",
-                layer->discoveryUrl.length, layer->discoveryUrl.data);
+                nl->discoveryUrl.length, nl->discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
 }
 
-/* delayed callback that frees old connections */
-static void freeConnections(UA_Server *server, struct DeleteList *d) {
-    while(d) {
-        UA_Connection_deleteMembers(d->connection);
-        free(d->connection);
-        struct DeleteList *old = d;
-        d = d->next;
-        free(old);
-    }
-}
-
-/* remove the closed sockets from the mappings array */
-static void removeMappings(ServerNetworkLayerTCP *layer, struct DeleteList *d) {
-    while(d) {
-        size_t i = 0;
-        for(; i < layer->mappingsSize; i++) {
-            if(layer->mappings[i].sockfd == d->connection->sockfd)
-                break;
-        }
-        if(i >= layer->mappingsSize)
-            continue;
-        layer->mappingsSize--;
-        layer->mappings[i] = layer->mappings[layer->mappingsSize];
-        d = d->next;
-    }
-}
-
-static UA_Int32 ServerNetworkLayerTCP_getJobs(ServerNetworkLayerTCP *layer, UA_Job **jobs, UA_UInt16 timeout) {
-    /* remove the deleted sockets from the array */
-    struct DeleteList *deletes;
-#ifdef UA_MULTITHREADING
-        deletes = uatomic_xchg(&layer->deletes, NULL);
-#else
-        deletes = layer->deletes;
-        layer->deletes = NULL;
-#endif
-    removeMappings(layer, deletes);
-
+static UA_Int32 ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
+    ServerNetworkLayerTCP *layer = nl->handle;
     setFDSet(layer);
     struct timeval tmptv = {0, timeout};
-    UA_Int32 resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+    UA_Int32 resultsize;
+ repeat_select:
+    resultsize = select(layer->highestfd+1, &layer->fdset, NULL, NULL, &tmptv);
+    if(resultsize < 0) {
+        if(errno == EINTR)
+            goto repeat_select;
+        *jobs = NULL;
+        return resultsize;
+    }
 
     /* accept new connections (can only be a single one) */
     if(FD_ISSET(layer->serversockfd, &layer->fdset)) {
@@ -355,40 +313,18 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(ServerNetworkLayerTCP *layer, UA_J
         int newsockfd = accept(layer->serversockfd, (struct sockaddr *) &cli_addr, &cli_len);
         int i = 1;
         setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
-        if (newsockfd >= 0) {
+        if(newsockfd >= 0) {
             socket_set_nonblocking(newsockfd);
             ServerNetworkLayerTCP_add(layer, newsockfd);
         }
     }
 
-    if(!deletes && resultsize <= 0) {
-        *jobs = NULL;
+    /* alloc enough space for a cleanup-connection and free-connection job per resulted socket */
+    if(resultsize == 0)
+        return 0;
+    UA_Job *js = malloc(sizeof(UA_Job) * resultsize * 2);
+    if(!js)
         return 0;
-    }
-    if(resultsize < 0)
-        resultsize = 0;
-    UA_Int32 deletesJob = 0;
-    if(deletes)
-        deletesJob = 1;
-        
-    UA_Job *items = malloc(sizeof(UA_Job) * (resultsize + deletesJob));
-    if(deletes && !items) {
-        /* abort. reattach the deletes so that they get deleted eventually. */
-#ifdef UA_MULTITHREADING
-        struct DeleteList *last_delete;
-        while(deletes) {
-            last_delete = deletes;
-            deletes = deletes->next;
-        }
-        while(1) {
-            last_delete->next = layer->deletes;
-            if(uatomic_cmpxchg(&layer->deletes, last_delete->next, deletes) == last_delete->next)
-                break;
-        }
-#else
-        layer->deletes = deletes;
-#endif
-    }
 
     /* read from established sockets */
     UA_Int32 j = 0;
@@ -396,72 +332,55 @@ static UA_Int32 ServerNetworkLayerTCP_getJobs(ServerNetworkLayerTCP *layer, UA_J
     for(size_t i = 0; i < layer->mappingsSize && j < resultsize; i++) {
         if(!(FD_ISSET(layer->mappings[i].sockfd, &layer->fdset)))
             continue;
-        if(!buf.data) {
-            buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
-            buf.length = layer->conf.recvBufferSize;
-            if(!buf.data)
-                break;
-        }
         if(socket_recv(layer->mappings[i].connection, &buf, 0) == UA_STATUSCODE_GOOD) {
-            items[j].type = UA_JOBTYPE_BINARYMESSAGE;
-            items[j].job.binaryMessage.message = buf;
-            items[j].job.binaryMessage.connection = layer->mappings[i].connection;
-            buf.data = NULL;
+            if(!buf.data)
+                continue;
+            js[j].type = UA_JOBTYPE_BINARYMESSAGE;
+            js[j].job.binaryMessage.message = buf;
+            js[j].job.binaryMessage.connection = layer->mappings[i].connection;
         } else {
-            items[j].type = UA_JOBTYPE_CLOSECONNECTION;
-            items[j].job.closeConnection = layer->mappings[i].connection;
+            UA_Connection *c = layer->mappings[i].connection;
+            /* the socket is already closed */
+            js[j].type = UA_JOBTYPE_DETACHCONNECTION;
+            js[j].job.closeConnection = layer->mappings[i].connection;
+            layer->mappings[i] = layer->mappings[layer->mappingsSize-1];
+            layer->mappingsSize--;
+            j++;
+            i--; // iterate over the same index again
+            js[j].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+            js[j].job.methodCall.method = FreeConnectionCallback;
+            js[j].job.methodCall.data = c;
         }
         j++;
     }
 
-    /* add the delayed job that frees the connections */
-    if(deletes) {
-        items[j].type = UA_JOBTYPE_DELAYEDMETHODCALL;
-        items[j].job.methodCall.data = deletes;
-        items[j].job.methodCall.method = (void (*)(UA_Server *server, void *data))freeConnections;
-        j++;
-    }
-
-    if(buf.data)
-        free(buf.data);
-
-    /* free the array if there is no job */
-    if(j == 0) {
-        free(items);
-        *jobs = NULL;
-    } else
-        *jobs = items;
+    *jobs = js;
     return j;
 }
 
-static UA_Int32 ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Job **jobs) {
-    struct DeleteList *deletes;
-#ifdef UA_MULTITHREADING
-        deletes = uatomic_xchg(&layer->deletes, NULL);
-#else
-        deletes = layer->deletes;
-        layer->deletes = NULL;
-#endif
-    removeMappings(layer, deletes);
-    UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize);
+static UA_Int32 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
+    ServerNetworkLayerTCP *layer = nl->handle;
+    UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
     if(!items)
         return 0;
     for(size_t i = 0; i < layer->mappingsSize; i++) {
-        items[i].type = UA_JOBTYPE_CLOSECONNECTION;
-        items[i].job.closeConnection = layer->mappings[i].connection;
+        socket_close(layer->mappings[i].connection);
+        items[i*2].type = UA_JOBTYPE_DETACHCONNECTION;
+        items[i*2].job.closeConnection = layer->mappings[i].connection;
+        items[(i*2)+1].type = UA_JOBTYPE_DELAYEDMETHODCALL;
+        items[(i*2)+1].job.methodCall.method = FreeConnectionCallback;
+        items[(i*2)+1].job.methodCall.data = layer->mappings[i].connection;
     }
 #ifdef _WIN32
     WSACleanup();
 #endif
     *jobs = items;
-    return layer->mappingsSize;
+    return layer->mappingsSize*2;
 }
 
 /* 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);
+static void ServerNetworkLayerTCP_deleteMembers(UA_ServerNetworkLayer *nl) {
+    ServerNetworkLayerTCP *layer = nl->handle;
 #ifndef UA_MULTITHREADING
     UA_ByteString_deleteMembers(&layer->buffer);
 #endif
@@ -489,21 +408,19 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
     layer->mappingsSize = 0;
     layer->mappings = NULL;
     layer->port = port;
-    layer->deletes = NULL;
     char hostname[256];
     gethostname(hostname, 255);
-    UA_String_copyprintf("opc.tcp://%s:%d", &layer->discoveryUrl, hostname, port);
+    UA_String_copyprintf("opc.tcp://%s:%d", &nl.discoveryUrl, hostname, port);
 
 #ifndef UA_MULTITHREADING
     layer->buffer = (UA_ByteString){.length = conf.maxMessageSize, .data = malloc(conf.maxMessageSize)};
 #endif
 
-    nl.nlHandle = layer;
-    nl.start = (UA_StatusCode (*)(void*, UA_Logger *logger))ServerNetworkLayerTCP_start;
-    nl.getJobs = (UA_Int32 (*)(void*, UA_Job**, UA_UInt16))ServerNetworkLayerTCP_getJobs;
-    nl.stop = (UA_Int32 (*)(void*, UA_Job**))ServerNetworkLayerTCP_stop;
-    nl.free = (void (*)(void*))ServerNetworkLayerTCP_delete;
-    nl.discoveryUrl = &layer->discoveryUrl;
+    nl.handle = layer;
+    nl.start = ServerNetworkLayerTCP_start;
+    nl.getJobs = ServerNetworkLayerTCP_getJobs;
+    nl.stop = ServerNetworkLayerTCP_stop;
+    nl.deleteMembers = ServerNetworkLayerTCP_deleteMembers;
     return nl;
 }
 
@@ -511,12 +428,12 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
 /* Client NetworkLayer TCP */
 /***************************/
 
-static UA_StatusCode ClientNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf, size_t minSize) {
-#ifdef UA_MULTITHREADING
+static UA_StatusCode ClientNetworkLayerGetBuffer(UA_Connection *connection, UA_ByteString *buf) {
+#ifndef UA_MULTITHREADING
     *buf = *(UA_ByteString*)connection->handle;
     return UA_STATUSCODE_GOOD;
 #else
-    return UA_ByteString_newMembers(buf, minSize);
+    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
 #endif
 }
 
@@ -527,6 +444,9 @@ static void ClientNetworkLayerReleaseBuffer(UA_Connection *connection, UA_ByteSt
 }
 
 static void ClientNetworkLayerClose(UA_Connection *connection) {
+    if(connection->state == UA_CONNECTION_CLOSED)
+        return;
+    connection->state = UA_CONNECTION_CLOSED;
     socket_close(connection);
 #ifndef UA_MULTITHREADING
     UA_ByteString_delete(connection->handle);

+ 1 - 6
examples/networklayer_tcp.h

@@ -10,17 +10,12 @@
 extern "C" {
 #endif
 
-#ifdef NOT_AMALGATED
 #include "ua_server.h"
 #include "ua_client.h"
-#else
-#include "open62541.h"
-#endif
 
 /** @brief Create the TCP networklayer and listen to the specified port */
 UA_EXPORT UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UInt32 port);
-UA_EXPORT UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig conf, char *endpointUrl,
-                                                      UA_Logger *logger);
+UA_EXPORT UA_Connection ClientNetworkLayerTCP_connect(UA_ConnectionConfig conf, char *endpointUrl, UA_Logger *logger);
 
 #ifdef __cplusplus
 } // extern "C"

+ 46 - 100
examples/server.c

@@ -3,15 +3,25 @@
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
 
+#ifdef UA_NO_AMALGAMATION
+# include <time.h>
+# include "ua_types.h"
+# include "ua_server.h"
+# include "logger_stdout.h"
+# include "networklayer_tcp.h"
+#else
+# include "open62541.h"
+#endif
+
 #include <signal.h>
 #include <errno.h> // errno, EINTR
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #ifdef _MSC_VER
-    #include <io.h> //access
+# include <io.h> //access
 #else
-    #include <unistd.h> //access
+# include <unistd.h> //access
 #endif
 
 #define __USE_XOPEN2K
@@ -19,16 +29,6 @@
 # include <pthread.h>
 #endif
 
-#ifdef NOT_AMALGATED
-# include <time.h>
-# include "ua_types.h"
-# include "ua_server.h"
-# include "logger_stdout.h"
-# include "networklayer_tcp.h"
-#else
-# include "open62541.h"
-#endif
-
 /****************************/
 /* Server-related variables */
 /****************************/
@@ -39,8 +39,7 @@ UA_Logger logger;
 /*************************/
 /* Read-only data source */
 /*************************/
-static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp,
-                                  const UA_NumericRange *range, 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;
@@ -53,8 +52,6 @@ static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp,
 	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;
@@ -63,18 +60,12 @@ static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp,
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseTimeData(void *handle, UA_DataValue *value) {
-    if(value->hasValue)
-        UA_DateTime_delete((UA_DateTime*)value->value.data);
-}
-
 /*****************************/
 /* Read-only CPU temperature */
 /*      Only on Linux        */
 /*****************************/
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp,
-                                     const UA_NumericRange *range, 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;
@@ -104,11 +95,6 @@ static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp,
 	return UA_STATUSCODE_GOOD;
 }
 
-static void releaseTemperature(void *handle, UA_DataValue *value) {
-    if(value->hasValue)
-        UA_Double_delete((UA_Double*)value->value.data);
-}
-
 /*************************/
 /* Read-write status led */
 /*************************/
@@ -119,25 +105,13 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
-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;
-    }
+static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
+    if(range)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
-	/* In order to reduce blocking time, we could alloc memory for every read
-       and return a copy of the data. */
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_rdlock(&writeLock);
-#endif
-	value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
-	value->value.arrayLength = -1;
-	value->value.data = &ledStatus;
-	value->value.arrayDimensionsSize = -1;
-	value->value.arrayDimensions = NULL;
-	value->hasValue = UA_TRUE;
+    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
 	if(sourceTimeStamp) {
 		value->sourceTimestamp = UA_DateTime_now();
 		value->hasSourceTimestamp = UA_TRUE;
@@ -145,18 +119,6 @@ static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp,
 	return UA_STATUSCODE_GOOD;
 }
 
-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;
-	value->value.data = NULL;
-#ifdef UA_MULTITHREADING
-	pthread_rwlock_unlock(&writeLock);
-#endif
-}
-
 static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const UA_NumericRange *range) {
     if(range)
         return UA_STATUSCODE_BADINDEXRANGEINVALID;
@@ -170,12 +132,11 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const
 	if(triggerFile)
 		fseek(triggerFile, 0, SEEK_SET);
 
-	if(ledFile){
-		if(ledStatus == 1){
+	if(ledFile) {
+		if(ledStatus == 1)
 			fprintf(ledFile, "%s", "1");
-		} else {
+		else
 			fprintf(ledFile, "%s", "0");
-		}
 		fflush(ledFile);
 	}
 #ifdef UA_MULTITHREADING
@@ -192,10 +153,7 @@ static void stopHandler(int sign) {
 static UA_ByteString loadCertificate(void) {
 	UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
-	//FIXME: a potential bug of locating the certificate, we need to get the path from the server's config
-	fp=fopen("server_cert.der", "rb");
-
-	if(!fp) {
+	if(!(fp=fopen("server_cert.der", "rb"))) {
         errno = 0; // we read errno also from the tcp layer...
         return certificate;
     }
@@ -220,44 +178,33 @@ int main(int argc, char** argv) {
 	pthread_rwlock_init(&writeLock, 0);
 #endif
 
-	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-	logger = Logger_Stdout_new();
-	UA_Server_setLogger(server, logger);
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    logger = Logger_Stdout_new();
+    UA_Server_setLogger(server, logger);
     UA_ByteString certificate = loadCertificate();
     UA_Server_setServerCertificate(server, certificate);
     UA_ByteString_deleteMembers(&certificate);
 	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
 	// add node with the datetime data source
-	UA_DataSource dateDataSource = (UA_DataSource)
-        {.handle = NULL,
-		.read = readTimeData,
-		.release = releaseTimeData,
-		.write = NULL};
+	UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
 	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));
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
+#ifndef _WIN32
 	//cpu temperature monitoring for linux machines
-	if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))){
+	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};
+		UA_DataSource temperatureDataSource = (UA_DataSource) {.handle = NULL, .read = readTemperature, .write = 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));
+                                            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"))) {
+	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);
@@ -267,19 +214,16 @@ int main(int argc, char** argv) {
             fflush(ledFile);
 
             // add node with the LED status data source
-            UA_DataSource ledStatusDataSource = (UA_DataSource)
-                {.handle = NULL,
-                .read = readLedStatus,
-                .release = releaseLedStatus,
-                .write = writeLedStatus};
+            UA_DataSource ledStatusDataSource = (UA_DataSource) {.handle = NULL, .read = readLedStatus, .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{
+        } else {
             UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "[Raspberry Pi] LED file exist, but I have no access (try to run server with sudo)");
         }
     }
+#endif
 
 	// add a static variable node to the adresspace
     UA_Variant *myIntegerVariant = UA_Variant_new();
@@ -297,13 +241,16 @@ int main(int argc, char** argv) {
    /**************/
 
 #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));
+   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));
+   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_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++) {
@@ -335,16 +282,15 @@ int main(int argc, char** argv) {
 	if(temperatureFile)
 		fclose(temperatureFile);
 
-	if(triggerFile){
+	if(triggerFile) {
 		fseek(triggerFile, 0, SEEK_SET);
 		//setting led mode to default
 		fprintf(triggerFile, "%s", "mmc0");
 		fclose(triggerFile);
 	}
 
-	if(ledFile){
+	if(ledFile)
 		fclose(ledFile);
-	}
 
 #ifdef UA_MULTITHREADING
 	pthread_rwlock_destroy(&writeLock);

+ 6 - 8
examples/server_simple.c

@@ -9,17 +9,15 @@
 #include <errno.h> // errno, EINTR
 #include <string.h>
 
-#ifdef NOT_AMALGATED
-    #include "ua_types.h"
-    #include "ua_server.h"
+#ifdef UA_NO_AMALGAMATION
+# include "ua_types.h"
+# include "ua_server.h"
+# include "logger_stdout.h"
+# include "networklayer_tcp.h"
 #else
-    #include "open62541.h"
+# include "open62541.h"
 #endif
 
-// provided by the user, implementations available in the /examples folder
-#include "logger_stdout.h"
-#include "networklayer_tcp.h"
-
 UA_Boolean running = 1;
 UA_Logger logger;
 

+ 23 - 2
include/ua_client.h

@@ -5,11 +5,12 @@
 extern "C" {
 #endif
 
-#include "ua_util.h"
+#include "ua_config.h"
 #include "ua_types.h"
 #include "ua_connection.h"
 #include "ua_log.h"
 #include "ua_types_generated.h"
+#include "queue.h"
 
 struct UA_Client;
 typedef struct UA_Client UA_Client;
@@ -57,7 +58,27 @@ UA_AddReferencesResponse UA_EXPORT
 UA_DeleteNodesResponse UA_EXPORT UA_Client_deleteNodes(UA_Client *client, UA_DeleteNodesRequest *request);
 UA_DeleteReferencesResponse UA_EXPORT
     UA_Client_deleteReferences(UA_Client *client, UA_DeleteReferencesRequest *request);
-    
+
+
+/* Client-Side Macro/Procy functions */
+UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectNode(  UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+
+UA_AddNodesResponse UA_EXPORT *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, 
+                                                            UA_NodeId dataType, UA_Variant *value );
+
+UA_AddNodesResponse UA_EXPORT *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
+                                                            UA_LocalizedText inverseName );
+
+UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 45 - 28
include/ua_config.h.in

@@ -1,46 +1,63 @@
 #ifndef UA_CONFIG_H_
 #define UA_CONFIG_H_
 
-/* Build options and configuration (set by cmake) */
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+#endif
 
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
 #cmakedefine UA_MULTITHREADING
 
 /* Function Export */
 #ifdef _WIN32
-#  ifdef UA_DYNAMIC_LINKING
-#    ifdef __GNUC__
-#      define UA_EXPORT __attribute__ ((dllexport))
-#    else
-#      define UA_EXPORT __declspec(dllexport)
-#    endif
+# ifdef UA_DYNAMIC_LINKING
+#  ifdef __GNUC__
+#   define UA_EXPORT __attribute__ ((dllexport))
 #  else
-#    ifdef __GNUC__
-#      define UA_EXPORT __attribute__ ((dllexport))
-#    else
-#      define UA_EXPORT __declspec(dllimport)
-#    endif
+#   define UA_EXPORT __declspec(dllexport)
 #  endif
-#else
-#  if __GNUC__ || __clang__
-#    define UA_EXPORT __attribute__ ((visibility ("default")))
+# else
+#  ifdef __GNUC__
+#   define UA_EXPORT __attribute__ ((dllexport))
 #  else
-#    define UA_EXPORT
+#   define UA_EXPORT __declspec(dllimport)
 #  endif
+# endif
+#else
+# if __GNUC__ || __clang__
+#  define UA_EXPORT __attribute__ ((visibility ("default")))
+# else
+#  define UA_EXPORT
+# 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)
-
-#if defined(htole16) || defined(htole32) || defined(htole64) || \
-    defined(le16toh) || defined(le32toh) || defined(le64toh)
-#define UA_NON_LITTLEENDIAN_ARCHITECTURE
+/* Endianness */
+#if defined(__linux__) || defined(__APPLE__)
+# include <endian.h>
+# if ( __BYTE_ORDER != __LITTLE_ENDIAN )
+#  define UA_NON_LITTLEENDIAN_ARCHITECTURE
+# endif
 #endif
 
+/* Force non-little endian manually by setting the following. */
+// #define UA_NON_LITTLEENDIAN_ARCHITECTURE
+// #define htole16(x) {...}(x)
+// #define htole32(x) {...}(x)
+// #define htole64(x) {...}(x)
+// #define le16toh(x) {...}(x)
+// #define le32toh(x) {...}(x)
+// #define le64toh(x) {...}(x)
+
+/* Mixed Endian */
+#ifdef __ARM_ARCH_4T__
+# define UA_MIXED_ENDIAN
+# define UA_NON_LITTLEENDIAN_ARCHITECTURE
+#endif
+
+/* Aligned Memory Access */
+#if defined(__arm__) && !defined(__ARM_FEATURE_UNALIGNED)
+# define UA_ALIGNED_MEMORY_ACCESS
+#endif
+
+
 #endif /* UA_CONFIG_H_ */

+ 1 - 1
include/ua_connection.h

@@ -64,7 +64,7 @@ struct UA_Connection {
     UA_Int32 sockfd; ///> Most connectivity solutions run on sockets. Having the socket id here simplifies the design.
     void *handle; ///> A pointer to the networklayer
     UA_ByteString incompleteMessage; ///> Half-received messages (tcp is a streaming protocol) get stored here
-    UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf, size_t minSize); ///> Attach the data array to the buffer. Fails if minSize is larger than remoteConf allows
+    UA_StatusCode (*getBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Get a buffer of the maximum remote recv size
     void (*releaseBuffer)(UA_Connection *connection, UA_ByteString *buf); ///> Release the buffer manually
     /**
      * Sends a message over the connection.

+ 17 - 32
include/ua_server.h

@@ -20,6 +20,7 @@
 extern "C" {
 #endif
 
+#include "ua_config.h"
 #include "ua_types.h"
 #include "ua_types_generated.h"
 #include "ua_nodeids.h"
@@ -81,18 +82,15 @@ UA_StatusCode UA_EXPORT UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThr
 UA_StatusCode UA_EXPORT UA_Server_run_mainloop(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
+ * Datasources are the interface to local data providers. It is expected that
+ * the read and release callbacks are implemented. The write callback can be set
  * to null.
  */
 typedef struct {
     void *handle; ///> A custom pointer to reuse the same datasource functions for multiple sources
 
     /**
-     * Read current data from the data source
+     * Copies the data from the source into the provided value.
      *
      * @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
@@ -104,16 +102,7 @@ typedef struct {
      * @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 (*read)(void *handle, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value);
 
     /**
      * Write into a data source. The write member of UA_DataSource can be empty if the operation
@@ -157,7 +146,7 @@ UA_Server_AddMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
 typedef struct {
     enum {
         UA_JOBTYPE_NOTHING,
-        UA_JOBTYPE_CLOSECONNECTION,
+        UA_JOBTYPE_DETACHCONNECTION,
         UA_JOBTYPE_BINARYMESSAGE,
         UA_JOBTYPE_METHODCALL,
         UA_JOBTYPE_DELAYEDMETHODCALL,
@@ -212,48 +201,44 @@ UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid j
  * in parallel but only sequentially from the server's main loop. So the network
  * layer does not need to be thread-safe.
  */
-typedef struct {
-    void *nlHandle;
+typedef struct UA_ServerNetworkLayer {
+    void *handle;
+    UA_String discoveryUrl;
 
     /**
      * Starts listening on the the networklayer.
      *
+     * @param nl The network layer
+     * @param logger The logger
      * @return Returns UA_STATUSCODE_GOOD or an error code.
      */
-    UA_StatusCode (*start)(void *nlHandle, UA_Logger *logger);
+    UA_StatusCode (*start)(struct UA_ServerNetworkLayer *nl, UA_Logger *logger);
     
     /**
      * Gets called from the main server loop and returns the jobs (accumulated messages and close
      * events) for dispatch.
      *
+     * @param nl The network layer
      * @param jobs When the returned integer is positive, *jobs points to an array of UA_Job of the
      * returned size.
-     *
      * @param timeout The timeout during which an event must arrive in microseconds
-     
      * @return The size of the jobs array. If the result is negative, an error has occurred.
      */
-    UA_Int32 (*getJobs)(void *nlhandle, UA_Job **jobs, UA_UInt16 timeout);
+    UA_Int32 (*getJobs)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout);
 
     /**
      * Closes the network connection and returns all the jobs that need to be finished before the
      * network layer can be safely deleted.
      *
+     * @param nl The network layer
      * @param jobs When the returned integer is positive, jobs points to an array of UA_Job of the
      * returned size.
-     *
      * @return The size of the jobs array. If the result is negative, an error has occurred.
      */
-    UA_Int32 (*stop)(void *nlhandle, UA_Job **jobs);
+    UA_Int32 (*stop)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs);
 
     /** Deletes the network layer. Call only after a successful shutdown. */
-    void (*free)(void *nlhandle);
-
-    /**
-     * String containing the discovery URL that will be add to the server's list
-     * contains the protocol the host and the port of the layer
-     */
-    UA_String* discoveryUrl;
+    void (*deleteMembers)(struct UA_ServerNetworkLayer *nl);
 } UA_ServerNetworkLayer;
 
 /**

+ 69 - 98
include/ua_types.h

@@ -25,39 +25,6 @@ extern "C" {
 #include <stddef.h>
 #include "ua_config.h"
 
-/**
- * @defgroup types Datatypes
- *
- * @brief The built-in datatypes. The remaining datatypes are autogenerated from
- * XML descriptions as they are all enums or structures made up of the built-in
- * datatypes.
- *
- * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
- * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
- *
- * - _new: Allocates the memory for the type and runs _init on the returned
- *   variable. Returns null if no memory could be allocated.
- *
- * - _init: Sets all members to a "safe" standard, usually zero. Arrays (e.g.
- *   for strings) are set to a length of -1.
- *
- * - _copy: Copies a datatype. This performs a deep copy that iterates over the
- *    members. Copying into variants with an external data source is not
- *    permitted. If copying fails, a deleteMembers is performed and an error
- *    code returned.
- *
- * - _delete: Frees the memory where the datatype was stored. This performs an
- *   _deleteMembers internally if required.
- *
- * - _deleteMembers: Frees the memory of dynamically sized members (e.g. a
- *   string) of a datatype. This is useful when the datatype was allocated on
- *   the stack, whereas the dynamically sized members is heap-allocated. To
- *   reuse the variable, the remaining members (not dynamically allocated) need
- *   to be cleaned up with an _init.
- *
- * @{
- */
-
 /** @brief A two-state logical value (true or false). */
 typedef bool UA_Boolean;
 #define UA_TRUE true
@@ -111,8 +78,8 @@ typedef double UA_Double;
 
 /** @brief A sequence of Unicode characters. */
 typedef struct {
-    UA_Int32 length;
-    UA_Byte *data;
+    UA_Int32 length; ///< The length of the string
+    UA_Byte *data; ///< The string's content (not null-terminated)
 } UA_String;
 
 /** @brief An instance in time.
@@ -138,26 +105,26 @@ typedef UA_String UA_XmlElement;
 /** @brief An identifier for a node in the address space of an OPC UA Server. */
 /* The shortened numeric types are introduced during encoding. */
 typedef struct {
-    UA_UInt16 namespaceIndex;
-    enum {
+    UA_UInt16 namespaceIndex; ///< The namespace index of the node
+    enum UA_NodeIdType {
         UA_NODEIDTYPE_NUMERIC    = 2,
         UA_NODEIDTYPE_STRING     = 3,
         UA_NODEIDTYPE_GUID       = 4,
         UA_NODEIDTYPE_BYTESTRING = 5
-    } identifierType;
+    } identifierType; ///< The type of the node identifier
     union {
         UA_UInt32     numeric;
         UA_String     string;
         UA_Guid       guid;
         UA_ByteString byteString;
-    } identifier;
+    } identifier; ///< The node identifier
 } UA_NodeId;
 
 /** @brief A NodeId that allows the namespace URI to be specified instead of an index. */
 typedef struct {
-    UA_NodeId nodeId;
-    UA_String namespaceUri; // not encoded if length=-1
-    UA_UInt32 serverIndex;  // not encoded if 0
+    UA_NodeId nodeId; ///< The nodeid without extensions
+    UA_String namespaceUri; ///< The Uri of the namespace (tindex in the nodeId is ignored)
+    UA_UInt32 serverIndex;  ///< The index of the server
 } UA_ExpandedNodeId;
 
 #include "ua_statuscodes.h"
@@ -166,43 +133,31 @@ typedef enum UA_StatusCode UA_StatusCode; // StatusCodes aren't an enum(=int) si
 
 /** @brief A name qualified by a namespace. */
 typedef struct {
-    UA_UInt16 namespaceIndex;
-    UA_String name;
+    UA_UInt16 namespaceIndex; ///< The namespace index
+    UA_String name; ///< The actual name
 } UA_QualifiedName;
 
 /** @brief Human readable text with an optional locale identifier. */
 typedef struct {
-    UA_String locale;
-    UA_String text;
+    UA_String locale; ///< The locale (e.g. "en-US")
+    UA_String text; ///< The actual text
 } UA_LocalizedText;
 
 /** @brief A structure that contains an application specific data type that may
     not be recognized by the receiver. */
 typedef struct {
-    UA_NodeId typeId;
+    UA_NodeId typeId; ///< The nodeid of the datatype
     enum {
         UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED  = 0,
         UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING = 1,
         UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISXML        = 2
-    } encoding;
-    UA_ByteString body; // contains either the bytestring or a pointer to the memory-object
+    } encoding; ///< The encoding of the contained data
+    UA_ByteString body; ///< The bytestring of the encoded data
 } UA_ExtensionObject;
 
 struct UA_DataType;
 typedef struct UA_DataType UA_DataType; 
 
-/** @brief NumericRanges are used select a subset in a (multidimensional) variant array.
-    NumericRange has no official type structure in the standard. Officially, it only exists as an
-    encoded string, such as "1:2,0:3,5". The colon separates min/max index and the comma separates
-    dimensions. A single value indicates a range with a single element (min==max). */
-typedef struct {
-    UA_Int32 dimensionsSize;
-    struct UA_NumericRangeDimension {
-        UA_UInt32 min;
-        UA_UInt32 max;
-    } *dimensions;
-} UA_NumericRange;
-
 /** @brief Variants store (arrays of) any data type. Either they provide a pointer to the data in
  *   memory, or functions from which the data can be accessed. Variants are replaced together with
  *   the data they store (exception: use a data source).
@@ -215,14 +170,14 @@ typedef struct {
  */
 
 typedef struct {
-    const UA_DataType *type;
+    const UA_DataType *type; ///< The nodeid of the datatype
     enum {
         UA_VARIANT_DATA, ///< The data is "owned" by this variant (copied and deleted together)
         UA_VARIANT_DATA_NODELETE, /**< The data is "borrowed" by the variant and shall not be
                                        deleted at the end of this variant's lifecycle. It is not
                                        possible to overwrite borrowed data due to concurrent access.
                                        Use a custom datasource with a mutex. */
-    } storageType;
+    } storageType; ///< Shall the data be deleted together with the variant
     UA_Int32  arrayLength;  ///< the number of elements in the data-pointer
     void     *data; ///< points to the scalar or array data
     UA_Int32  arrayDimensionsSize; ///< the number of dimensions the data-array has
@@ -322,18 +277,15 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
     allocated. If the memory cannot be allocated, a null-string is returned. */
 UA_String UA_EXPORT UA_String_fromChars(char const src[]);
 
-#define UA_STRING_ALLOC(CHARS) UA_String_fromChars(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, ...);
+#define UA_STRING(CHARS) (UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
+#define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
 
 /** Compares two strings */
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
 
-/** Compares an UA String with a char array */
-UA_Boolean UA_EXPORT UA_String_equalchars(const UA_String *string1, char *charString);
+/** 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, ...);
 
 /* DateTime */
 /** Returns the current time */
@@ -387,27 +339,27 @@ UA_NodeId UA_EXPORT UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char con
 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_NUMERIC(NSID, NUMERICID) UA_NodeId_fromInteger(NSID, NUMERICID)
+#define UA_NODEID_STRING(NSID, CHARS) UA_NodeId_fromCharString(NSID, CHARS)
+#define UA_NODEID_STRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharStringCopy(NSID, CHARS)
+#define UA_NODEID_GUID(NSID, GUID) UA_NodeId_fromGuid(NSID, GUID)
+#define UA_NODEID_BYTESTRING(NSID, CHARS) UA_NodeId_fromCharByteString(NSID, CHARS)
+#define UA_NODEID_BYTESTRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharStringCopy(NSID, CHARS)
 #define UA_NODEID_NULL UA_NODEID_NUMERIC(0,0)
 
 /* ExpandedNodeId */
 UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
 
-#define UA_EXPANDEDNODEID_NUMERIC(NS_INDEX, NUMERICID) (UA_ExpandedNodeId) {            \
-        .nodeId = {.namespaceIndex = NS_INDEX, .identifierType = UA_NODEIDTYPE_NUMERIC, \
+#define UA_EXPANDEDNODEID_NUMERIC(NSID, NUMERICID) (UA_ExpandedNodeId) {            \
+        .nodeId = {.namespaceIndex = NSID, .identifierType = UA_NODEIDTYPE_NUMERIC, \
                    .identifier.numeric = NUMERICID },                                   \
         .serverIndex = 0, .namespaceUri = {.data = (UA_Byte*)0, .length = -1} }
     
 /* QualifiedName */
-#define UA_QUALIFIEDNAME(NS_INDEX, CHARS) (const UA_QualifiedName) {    \
-        .namespaceIndex = NS_INDEX, .name = UA_STRING(CHARS) }
-#define UA_QUALIFIEDNAME_ALLOC(NS_INDEX, CHARS) (UA_QualifiedName) {    \
-        .namespaceIndex = NS_INDEX, .name = UA_STRING_ALLOC(CHARS) }
+#define UA_QUALIFIEDNAME(NSID, CHARS) (const UA_QualifiedName) {    \
+        .namespaceIndex = NSID, .name = UA_STRING(CHARS) }
+#define UA_QUALIFIEDNAME_ALLOC(NSID, CHARS) (UA_QualifiedName) {    \
+        .namespaceIndex = NSID, .name = UA_STRING_ALLOC(CHARS) }
 
 /* LocalizedText */
 #define UA_LOCALIZEDTEXT(LOCALE, TEXT) (const UA_LocalizedText) {       \
@@ -453,11 +405,11 @@ UA_StatusCode UA_EXPORT UA_Variant_setScalarCopy(UA_Variant *v, const void *p, c
  *
  * @param v The variant
  * @param array A pointer to the array data
- * @param noElements The size of the array
+ * @param elements The size of the array
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 noElements,
+UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 elements,
                                             const UA_DataType *type);
 
 /**
@@ -465,16 +417,34 @@ UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32
  *
  * @param v The variant
  * @param array A pointer to the array data
- * @param noElements The size of the array
+ * @param elements The size of the array
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 noElements,
+UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 elements,
                                                 const UA_DataType *type);
 
+/** @brief NumericRanges are used select a subset in a (multidimensional)
+    variant array. NumericRange has no official type structure in the standard.
+    On the wire, it only exists as an encoded string, such as "1:2,0:3,5". The
+    colon separates min/max index and the comma separates dimensions. A single
+    value indicates a range with a single element (min==max). */
+typedef struct {
+    UA_Int32 dimensionsSize;
+    struct UA_NumericRangeDimension {
+        UA_UInt32 min;
+        UA_UInt32 max;
+    } *dimensions;
+} UA_NumericRange;
+
 /**
  * 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.
+ *
+ * @param src The source variant
+ * @param dst The target variant
+ * @param range The range of the copied data
+ * @return Returns UA_STATUSCODE_GOOD or an error code
  */
 UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
 
@@ -485,9 +455,9 @@ UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *
  *
  * @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 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
+ * @return Returns UA_STATUSCODE_GOOD or an error code
  */
 UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize,
                                             const UA_NumericRange range);
@@ -497,9 +467,9 @@ UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_I
  *
  * @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 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
+ * @return Returns UA_STATUSCODE_GOOD or an error code
  */
 UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
                                                 const UA_NumericRange range);
@@ -579,6 +549,8 @@ UA_StatusCode UA_EXPORT UA_copy(const void *src, void *dst, const UA_DataType *d
  */
 void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *dataType);
 
+void UA_EXPORT UA_deleteMembersUntil(void *p, const UA_DataType *dataType, UA_Int32 lastMember);
+
 /**
  * Deletes (frees) a variable and all of its content.
  *
@@ -597,9 +569,10 @@ void UA_EXPORT UA_delete(void *p, const UA_DataType *dataType);
  * Allocates and initializes an array of variables of a specific type
  *
  * @param dataType The datatype description
+ * @param elements The number of elements in the array
  * @return Returns the memory location of the variable or (void*)0 if no memory could be allocated
  */
-void UA_EXPORT * UA_Array_new(const UA_DataType *dataType, UA_Int32 noElements);
+void UA_EXPORT * UA_Array_new(const UA_DataType *dataType, UA_Int32 elements);
 
 /**
  * Allocates and copies an array. dst is set to (void*)0 if not enough memory is available.
@@ -607,21 +580,19 @@ void UA_EXPORT * UA_Array_new(const UA_DataType *dataType, UA_Int32 noElements);
  * @param src The memory location of the source array
  * @param dst The memory location where the pointer to the destination array is written
  * @param dataType The datatype of the array members
- * @param noElements The size of the array
+ * @param elements The size of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, void **dst, const UA_DataType *dataType, UA_Int32 noElements);
+UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, void **dst, const UA_DataType *dataType, UA_Int32 elements);
 
 /**
  * Deletes an array.
  *
- * @param src The memory location of the array
+ * @param p The memory location of the array
  * @param dataType The datatype of the array members
- * @param noElements The size of the array
+ * @param elements The size of the array
  */
-void UA_EXPORT UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 noElements);
-
-/** @} */
+void UA_EXPORT UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 elements);
 
 /* These are not generated from XML. Server *and* client need them. */
 typedef enum {

+ 254 - 64
src/client/ua_client.c

@@ -72,7 +72,7 @@ static UA_StatusCode HelAckHandshake(UA_Client *c) {
     messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_HELF;
 
     UA_TcpHelloMessage hello;
-    UA_String_copy(&c->endpointUrl, &hello.endpointUrl);
+    UA_String_copy(&c->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
 
     UA_Connection *conn = &c->connection;
     hello.maxChunkCount = conn->localConf.maxChunkCount;
@@ -81,37 +81,41 @@ static UA_StatusCode HelAckHandshake(UA_Client *c) {
     hello.receiveBufferSize = conn->localConf.recvBufferSize;
     hello.sendBufferSize = conn->localConf.sendBufferSize;
 
-    messageHeader.messageSize = UA_TcpHelloMessage_calcSizeBinary((UA_TcpHelloMessage const*)&hello) +
-                                UA_TcpMessageHeader_calcSizeBinary((UA_TcpMessageHeader const*)&messageHeader);
     UA_ByteString message;
-    UA_StatusCode retval = c->connection.getBuffer(&c->connection, &message, messageHeader.messageSize);
+    UA_StatusCode retval = c->connection.getBuffer(&c->connection, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    size_t offset = 0;
-    UA_TcpMessageHeader_encodeBinary(&messageHeader, &message, &offset);
-    UA_TcpHelloMessage_encodeBinary(&hello, &message, &offset);
+    size_t offset = 8;
+    retval |= UA_TcpHelloMessage_encodeBinary(&hello, &message, &offset);
+    messageHeader.messageSize = offset;
+    offset = 0;
+    retval |= UA_TcpMessageHeader_encodeBinary(&messageHeader, &message, &offset);
     UA_TcpHelloMessage_deleteMembers(&hello);
+    if(retval != UA_STATUSCODE_GOOD) {
+        c->connection.releaseBuffer(&c->connection, &message);
+        return retval;
+    }
 
     retval = c->connection.write(&c->connection, &message, messageHeader.messageSize);
-    if(retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD) {
         c->connection.releaseBuffer(&c->connection, &message);
-    if(retval)
         return retval;
+    }
 
     UA_ByteString reply;
     UA_ByteString_init(&reply);
     do {
         retval = c->connection.recv(&c->connection, &reply, c->config.timeout);
-        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+        if(retval != UA_STATUSCODE_GOOD)
             return retval;
-    } while(retval != UA_STATUSCODE_GOOD);
+    } while(!reply.data);
 
     offset = 0;
     UA_TcpMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
     UA_TcpAcknowledgeMessage ackMessage;
     retval = UA_TcpAcknowledgeMessage_decodeBinary(&reply, &offset, &ackMessage);
-    c->connection.releaseBuffer(&c->connection, &reply);
+    UA_ByteString_deleteMembers(&reply);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     conn->remoteConf.maxChunkCount = ackMessage.maxChunkCount;
@@ -153,47 +157,43 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
         opnSecRq.securityMode = UA_MESSAGESECURITYMODE_NONE;
     }
 
-    messageHeader.messageHeader.messageSize =
-        UA_SecureConversationMessageHeader_calcSizeBinary(&messageHeader) +
-        UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(&asymHeader) +
-        UA_SequenceHeader_calcSizeBinary(&seqHeader) +
-        UA_NodeId_calcSizeBinary(&requestType) +
-        UA_OpenSecureChannelRequest_calcSizeBinary(&opnSecRq);
-
     UA_ByteString message;
-    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message,
-                                                        messageHeader.messageHeader.messageSize);
+    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
         return retval;
     }
 
-    size_t offset = 0;
-    UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
-    UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
-    UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
-    UA_NodeId_encodeBinary(&requestType, &message, &offset);
-    UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
+    size_t offset = 12;
+    retval = UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &message, &offset);
+    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
+    retval |= UA_NodeId_encodeBinary(&requestType, &message, &offset);
+    retval |= UA_OpenSecureChannelRequest_encodeBinary(&opnSecRq, &message, &offset);
+    messageHeader.messageHeader.messageSize = offset;
+    offset = 0;
+    retval |= UA_SecureConversationMessageHeader_encodeBinary(&messageHeader, &message, &offset);
 
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     UA_OpenSecureChannelRequest_deleteMembers(&opnSecRq);
+    if(retval != UA_STATUSCODE_GOOD) {
+        client->connection.releaseBuffer(&client->connection, &message);
+        return retval;
+    }
 
-    retval = client->connection.write(&client->connection, &message,
-                                      messageHeader.messageHeader.messageSize);
-    if(retval != UA_STATUSCODE_GOOD)
+    retval = client->connection.write(&client->connection, &message, messageHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD) {
         client->connection.releaseBuffer(&client->connection, &message);
-    if(retval)
         return retval;
+    }
 
-    // parse the response
     UA_ByteString reply;
     UA_ByteString_init(&reply);
     do {
         retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+        if(retval != UA_STATUSCODE_GOOD)
             return retval;
-    } while(retval != UA_STATUSCODE_GOOD);
+    } while(!reply.data);
 
     offset = 0;
     UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &messageHeader);
@@ -203,16 +203,18 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     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_ByteString_deleteMembers(&reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
+        UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT,
+                     "Reply answers the wrong request. Expected OpenSecureChannelResponse.");
         return UA_STATUSCODE_BADINTERNALERROR;
     }
 
     UA_OpenSecureChannelResponse response;
     UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
     client->scExpiresAt = UA_DateTime_now() + response.securityToken.revisedLifetime * 10000;
-    client->connection.releaseBuffer(&client->connection, &reply);
+    UA_ByteString_deleteMembers(&reply);
     retval = response.responseHeader.serviceResult;
 
     if(!renew && retval == UA_STATUSCODE_GOOD) {
@@ -224,7 +226,6 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     UA_OpenSecureChannelResponse_deleteMembers(&response);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     return retval;
-
 }
 
 /** If the request fails, then the response is cast to UA_ResponseHeader (at the beginning of every
@@ -252,7 +253,10 @@ static void synchronousRequest(UA_Client *client, void *request, const UA_DataTy
                                                               request, requestType);
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
     if(retval) {
-        respHeader->serviceResult = retval;
+        if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
+            respHeader->serviceResult = UA_STATUSCODE_BADREQUESTTOOLARGE;
+        else
+            respHeader->serviceResult = retval;
         return;
     }
 
@@ -262,11 +266,11 @@ static void synchronousRequest(UA_Client *client, void *request, const UA_DataTy
     UA_ByteString_init(&reply);
     do {
         retval = client->connection.recv(&client->connection, &reply, client->config.timeout);
-        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED) {
+        if(retval != UA_STATUSCODE_GOOD) {
             respHeader->serviceResult = retval;
             return;
         }
-    } while(retval != UA_STATUSCODE_GOOD);
+    } while(!reply.data);
 
     size_t offset = 0;
     UA_SecureConversationMessageHeader msgHeader;
@@ -279,16 +283,30 @@ static void synchronousRequest(UA_Client *client, void *request, const UA_DataTy
     retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
     UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->typeId.identifier.numeric +
                                                  UA_ENCODINGOFFSET_BINARY);
-    if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
-        // Todo: we need to demux responses since a publish responses may come at any time
-        client->connection.releaseBuffer(&client->connection, &reply);
-        UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
-        respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR;
-        return;
-    }
 
+    if(retval != UA_STATUSCODE_GOOD)
+        goto finish;
+
+    /* Todo: we need to demux responses since a publish responses may come at any time */
+    if(!UA_NodeId_equal(&responseId, &expectedNodeId) || seqHeader.requestId != requestId) {
+        if(responseId.identifier.numeric != UA_NS0ID_SERVICEFAULT + UA_ENCODINGOFFSET_BINARY) {
+            UA_LOG_ERROR(client->logger, UA_LOGCATEGORY_CLIENT,
+                         "Reply answers the wrong request. Expected ns=%i,i=%i. But retrieved ns=%i,i=%i",
+                         expectedNodeId.namespaceIndex, expectedNodeId.identifier.numeric,
+                         responseId.namespaceIndex, responseId.identifier.numeric);
+            respHeader->serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+        } else
+            retval = UA_decodeBinary(&reply, &offset, respHeader, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+        goto finish;
+    } 
+    
     retval = UA_decodeBinary(&reply, &offset, response, responseType);
-    client->connection.releaseBuffer(&client->connection, &reply);
+    if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
+        retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
+
+ finish:
+    UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
+    UA_ByteString_deleteMembers(&reply);
     if(retval != UA_STATUSCODE_GOOD)
         respHeader->serviceResult = retval;
 }
@@ -439,28 +457,29 @@ static UA_StatusCode CloseSecureChannel(UA_Client *client) {
 
     UA_NodeId typeId = UA_NODEID_NUMERIC(0, UA_NS0ID_CLOSESECURECHANNELREQUEST + UA_ENCODINGOFFSET_BINARY);
 
-    msgHeader.messageHeader.messageSize =
-        UA_SecureConversationMessageHeader_calcSizeBinary(&msgHeader) +
-        UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symHeader) +
-        UA_SequenceHeader_calcSizeBinary(&seqHeader) +
-        UA_NodeId_calcSizeBinary(&typeId) +
-        UA_calcSizeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
-
     UA_ByteString message;
-    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message,
-                                                        msgHeader.messageHeader.messageSize);
+    UA_StatusCode retval = client->connection.getBuffer(&client->connection, &message);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    size_t offset = 0;
-    retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &message, &offset);
+    size_t offset = 12;
     retval |= UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symHeader, &message, &offset);
     retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &message, &offset);
     retval |= UA_NodeId_encodeBinary(&typeId, &message, &offset);
     retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST], &message, &offset);
-    if(retval == UA_STATUSCODE_GOOD)
-        retval = client->connection.write(&client->connection, &message, msgHeader.messageHeader.messageSize);
-    client->connection.releaseBuffer(&client->connection, &message);
+
+    msgHeader.messageHeader.messageSize = offset;
+    offset = 0;
+    retval |= UA_SecureConversationMessageHeader_encodeBinary(&msgHeader, &message, &offset);
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        client->connection.releaseBuffer(&client->connection, &message);
+        return retval;
+    }
+        
+    retval = client->connection.write(&client->connection, &message, msgHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD)
+        client->connection.releaseBuffer(&client->connection, &message);
     return retval;
 }
 
@@ -487,12 +506,15 @@ UA_StatusCode UA_Client_connect(UA_Client *client, UA_ConnectClientConnection co
         retval = SessionHandshake(client);
     if(retval == UA_STATUSCODE_GOOD)
         retval = ActivateSession(client);
-        
+    if(retval == UA_STATUSCODE_GOOD)
+        client->connection.state = UA_CONNECTION_ESTABLISHED;
     return retval;
 }
 
 UA_StatusCode UA_Client_disconnect(UA_Client *client) {
     UA_StatusCode retval;
+    if(client->channel.connection->state != UA_CONNECTION_ESTABLISHED)
+        return UA_STATUSCODE_GOOD;
     retval = CloseSession(client);
     if(retval == UA_STATUSCODE_GOOD)
         retval = CloseSecureChannel(client);
@@ -922,6 +944,174 @@ void UA_Client_doPublish(UA_Client *client) {
     return;
 }
 
+#endif
 
+/**********************************/
+/* User-Facing Macros-Function    */
+/**********************************/
+#define ADDNODES_COPYDEFAULTATTRIBUTES(REQUEST,ATTRIBUTES) do {                           \
+    ATTRIBUTES.specifiedAttributes = 0;                                                   \
+    if(! UA_LocalizedText_copy(&description, &(ATTRIBUTES.description)))                  \
+        ATTRIBUTES.specifiedAttributes |=  UA_NODEATTRIBUTESMASK_DESCRIPTION;             \
+    if(! UA_LocalizedText_copy(&displayName, &(ATTRIBUTES.displayName)))                  \
+        ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_DISPLAYNAME;              \
+    ATTRIBUTES.userWriteMask       = userWriteMask;                                       \
+    ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERWRITEMASK;                \
+    ATTRIBUTES.writeMask           = writeMask;                                           \
+    ATTRIBUTES.specifiedAttributes |= UA_NODEATTRIBUTESMASK_WRITEMASK;                    \
+    UA_QualifiedName_copy(&browseName, &(REQUEST.nodesToAdd[0].browseName));              \
+    UA_ExpandedNodeId_copy(&parentNodeId, &(REQUEST.nodesToAdd[0].parentNodeId));         \
+    UA_NodeId_copy(&referenceTypeId, &(REQUEST.nodesToAdd[0].referenceTypeId));           \
+    UA_ExpandedNodeId_copy(&typeDefinition, &(REQUEST.nodesToAdd[0].typeDefinition));     \
+    UA_ExpandedNodeId_copy(&reqId, &(REQUEST.nodesToAdd[0].requestedNewNodeId ));         \
+    REQUEST.nodesToAddSize = 1;                                                           \
+    } while(0)
+    
+#define ADDNODES_PACK_AND_SEND(PREQUEST,PATTRIBUTES,PNODETYPE) do {                                                                       \
+    PREQUEST.nodesToAdd[0].nodeAttributes.encoding = UA_EXTENSIONOBJECT_ENCODINGMASK_BODYISBYTESTRING;                                    \
+    PREQUEST.nodesToAdd[0].nodeAttributes.typeId   = UA_NODEID_NUMERIC(0, UA_NS0ID_##PNODETYPE##ATTRIBUTES + UA_ENCODINGOFFSET_BINARY);   \
+    size_t encOffset = 0;                                                                                                                 \
+    UA_ByteString_newMembers(&PREQUEST.nodesToAdd[0].nodeAttributes.body, client->connection.remoteConf.maxMessageSize);                  \
+    UA_encodeBinary(&PATTRIBUTES,&UA_TYPES[UA_TYPES_##PNODETYPE##ATTRIBUTES], &(PREQUEST.nodesToAdd[0].nodeAttributes.body), &encOffset); \
+    PREQUEST.nodesToAdd[0].nodeAttributes.body.length = encOffset;                                                                                 \
+    *(adRes) = UA_Client_addNodes(client, &PREQUEST);                                                                                     \
+    UA_AddNodesRequest_deleteMembers(&PREQUEST);                                                                                          \
+} while(0)
+    
+/* NodeManagement */
+UA_AddNodesResponse *UA_Client_createObjectNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition ) {
+    UA_AddNodesRequest adReq;
+    UA_AddNodesRequest_init(&adReq);
+
+    UA_AddNodesResponse *adRes;
+    adRes = UA_AddNodesResponse_new();
+    UA_AddNodesResponse_init(adRes);
+
+    UA_ObjectAttributes vAtt;
+    UA_ObjectAttributes_init(&vAtt);
+    adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
+    UA_AddNodesItem_init(adReq.nodesToAdd);
+
+    // Default node properties and attributes
+    ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
+    
+    // Specific to objects
+    adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_OBJECT;
+    vAtt.eventNotifier       = 0;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_EVENTNOTIFIER;
 
-#endif
+    ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECT); 
+    
+    return adRes;
+}
+
+UA_AddNodesResponse *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                    UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                    UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, 
+                                                    UA_NodeId dataType, UA_Variant *value) {
+    UA_AddNodesRequest adReq;
+    UA_AddNodesRequest_init(&adReq);
+    
+    UA_AddNodesResponse *adRes;
+    adRes = UA_AddNodesResponse_new();
+    UA_AddNodesResponse_init(adRes);
+    
+    UA_VariableAttributes vAtt;
+    UA_VariableAttributes_init(&vAtt);
+    adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
+    UA_AddNodesItem_init(adReq.nodesToAdd);
+    
+    // Default node properties and attributes
+    ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
+    
+    // Specific to variables
+    adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_VARIABLE;
+    vAtt.accessLevel              = 0;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ACCESSLEVEL;
+    vAtt.userAccessLevel          = 0;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_USERACCESSLEVEL;
+    vAtt.minimumSamplingInterval  = 100;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_MINIMUMSAMPLINGINTERVAL;
+    vAtt.historizing              = UA_FALSE;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_HISTORIZING;
+    
+    if (value != NULL) {
+        UA_Variant_copy(value, &(vAtt.value));
+        vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUE;
+        vAtt.valueRank            = -2;
+        vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_VALUERANK;
+        // These are defined by the variant
+        //vAtt.arrayDimensionsSize  = value->arrayDimensionsSize;
+        //vAtt.arrayDimensions      = NULL;
+    }
+    UA_NodeId_copy(&dataType, &(vAtt.dataType));
+    
+    ADDNODES_PACK_AND_SEND(adReq,vAtt,VARIABLE);
+    
+    return adRes;
+}
+
+UA_AddNodesResponse *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                  UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                  UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
+                                                  UA_LocalizedText inverseName ) {
+    UA_AddNodesRequest adReq;
+    UA_AddNodesRequest_init(&adReq);
+    
+    UA_AddNodesResponse *adRes;
+    adRes = UA_AddNodesResponse_new();
+    UA_AddNodesResponse_init(adRes);
+    
+    UA_ReferenceTypeAttributes vAtt;
+    UA_ReferenceTypeAttributes_init(&vAtt);
+    adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
+    UA_AddNodesItem_init(adReq.nodesToAdd);
+    
+    // Default node properties and attributes
+    ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
+
+    // Specific to referencetypes
+    adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_REFERENCETYPE;
+    UA_LocalizedText_copy(&inverseName, &(vAtt.inverseName));
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_INVERSENAME;
+    vAtt.symmetric = UA_FALSE;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_SYMMETRIC;
+    vAtt.isAbstract = UA_FALSE;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ISABSTRACT;
+    
+    
+    ADDNODES_PACK_AND_SEND(adReq,vAtt,REFERENCETYPE);
+    
+    return adRes;
+}
+
+UA_AddNodesResponse *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                    UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                    UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition) {
+    UA_AddNodesRequest adReq;
+    UA_AddNodesRequest_init(&adReq);
+    
+    UA_AddNodesResponse *adRes;
+    adRes = UA_AddNodesResponse_new();
+    UA_AddNodesResponse_init(adRes);
+    
+    UA_ObjectTypeAttributes vAtt;
+    UA_ObjectTypeAttributes_init(&vAtt);
+    adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
+    UA_AddNodesItem_init(adReq.nodesToAdd);
+    
+    // Default node properties and attributes
+    ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
+    
+    // Specific to referencetypes
+    adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_OBJECTTYPE;
+    vAtt.isAbstract = UA_FALSE;
+    vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ISABSTRACT;
+    
+    
+    ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECTTYPE);
+    
+    return adRes;
+}

+ 10 - 6
src/server/ua_nodestore.c

@@ -245,16 +245,20 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
         if(expand(ns) != UA_STATUSCODE_GOOD)
             return UA_STATUSCODE_BADINTERNALERROR;
     }
-    
     // get a free slot
     struct nodeEntry **slot;
-    if(UA_NodeId_isNull(&node->nodeId)) {
+    //FIXME: a bit dirty workaround of preserving namespace
+    //namespace index is assumed to be valid
+    UA_NodeId tempNodeid;
+    UA_NodeId_copy(&node->nodeId, &tempNodeid);
+    tempNodeid.namespaceIndex = 0;
+    if(UA_NodeId_isNull(&tempNodeid)) {
         // find a unique nodeid that is not taken
         node->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-        node->nodeId.namespaceIndex = 1; // namespace 1 is always in the local nodestore
-        if(node->nodeClass==UA_NODECLASS_VARIABLE){ //set namespaceIndex in browseName in case id is generated
-        	((UA_VariableNode*)node)->browseName.namespaceIndex=node->nodeId.namespaceIndex;
-        }
+
+        if(node->nodeId.namespaceIndex==0) //original request for ns=0 should yield ns=1
+            node->nodeId.namespaceIndex=1;
+
         UA_Int32 identifier = ns->count+1; // start value
         UA_Int32 size = ns->size;
         hash_t increase = mod2(identifier, size);

+ 9 - 2
src/server/ua_nodestore_concurrent.c

@@ -154,7 +154,12 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
         entry->refcount++;
 
     struct cds_lfht_node *result;
-    if(!UA_NodeId_isNull(&node->nodeId)) {
+    //FIXME: a bit dirty workaround of preserving namespace
+    //namespace index is assumed to be valid
+    UA_NodeId tempNodeid;
+    UA_NodeId_copy(&node->nodeId, &tempNodeid);
+    tempNodeid.namespaceIndex = 0;
+    if(!UA_NodeId_isNull(&tempNodeid)) {
         hash_t h = hash(&node->nodeId);
         rcu_read_lock();
         result = cds_lfht_add_unique(ns->ht, h, compare, &entry->node.nodeId, &entry->htn);
@@ -168,10 +173,12 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
     } else {
         /* create a unique nodeid */
         ((UA_Node *)&entry->node)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-        ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1; // namespace 1 is always in the local nodestore
+        if(((UA_Node *)&entry->node)->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1
+            ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1;
         if(((UA_Node *)&entry->node)->nodeClass==UA_NODECLASS_VARIABLE){ //set namespaceIndex in browseName in case id is generated
         	((UA_VariableNode*)&entry->node)->browseName.namespaceIndex=((UA_Node *)&entry->node)->nodeId.namespaceIndex;
         }
+
         unsigned long identifier;
         long before, after;
         rcu_read_lock();

+ 28 - 14
src/server/ua_securechannel_manager.c

@@ -28,9 +28,15 @@ void UA_SecureChannelManager_cleanupTimedOut(UA_SecureChannelManager *cm, UA_Dat
     /* 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 &&
+            (UA_DateTime)entry->channel.securityToken.revisedLifetime*10000 > now &&
             entry->channel.connection) {
             entry = LIST_NEXT(entry, pointers);
+        }else if(entry->channel.nextSecurityToken.tokenId > 0 &&
+                 entry->channel.nextSecurityToken.createdAt +
+                (UA_DateTime)entry->channel.nextSecurityToken.revisedLifetime*10000 > now &&
+                entry->channel.connection){
+            UA_SecureChannel_revolveTokens(&entry->channel);
+            entry = LIST_NEXT(entry, pointers);
         }
         else {
             channel_list_entry *next = LIST_NEXT(entry, pointers);
@@ -99,22 +105,30 @@ UA_StatusCode UA_SecureChannelManager_renew(UA_SecureChannelManager *cm, UA_Conn
     if(channel == UA_NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
 
-    channel->securityToken.tokenId         = cm->lastTokenId++;
-    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 no new security token is already issued
+    if(channel->nextSecurityToken.tokenId == 0){
+        channel->nextSecurityToken.channelId       = channel->securityToken.channelId;
+        //FIXME: UaExpert seems not to use new the new tokenid
+        channel->nextSecurityToken.tokenId         = cm->lastTokenId++;
+        //channel->nextSecurityToken.tokenId         = channel->securityToken.tokenId;
+        channel->nextSecurityToken.createdAt       = UA_DateTime_now();
+        channel->nextSecurityToken.revisedLifetime = (request->requestedLifetime > cm->maxChannelLifetime) ?
+                                                 cm->maxChannelLifetime : request->requestedLifetime;
+
+        //FIXME: pragmatic workaround to get clients requesting lifetime of 0 working
+        if(channel->nextSecurityToken.revisedLifetime == 0){
+            channel->nextSecurityToken.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->clientNonce.data)
+        UA_ByteString_deleteMembers(&channel->clientNonce);
+    UA_ByteString_copy(&request->clientNonce, &channel->clientNonce);
 
-    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);
+    UA_ChannelSecurityToken_copy(&channel->nextSecurityToken, &response->securityToken);
     return UA_STATUSCODE_GOOD;
 }
 

+ 77 - 60
src/server/ua_server.c

@@ -59,6 +59,17 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
     ens->externalNodeStore.destroy(ens->externalNodeStore.ensHandle);
 }
 
+static void UA_Server_deleteExternalNamespaces(UA_Server *server){
+	for(UA_UInt32 i = 0; i < server->externalNamespacesSize; i++){
+		UA_ExternalNamespace_deleteMembers(&(server->externalNamespaces[i]));
+	}
+	if(server->externalNamespacesSize > 0){
+		UA_free(server->externalNamespaces);
+		server->externalNamespaces = UA_NULL;
+		server->externalNamespacesSize = 0;
+	}
+}
+
 UA_StatusCode UA_EXPORT UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex,
                                                        const UA_String *url, UA_ExternalNodeStore *nodeStore) {
 	if(nodeStore == UA_NULL)
@@ -105,23 +116,21 @@ void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkL
     server->networkLayers[server->networkLayersSize] = networkLayer;
     server->networkLayersSize++;
 
-    if(networkLayer.discoveryUrl) {
-        if(server->description.discoveryUrlsSize < 0)
-            server->description.discoveryUrlsSize = 0;
-        UA_String* newUrls = UA_realloc(server->description.discoveryUrls,
-                                        sizeof(UA_String)*(server->description.discoveryUrlsSize+1));
-        if(!newUrls) {
-            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);
+    if(server->description.discoveryUrlsSize < 0)
+        server->description.discoveryUrlsSize = 0;
+    UA_String* newUrls = UA_realloc(server->description.discoveryUrls,
+                                    sizeof(UA_String)*(server->description.discoveryUrlsSize+1));
+    if(!newUrls) {
+        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);
 }
 
 void UA_Server_setServerCertificate(UA_Server *server, UA_ByteString certificate) {
@@ -148,6 +157,9 @@ void UA_Server_delete(UA_Server *server) {
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager);
     UA_NodeStore_delete(server->nodestore);
+#ifdef UA_EXTERNAL_NAMESPACES
+    UA_Server_deleteExternalNamespaces(server);
+#endif
     UA_ByteString_deleteMembers(&server->serverCertificate);
     UA_Array_delete(server->namespaces, &UA_TYPES[UA_TYPES_STRING], server->namespacesSize);
     UA_Array_delete(server->endpointDescriptions, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION],
@@ -155,7 +167,8 @@ void UA_Server_delete(UA_Server *server) {
 
     // Delete the network layers
     for(size_t i = 0; i < server->networkLayersSize; i++) {
-        server->networkLayers[i].free(server->networkLayers[i].nlHandle);
+        UA_String_deleteMembers(&server->networkLayers[i].discoveryUrl);
+        server->networkLayers[i].deleteMembers(&server->networkLayers[i]);
     }
     UA_free(server->networkLayers);
 
@@ -190,13 +203,13 @@ static void getBulidInfo(const UA_Server* server, UA_BuildInfo *buildInfo) {
     buildInfo->buildDate = server->buildDate;
 }
 
-static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp,
-                                const UA_NumericRange *range, 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();
@@ -217,28 +230,17 @@ static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp,
     return UA_STATUSCODE_GOOD;
 }
 
-static void releaseStatus(void *handle, UA_DataValue *value) {
-    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,
-                                    const UA_NumericRange *range, 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;
+    UA_StatusCode retval = UA_Variant_setArrayCopy(&value->value, server->namespaces, server->namespacesSize, &UA_TYPES[UA_TYPES_STRING]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
     value->hasValue = UA_TRUE;
-    value->value.storageType = UA_VARIANT_DATA_NODELETE;
-    value->value.type = &UA_TYPES[UA_TYPES_STRING];
-    value->value.arrayLength = server->namespacesSize;
-    value->value.data = server->namespaces;
     if(sourceTimestamp) {
         value->hasSourceTimestamp = UA_TRUE;
         value->sourceTimestamp = UA_DateTime_now();
@@ -246,9 +248,6 @@ static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp,
     return UA_STATUSCODE_GOOD;
 }
 
-static void releaseNamespaces(void *handle, UA_DataValue *value) {
-}
-
 static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp,
                                      const UA_NumericRange *range, UA_DataValue *value) {
     if(range) {
@@ -270,11 +269,6 @@ static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp,
     return UA_STATUSCODE_GOOD;
 }
 
-static void releaseCurrentTime(void *handle, UA_DataValue *value) {
-    if(value->hasValue)
-        UA_DateTime_delete((UA_DateTime*)value->value.data);
-}
-
 static void copyNames(UA_Node *node, char *name) {
     node->browseName = UA_QUALIFIEDNAME_ALLOC(0, name);
     node->displayName = UA_LOCALIZEDTEXT_ALLOC("", name);
@@ -416,7 +410,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
 #define MAXCHANNELCOUNT 100
 #define STARTCHANNELID 1
-#define TOKENLIFETIME 600000
+#define TOKENLIFETIME 600000 //this is in milliseconds //600000 seems to be the minimal allowet time for UaExpert
 #define STARTTOKENID 1
     UA_SecureChannelManager_init(&server->secureChannelManager, MAXCHANNELCOUNT,
                                  TOKENLIFETIME, STARTCHANNELID, STARTTOKENID);
@@ -835,12 +829,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    copyNames((UA_Node*)namespaceArray, "NamespaceArray");
    namespaceArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_NAMESPACEARRAY;
    namespaceArray->valueSource = UA_VALUESOURCE_DATASOURCE;
-   namespaceArray->value.dataSource = (UA_DataSource) {
-       .handle = server, .read = readNamespaces,
-       .release = releaseNamespaces, .write = UA_NULL};
+   namespaceArray->value.dataSource = (UA_DataSource) {.handle = server, .read = readNamespaces, .write = UA_NULL};
    namespaceArray->valueRank = 1;
    namespaceArray->minimumSamplingInterval = 1.0;
-   namespaceArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)namespaceArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
                      nodeIdHasProperty);
    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), nodeIdHasTypeDefinition,
@@ -855,7 +846,6 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(server->config.Application_applicationURI);
    serverArray->valueRank = 1;
    serverArray->minimumSamplingInterval = 1.0;
-   serverArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)serverArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
                      nodeIdHasProperty);
    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY),
@@ -879,7 +869,6 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    *(UA_String *)localeIdArray->value.variant.data = UA_STRING_ALLOC("en");
    localeIdArray->valueRank = 1;
    localeIdArray->minimumSamplingInterval = 1.0;
-   localeIdArray->historizing = UA_FALSE;
    UA_Server_addNode(server, (UA_Node*)localeIdArray,
                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY),
@@ -898,6 +887,39 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
                            UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
                            nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
+    /** ServerProfileArray **/
+#define MAX_PROFILEARRAY 16 //a *magic* limit to the number of supported profiles
+#define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING_ALLOC(x)
+    UA_String profileArray[MAX_PROFILEARRAY];
+    UA_UInt16  profileArraySize = 0;
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NanoEmbeddedDevice");
+
+#ifdef ENABLE_SERVICESET_NODEMANAGEMENT
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NodeManagement");
+#endif
+#ifdef ENABLE_SERVICESET_METHOD
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/Methods");
+#endif
+#ifdef ENABLE_SUBSCRIPTIONS
+    ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/EmbeddedDataChangeSubscription");
+#endif
+
+    UA_VariableNode *serverProfileArray = UA_VariableNode_new();
+    copyNames((UA_Node*)serverProfileArray, "ServerProfileArray");
+    serverProfileArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY;
+    serverProfileArray->value.variant.arrayLength = profileArraySize;
+    serverProfileArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], profileArraySize);
+    serverProfileArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+    for(UA_UInt16 i=0;i<profileArraySize;i++){
+        ((UA_String *)serverProfileArray->value.variant.data)[i] = profileArray[i];
+    }
+    serverProfileArray->valueRank = 1;
+    serverProfileArray->minimumSamplingInterval = 1.0;
+    UA_Server_addNode(server, (UA_Node*)serverProfileArray,
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
+    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY),
+                           nodeIdHasTypeDefinition, 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;
@@ -914,7 +936,6 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
      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), nodeIdHasProperty);
      UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG),
@@ -924,15 +945,13 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
       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};
+      serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus, .write = UA_NULL};
       UA_Server_addNode(server, (UA_Node*)serverstatus, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
                         nodeIdHasComponent);
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                              nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE));
 
-      UA_VariableNode *starttime = UA_VariableNode_new();
+     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;
@@ -943,21 +962,19 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
                              nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
-      UA_VariableNode *currenttime = UA_VariableNode_new();
+     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};
+      currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime, .write = UA_NULL};
       UA_Server_addNode(server, (UA_Node*)currenttime,
                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                         nodeIdHasComponent);
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
                              nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
-      UA_VariableNode *state = UA_VariableNode_new();
-      UA_ServerState *stateEnum = UA_ServerState_new();
+     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;

+ 6 - 1
src/server/ua_server_addressspace.c

@@ -331,8 +331,13 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
     }
 
     // todo: test if the referencetype is hierarchical
+    //FIXME: a bit dirty workaround of preserving namespace
+    //namespace index is assumed to be valid
     const UA_Node *managed = UA_NULL;
-    if(UA_NodeId_isNull(&node->nodeId)) {
+    UA_NodeId tempNodeid;
+    UA_NodeId_copy(&node->nodeId, &tempNodeid);
+    tempNodeid.namespaceIndex = 0;
+    if(UA_NodeId_isNull(&tempNodeid)) {
         if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) {
             result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             goto ret2;

+ 65 - 38
src/server/ua_server_binary.c

@@ -6,8 +6,6 @@
 #include "ua_statuscodes.h"
 #include "ua_securechannel_manager.h"
 #include "ua_session_manager.h"
-#include "ua_nodeids.h"
-
 /** Max size of messages that are allocated on the stack */
 #define MAX_STACK_MESSAGE 65536
 
@@ -40,11 +38,10 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
 
     UA_TcpMessageHeader ackHeader;
     ackHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_ACKF;
-    ackHeader.messageSize = UA_TcpMessageHeader_calcSizeBinary(&ackHeader) 
-        + UA_TcpAcknowledgeMessage_calcSizeBinary(&ackMessage);
+    ackHeader.messageSize =  8 + 20; /* ackHeader + ackMessage */
 
     UA_ByteString ack_msg;
-    if(connection->getBuffer(connection, &ack_msg, ackHeader.messageSize) != UA_STATUSCODE_GOOD)
+    if(connection->getBuffer(connection, &ack_msg) != UA_STATUSCODE_GOOD)
         return;
 
     size_t tmpPos = 0;
@@ -99,7 +96,11 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     }
 
     /* send the response with an asymmetric security header */
-    seqHeader.sequenceNumber = channel->sequenceNumber;
+#ifndef UA_MULTITHREADING
+    seqHeader.sequenceNumber = ++channel->sequenceNumber;
+#else
+    seqHeader.sequenceNumber = uatomic_add_return(&channel->sequenceNumber, 1);
+#endif
 
     UA_SecureConversationMessageHeader respHeader;
     respHeader.messageHeader.messageTypeAndFinal = UA_MESSAGETYPEANDFINAL_OPNF;
@@ -109,33 +110,35 @@ static void processOPN(UA_Connection *connection, UA_Server *server, const UA_By
     UA_NodeId responseType = UA_NODEID_NUMERIC(0, UA_NS0ID_OPENSECURECHANNELRESPONSE +
                                                UA_ENCODINGOFFSET_BINARY);
 
-    respHeader.messageHeader.messageSize =
-        UA_SecureConversationMessageHeader_calcSizeBinary(&respHeader)
-        + UA_AsymmetricAlgorithmSecurityHeader_calcSizeBinary(&asymHeader)
-        + UA_SequenceHeader_calcSizeBinary(&seqHeader)
-        + UA_NodeId_calcSizeBinary(&responseType)
-        + UA_OpenSecureChannelResponse_calcSizeBinary(&p);
-
     UA_ByteString resp_msg;
-    retval = connection->getBuffer(connection, &resp_msg, respHeader.messageHeader.messageSize);
+    retval = connection->getBuffer(connection, &resp_msg);
     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);
+    size_t tmpPos = 12; /* skip the secureconversationmessageheader for now */
+    retval |= UA_AsymmetricAlgorithmSecurityHeader_encodeBinary(&asymHeader, &resp_msg, &tmpPos); // just mirror back
+    retval |= UA_SequenceHeader_encodeBinary(&seqHeader, &resp_msg, &tmpPos); // just mirror back
+    retval |= UA_NodeId_encodeBinary(&responseType, &resp_msg, &tmpPos);
+    retval |= UA_OpenSecureChannelResponse_encodeBinary(&p, &resp_msg, &tmpPos);
 
-    if(connection->write(connection, &resp_msg,
-                         respHeader.messageHeader.messageSize) != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD) {
         connection->releaseBuffer(connection, &resp_msg);
+        connection->close(connection);
+    } else {
+        respHeader.messageHeader.messageSize = tmpPos;
+        tmpPos = 0;
+        UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &resp_msg, &tmpPos);
+
+        if(connection->write(connection, &resp_msg,
+                             respHeader.messageHeader.messageSize) != UA_STATUSCODE_GOOD)
+            connection->releaseBuffer(connection, &resp_msg);
+    }
+
+    UA_OpenSecureChannelResponse_deleteMembers(&p);
+    UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
 }
 
 static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r) {
@@ -145,8 +148,8 @@ static void init_response_header(const UA_RequestHeader *p, UA_ResponseHeader *r
 }
 
 /* The request/response are casted to the header (first element of their struct) */
-static void invoke_service(UA_Server *server, UA_SecureChannel *channel,
-                           UA_UInt32 requestId, UA_RequestHeader *request, const UA_DataType *responseType,
+static void invoke_service(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 requestId,
+                           UA_RequestHeader *request, const UA_DataType *responseType,
                            void (*service)(UA_Server*, UA_Session*, void*, void*)) {
     UA_ResponseHeader *response = UA_alloca(responseType->memSize);
     UA_init(response, responseType);
@@ -163,7 +166,14 @@ static void invoke_service(UA_Server *server, UA_SecureChannel *channel,
         UA_Session_updateLifetime(session);
         service(server, session, request, response);
     }
-    UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
+    UA_StatusCode retval = UA_SecureChannel_sendBinaryMessage(channel, requestId, response, responseType);
+    if(retval != UA_STATUSCODE_GOOD) {
+        if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
+            response->serviceResult = UA_STATUSCODE_BADRESPONSETOOLARGE;
+        else
+            response->serviceResult = retval;
+        UA_SecureChannel_sendBinaryMessage(channel, requestId, response, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+    }
     UA_deleteMembers(response, responseType);
 }
 
@@ -197,13 +207,24 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     }
 
     /* Read the security header */
-    UA_UInt32 tokenId;
+    UA_UInt32 tokenId = 0;
     UA_SequenceHeader sequenceHeader;
     retval = UA_UInt32_decodeBinary(msg, pos, &tokenId);
     retval |= UA_SequenceHeader_decodeBinary(msg, pos, &sequenceHeader);
-    if(retval != UA_STATUSCODE_GOOD)
+    if(retval != UA_STATUSCODE_GOOD || tokenId==0) //0 is invalid
         return;
 
+    if(clientChannel != &anonymousChannel){
+        if(tokenId!=clientChannel->securityToken.tokenId){
+            //is client using a newly issued token?
+            if(tokenId==clientChannel->nextSecurityToken.tokenId){ //tokenId is not 0
+                UA_SecureChannel_revolveTokens(clientChannel);
+            }else{
+                //FIXME: how to react to this, what do we have to return? Or just kill the channel
+            }
+        }
+    }
+
     /* Read the request type */
     UA_NodeId requestType;
     if(UA_NodeId_decodeBinary(msg, pos, &requestType) != UA_STATUSCODE_GOOD)
@@ -301,7 +322,6 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     case UA_NS0ID_TRANSLATEBROWSEPATHSTONODEIDSREQUEST:
         INVOKE_SERVICE(TranslateBrowsePathsToNodeIds, UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE);
         break;
-
 #ifdef ENABLE_SUBSCRIPTIONS    
     case UA_NS0ID_CREATESUBSCRIPTIONREQUEST:
         INVOKE_SERVICE(CreateSubscription, UA_TYPES_CREATESUBSCRIPTIONRESPONSE);
@@ -320,6 +340,10 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         break;
     case UA_NS0ID_DELETEMONITOREDITEMSREQUEST:
         INVOKE_SERVICE(DeleteMonitoredItems, UA_TYPES_DELETEMONITOREDITEMSRESPONSE);
+#endif
+#ifdef ENABLE_ADDNODES 
+    case UA_NS0ID_ADDNODESREQUEST:
+        INVOKE_SERVICE(AddNodes, UA_TYPES_ADDNODESRESPONSE);
         break;
 #endif
     default: {
@@ -330,19 +354,20 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
             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;
+        UA_ServiceFault r;
         if(UA_RequestHeader_decodeBinary(msg, pos, &p) != UA_STATUSCODE_GOOD)
             return;
-        UA_ResponseHeader_init(&r);
-        init_response_header(&p, &r);
-        r.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
+        UA_ServiceFault_init(&r);
+        init_response_header(&p, &r.responseHeader);
+        r.responseHeader.serviceResult = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
 #ifdef EXTENSION_STATELESS
         if(retval != UA_STATUSCODE_GOOD)
             r.serviceResult = retval;
 #endif
+        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId, &r,
+                                           &UA_TYPES[UA_TYPES_SERVICEFAULT]);
         UA_RequestHeader_deleteMembers(&p);
-        UA_SecureChannel_sendBinaryMessage(clientChannel, sequenceHeader.requestId,
-            &r, &UA_TYPES[UA_TYPES_RESPONSEHEADER]);
+        UA_ServiceFault_deleteMembers(&r);
         break;
     }
     }
@@ -380,9 +405,11 @@ void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection
             break;
         case UA_MESSAGETYPEANDFINAL_MSGF & 0xffffff:
 #ifndef EXTENSION_STATELESS
-            if(connection->state != UA_CONNECTION_ESTABLISHED)
+            if(connection->state != UA_CONNECTION_ESTABLISHED){
                 connection->close(connection);
-            else
+                UA_ByteString_deleteMembers(msg);
+                return;
+            }else
 #endif
                 processMSG(connection, server, msg, &pos);
             break;

+ 5 - 8
src/server/ua_server_worker.c

@@ -34,12 +34,9 @@ static void processJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
         case UA_JOBTYPE_BINARYMESSAGE:
             UA_Server_processBinaryMessage(server, job->job.binaryMessage.connection,
                                            &job->job.binaryMessage.message);
-            UA_Connection *c = job->job.binaryMessage.connection;
-            c->releaseBuffer(job->job.binaryMessage.connection, &job->job.binaryMessage.message);
             break;
-        case UA_JOBTYPE_CLOSECONNECTION:
+        case UA_JOBTYPE_DETACHCONNECTION:
             UA_Connection_detachSecureChannel(job->job.closeConnection);
-            job->job.closeConnection->close(job->job.closeConnection);
             break;
         case UA_JOBTYPE_METHODCALL:
         case UA_JOBTYPE_DELAYEDMETHODCALL:
@@ -536,7 +533,7 @@ UA_StatusCode UA_Server_run_startup(UA_Server *server, UA_UInt16 nThreads, UA_Bo
 
     /* Start the networklayers */
     for(size_t i = 0; i < server->networkLayersSize; i++)
-        server->networkLayers[i].start(server->networkLayers[i].nlHandle, &server->logger);
+        server->networkLayers[i].start(&server->networkLayers[i], &server->logger);
 
     return UA_STATUSCODE_GOOD;
 }
@@ -556,11 +553,11 @@ UA_StatusCode UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running) {
         UA_Int32 jobsSize;
         if(*running) {
             if(i == server->networkLayersSize-1)
-                jobsSize = nl->getJobs(nl->nlHandle, &jobs, timeout);
+                jobsSize = nl->getJobs(nl, &jobs, timeout);
             else
-                jobsSize = nl->getJobs(nl->nlHandle, &jobs, 0);
+                jobsSize = nl->getJobs(nl, &jobs, 0);
         } else
-            jobsSize = server->networkLayers[i].stop(nl->nlHandle, &jobs);
+            jobsSize = server->networkLayers[i].stop(nl, &jobs);
 
 #ifdef UA_MULTITHREADING
         /* Filter out delayed work */

+ 8 - 2
src/server/ua_services.h

@@ -71,7 +71,7 @@ void Service_CloseSecureChannel(UA_Server *server, UA_Int32 channelId);
 /**
  * Used by an OPC UA Client to create a Session and the Server returns two
  * values which uniquely identify the Session. The first value is the sessionId
- * which is used to identify the Session in the audit logs and in the Servers
+ * which is used to identify the Session in the audit logs and in the Server's
  * address space. The second is the authenticationToken which is used to
  * associate an incoming request with a Session.
  */
@@ -196,8 +196,14 @@ void Service_UnregisterNodes(UA_Server *server, UA_Session *session, const UA_Un
  */
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
                   UA_ReadResponse *response);
-// Service_HistoryRead
 
+/* Mock-Up of the function signature for Unit Tests */
+#ifdef BUILD_UNIT_TESTS
+void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
+                      const UA_ReadValueId *id, UA_DataValue *v);
+#endif
+
+// Service_HistoryRead
 /**
  * Used to write one or more Attributes of one or more Nodes. For constructed
  * Attribute values whose elements are indexed, such as an array, this Service

+ 34 - 49
src/server/ua_services_attribute.c

@@ -92,8 +92,10 @@ 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) {
+#ifndef BUILD_UNIT_TESTS
+static
+#endif
+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){
@@ -195,18 +197,18 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     			handleSourceTimestamps(timestamps, v);
             }
 
-            UA_Boolean hasRange = UA_FALSE;
             UA_NumericRange range;
+            UA_NumericRange *rangeptr = UA_NULL;
             if(id->indexRange.length > 0) {
                 retval = parse_numericrange(id->indexRange, &range);
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
-                hasRange = UA_TRUE;
+                rangeptr = &range;
             }
 
             const UA_VariableNode *vn = (const UA_VariableNode*)node;
             if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-                if(hasRange)
+                if(rangeptr)
                     retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range);
                 else
                     retval |= UA_Variant_copy(&vn->value.variant, &v->value);
@@ -215,21 +217,11 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                     handleSourceTimestamps(timestamps, v);
                 }
             } else {
-                UA_DataValue val;
-                UA_DataValue_init(&val);
-                UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
-                                              timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-                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: still too much copying necessary!!
-                    vn->value.dataSource.release(vn->value.dataSource.handle, &val);
-                }
+                UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH);
+                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, rangeptr, v);
             }
 
-            if(hasRange)
+            if(rangeptr)
                 UA_free(range.dimensions);
         }
         break;
@@ -238,8 +230,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         const UA_VariableNode *vn = (const UA_VariableNode*)node;
         if(vn->valueSource == UA_VALUESOURCE_VARIANT)
-            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId,
-                                              &UA_TYPES[UA_TYPES_NODEID]);
+            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
         else {
             UA_DataValue val;
             UA_DataValue_init(&val);
@@ -247,8 +238,9 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
             if(retval != UA_STATUSCODE_GOOD)
                 break;
             retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
-            vn->value.dataSource.release(vn->value.dataSource.handle, &val);
+            UA_DataValue_deleteMembers(&val);
         }
+
         if(retval == UA_STATUSCODE_GOOD)
             v->hasValue = UA_TRUE;
         }
@@ -257,8 +249,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode *)node)->valueRank,
-                                          &UA_TYPES[UA_TYPES_INT32]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode *)node)->valueRank, &UA_TYPES[UA_TYPES_INT32]);
         break;
 
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
@@ -266,9 +257,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
         {
             const UA_VariableNode *vn = (const UA_VariableNode *)node;
             if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-                retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions,
-                                                 vn->value.variant.arrayDimensionsSize,
-                                                 &UA_TYPES[UA_TYPES_INT32]);
+                retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions, vn->value.variant.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
                 if(retval == UA_STATUSCODE_GOOD)
                     v->hasValue = UA_TRUE;
             } else {
@@ -277,12 +266,8 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                 retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, UA_NULL, &val);
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
-                if(!val.hasValue)
-                    retval = UA_STATUSCODE_BADNOTREADABLE;
-                else
-                    retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
-                                                     val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
-                vn->value.dataSource.release(vn->value.dataSource.handle, &val);
+                retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions, val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+                UA_DataValue_deleteMembers(&val);
             }
         }
         break;
@@ -290,43 +275,37 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel,
-                                          &UA_TYPES[UA_TYPES_BYTE]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]);
         break;
 
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel,
-                                          &UA_TYPES[UA_TYPES_BYTE]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
         break;
 
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval,
-                                          &UA_TYPES[UA_TYPES_DOUBLE]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]);
         break;
 
     case UA_ATTRIBUTEID_HISTORIZING:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing,
-                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
 
     case UA_ATTRIBUTEID_EXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable,
-                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
 
     case UA_ATTRIBUTEID_USEREXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
         v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable,
-                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
 
     default:
@@ -422,16 +401,21 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 		expireArray = UA_Array_new(&UA_TYPES[UA_TYPES_DATETIME], request->nodesToReadSize);
 		variant.data = expireArray;
 
-		UA_ByteString str;
-		UA_ByteString_init(&str);
-
 		/*expires in 20 seconds*/
 		for(UA_Int32 i = 0;i < response->resultsSize;i++) {
 			expireArray[i] = UA_DateTime_now() + 20 * 100 * 1000 * 1000;
 		}
 		size_t offset = 0;
-		str.data = UA_malloc(UA_Variant_calcSizeBinary(&variant));
-		str.length = UA_Variant_calcSizeBinary(&variant);
+        UA_Connection *c = UA_NULL;
+        UA_SecureChannel *sc = session->channel;
+        if(sc)
+            c = session->sc;
+        if(!c) {
+            response->responseHeader.serviceResult = UA_STATUSCODE_BADINTERNALERROR;
+            return;
+        }
+		UA_ByteString str;
+        UA_ByteString_newMembers(&str, c->remoteConf.maxMessageSize);
 		UA_Variant_encodeBinary(&variant, &str, &offset);
 		additionalHeader.body = str;
 		response->responseHeader.additionalHeader = additionalHeader;
@@ -508,6 +492,7 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                    oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
                     /* 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] && !UA_Variant_isScalar(oldV) &&
                         wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&

+ 9 - 8
src/server/ua_services_nodemanagement.c

@@ -6,20 +6,20 @@
 #include "ua_util.h"
 
 #define COPY_STANDARDATTRIBUTES do {                                       \
-      if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) {   \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DISPLAYNAME) {     \
         vnode->displayName = attr.displayName;                             \
         UA_LocalizedText_copy(&attr.displayName, &(vnode->displayName));   \
         UA_LocalizedText_init(&attr.displayName);                          \
-      }                                                                    \
-      if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DESCRIPTION) {   \
+    }                                                                      \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_DESCRIPTION) {     \
         UA_LocalizedText_copy(&attr.description, &(vnode->description));   \
         UA_LocalizedText_init(&attr.description);                          \
-      }                                                                    \
-      if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_WRITEMASK)       \
+    }                                                                      \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_WRITEMASK)         \
         vnode->writeMask = attr.writeMask;                                 \
-      if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERWRITEMASK)   \
+    if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_USERWRITEMASK)     \
         vnode->userWriteMask = attr.userWriteMask;                         \
-    } while(0)
+} while(0)
 
 static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node **new_node) {
     if(attributes->typeId.identifier.numeric !=
@@ -212,6 +212,7 @@ static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_Add
     // The BrowseName was not included with the NodeAttribute ExtensionObject
     UA_QualifiedName_init(&(node->browseName));
     UA_QualifiedName_copy(&(item->browseName), &(node->browseName));
+    UA_NodeId_copy(&item->requestedNewNodeId.nodeId, &node->nodeId);
     
     // add the node
     *result = UA_Server_addNodeWithSession(server, session, node, item->parentNodeId,
@@ -249,7 +250,7 @@ void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesR
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-
+    
 #ifdef UA_EXTERNAL_NAMESPACES
 #ifdef NO_ALLOCA
     UA_Boolean isExternal[size];

+ 8 - 6
src/server/ua_services_session.c

@@ -59,19 +59,19 @@ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
     UA_UserNameIdentityToken username_token;
     UA_UserNameIdentityToken_init(&username_token);
 
+    UA_String ap = UA_STRING(ANONYMOUS_POLICY);
+    UA_String up = UA_STRING(USERNAME_POLICY);
     if(token.policyId.data == UA_NULL) {
         /* 1) no policy defined */
         response->responseHeader.serviceResult = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
-    } else if(server->config.Login_enableAnonymous &&
-              UA_String_equalchars(&token.policyId, ANONYMOUS_POLICY)) {
+    } else if(server->config.Login_enableAnonymous && UA_String_equal(&token.policyId, &ap)) {
         /* 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)) {
+    } else if(server->config.Login_enableUsernamePassword && UA_String_equal(&token.policyId, &up)) {
         /* 3) username logins */
         offset = 0;
         UA_UserNameIdentityToken_decodeBinary(&request->userIdentityToken.body, &offset, &username_token);
@@ -85,8 +85,10 @@ void Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
             /* 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])) {
+                UA_String user = UA_STRING(server->config.Login_usernames[i]);
+                UA_String pw = UA_STRING(server->config.Login_passwords[i]);
+                if(UA_String_equal(&username_token.userName, &user) &&
+                   UA_String_equal(&username_token.password, &pw)) {
                     /* success - activate */
                     if(foundSession->channel && foundSession->channel != channel)
                         UA_SecureChannel_detachSession(foundSession->channel, foundSession);

+ 2 - 2
src/ua_connection.c

@@ -10,8 +10,8 @@ const UA_ConnectionConfig UA_ConnectionConfig_standard =
 
 void UA_Connection_init(UA_Connection *connection) {
     connection->state = UA_CONNECTION_CLOSED;
-    connection->localConf = (UA_ConnectionConfig){0,0,0,0,0};
-    connection->remoteConf = (UA_ConnectionConfig){0,0,0,0,0};
+    connection->localConf = UA_ConnectionConfig_standard;
+    connection->remoteConf = UA_ConnectionConfig_standard;
     connection->channel = UA_NULL;
     connection->sockfd = 0;
     connection->handle = UA_NULL;

+ 30 - 16
src/ua_securechannel.c

@@ -7,6 +7,7 @@
 void UA_SecureChannel_init(UA_SecureChannel *channel) {
     UA_MessageSecurityMode_init(&channel->securityMode);
     UA_ChannelSecurityToken_init(&channel->securityToken);
+    UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->clientAsymAlgSettings);
     UA_AsymmetricAlgorithmSecurityHeader_init(&channel->serverAsymAlgSettings);
     UA_ByteString_init(&channel->clientNonce);
@@ -21,7 +22,8 @@ void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
     UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&channel->clientAsymAlgSettings);
     UA_ByteString_deleteMembers(&channel->clientNonce);
-    UA_ChannelSecurityToken_deleteMembers(&channel->securityToken);
+    UA_ChannelSecurityToken_deleteMembers(&channel->securityToken); //FIXME: not really needed
+    UA_ChannelSecurityToken_deleteMembers(&channel->nextSecurityToken); //FIXME: not really needed
     UA_Connection *c = channel->connection;
     if(c) {
         UA_Connection_detachSecureChannel(c);
@@ -88,6 +90,16 @@ UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *t
     return se->session;
 }
 
+void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel){
+    if(channel->nextSecurityToken.tokenId==0) //no security token issued
+        return;
+
+    //FIXME: not thread-safe
+    //swap tokens
+    memcpy(&channel->securityToken, &channel->nextSecurityToken, sizeof(UA_ChannelSecurityToken));
+    UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
+}
+
 UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
                                                   const void *content,
                                                   const UA_DataType *contentType) {
@@ -111,33 +123,35 @@ UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_U
     UA_SequenceHeader seqHeader;
     seqHeader.requestId = requestId;
 
-    respHeader.messageHeader.messageSize =
-        UA_SecureConversationMessageHeader_calcSizeBinary(&respHeader)
-        + UA_SymmetricAlgorithmSecurityHeader_calcSizeBinary(&symSecHeader)
-        + UA_SequenceHeader_calcSizeBinary(&seqHeader)
-        + UA_NodeId_calcSizeBinary(&typeId)
-        + UA_calcSizeBinary(content, contentType);
-
     UA_ByteString message;
-    UA_StatusCode retval = connection->getBuffer(connection, &message, respHeader.messageHeader.messageSize);
-    if(retval)
+    UA_StatusCode retval = connection->getBuffer(connection, &message);
+    if(retval != UA_STATUSCODE_GOOD)
         return retval;
 
-    /* do this only now, so the sequence number does not increase if sth fails */
+    size_t messagePos = 24; // after the headers
+    retval |= UA_NodeId_encodeBinary(&typeId, &message, &messagePos);
+    retval |= UA_encodeBinary(content, contentType, &message, &messagePos);
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        connection->releaseBuffer(connection, &message);
+        return retval;
+    }
+
+    /* now write the header with the size */
+    respHeader.messageHeader.messageSize = messagePos;
 #ifndef UA_MULTITHREADING
     seqHeader.sequenceNumber = ++channel->sequenceNumber;
 #else
     seqHeader.sequenceNumber = uatomic_add_return(&channel->sequenceNumber, 1);
 #endif
 
-    size_t messagePos = 0;
+    messagePos = 0;
     UA_SecureConversationMessageHeader_encodeBinary(&respHeader, &message, &messagePos);
     UA_SymmetricAlgorithmSecurityHeader_encodeBinary(&symSecHeader, &message, &messagePos);
     UA_SequenceHeader_encodeBinary(&seqHeader, &message, &messagePos);
-    UA_NodeId_encodeBinary(&typeId, &message, &messagePos);
-    UA_encodeBinary(content, contentType, &message, &messagePos);
     
-    if(connection->write(connection, &message, respHeader.messageHeader.messageSize) != UA_STATUSCODE_GOOD)
+    retval = connection->write(connection, &message, respHeader.messageHeader.messageSize);
+    if(retval != UA_STATUSCODE_GOOD)
         connection->releaseBuffer(connection, &message);
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }

+ 2 - 0
src/ua_securechannel.h

@@ -23,6 +23,7 @@ struct SessionEntry {
 struct UA_SecureChannel {
     UA_MessageSecurityMode  securityMode;
     UA_ChannelSecurityToken securityToken; // the channelId is contained in the securityToken
+    UA_ChannelSecurityToken nextSecurityToken; // the channelId is contained in the securityToken
     UA_AsymmetricAlgorithmSecurityHeader clientAsymAlgSettings;
     UA_AsymmetricAlgorithmSecurityHeader serverAsymAlgSettings;
     UA_ByteString  clientNonce;
@@ -44,6 +45,7 @@ UA_Session * UA_SecureChannel_getSession(UA_SecureChannel *channel, UA_NodeId *t
 UA_StatusCode UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestId,
                                                   const void *content, const UA_DataType *contentType);
 
+void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel);
 /** @} */
 
 #endif /* UA_SECURECHANNEL_H_ */

+ 35 - 29
src/ua_types.c

@@ -159,11 +159,6 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 
-UA_Boolean UA_String_equalchars(const UA_String *string1, char *charString) {
-    UA_String string2 = UA_STRING(charString);
-    return UA_String_equal(string1, &string2);
-}
-
 /* DateTime */
 #define UNIX_EPOCH_BIAS_SEC 11644473600LL // Number of seconds from 1 Jan. 1601 00:00 to 1 Jan 1970 00:00 UTC
 #define HUNDRED_NANOSEC_PER_USEC 10LL
@@ -843,21 +838,21 @@ UA_StatusCode UA_Variant_setScalarCopy(UA_Variant *v, const void *p, const UA_Da
     return UA_Variant_setArray(v, new, -1, type);
 }
 
-UA_StatusCode UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 noElements,
+UA_StatusCode UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 elements,
                                   const UA_DataType *type) {
     v->type = type;
-    v->arrayLength = noElements;
+    v->arrayLength = elements;
     v->data = array;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 noElements,
+UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 elements,
                                       const UA_DataType *type) {
     void *new;
-    UA_StatusCode retval = UA_Array_copy(array, &new, type, noElements);
+    UA_StatusCode retval = UA_Array_copy(array, &new, type, elements);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
-    return UA_Variant_setArray(v, new, noElements, type);
+    return UA_Variant_setArray(v, new, elements, type);
 }
 
 /* DiagnosticInfo */
@@ -1027,16 +1022,16 @@ UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *dataType) {
             ptrs += (member->padding >> 3);
             ptrd += (member->padding >> 3);
             UA_Int32 *dstNoElements = (UA_Int32*)ptrd;
-            const UA_Int32 noElements = *((const UA_Int32*)ptrs);
+            const UA_Int32 elements = *((const UA_Int32*)ptrs);
             ptrs += sizeof(UA_Int32) + (member->padding & 0x07);
             ptrd += sizeof(UA_Int32) + (member->padding & 0x07);
-            retval = UA_Array_copy(*(void* const*)ptrs, (void**)ptrd, memberType, noElements);
+            retval = UA_Array_copy(*(void* const*)ptrs, (void**)ptrd, memberType, elements);
             if(retval != UA_STATUSCODE_GOOD) {
                 UA_deleteMembers(dst, dataType);
                 UA_init(dst, dataType);
                 return retval;
             }
-            *dstNoElements = noElements;
+            *dstNoElements = elements;
             ptrs += sizeof(void*);
             ptrd += sizeof(void*);
             continue;
@@ -1117,11 +1112,18 @@ UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *dataType) {
 }
 
 void UA_deleteMembers(void *p, const UA_DataType *dataType) {
+    UA_deleteMembersUntil(p, dataType, -1);
+}
+
+void UA_deleteMembersUntil(void *p, const UA_DataType *dataType, UA_Int32 lastMember) {
     uintptr_t ptr = (uintptr_t)p;
     if(dataType->fixedSize)
         return;
     UA_Byte membersSize = dataType->membersSize;
     for(size_t i=0;i<membersSize; i++) {
+        if(lastMember > -1 && (UA_Int32)i > lastMember){
+            return;
+        }
         const UA_DataTypeMember *member = &dataType->members[i];
         const UA_DataType *memberType;
         if(member->namespaceZero)
@@ -1131,9 +1133,9 @@ void UA_deleteMembers(void *p, const UA_DataType *dataType) {
 
         if(member->isArray) {
             ptr += (member->padding >> 3);
-            UA_Int32 noElements = *((UA_Int32*)ptr);
+            UA_Int32 elements = *((UA_Int32*)ptr);
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
-            UA_Array_delete(*(void**)ptr, memberType, noElements);
+            UA_Array_delete(*(void**)ptr, memberType, elements);
             *(void**)ptr = UA_NULL;
             ptr += sizeof(void*);
             continue;
@@ -1185,7 +1187,11 @@ void UA_deleteMembers(void *p, const UA_DataType *dataType) {
             break;
         default:
             // QualifiedName, LocalizedText and strings are treated as structures, also
-            UA_deleteMembers((void*)ptr, memberType);
+            if(lastMember > -1){
+                UA_deleteMembersUntil((void*)ptr, memberType, lastMember-i);
+            }
+            else
+                UA_deleteMembers((void*)ptr, memberType);
         }
         ptr += memberType->memSize;
     }
@@ -1200,58 +1206,58 @@ void UA_delete(void *p, const UA_DataType *dataType) {
 /* Array Handling */
 /******************/
 
-void* UA_Array_new(const UA_DataType *dataType, UA_Int32 noElements) {
-    if((UA_Int32)dataType->memSize * noElements < 0 || dataType->memSize * noElements > MAX_ARRAY_SIZE )
+void* UA_Array_new(const UA_DataType *dataType, UA_Int32 elements) {
+    if((UA_Int32)dataType->memSize * elements < 0 || dataType->memSize * elements > MAX_ARRAY_SIZE )
         return UA_NULL;
 
     if(dataType->fixedSize)
-        return calloc(noElements, dataType->memSize);
+        return UA_calloc(elements, dataType->memSize);
 
-    void *p = malloc(dataType->memSize * (size_t)noElements);
+    void *p = UA_malloc(dataType->memSize * (size_t)elements);
     if(!p)
         return p;
 
     uintptr_t ptr = (uintptr_t)p;
-    for(int i = 0; i<noElements; i++) {
+    for(int i = 0; i<elements; i++) {
         UA_init((void*)ptr, dataType);
         ptr += dataType->memSize;
     }
     return p;
 }
 
-UA_StatusCode UA_Array_copy(const void *src, void **dst, const UA_DataType *dataType, UA_Int32 noElements) {
-    if(noElements <= 0) {
+UA_StatusCode UA_Array_copy(const void *src, void **dst, const UA_DataType *dataType, UA_Int32 elements) {
+    if(elements <= 0) {
         *dst = UA_NULL;
         return UA_STATUSCODE_GOOD;
     }
 
-    if(!(*dst = UA_malloc((size_t)noElements * dataType->memSize)))
+    if(!(*dst = UA_malloc((size_t)elements * dataType->memSize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
     if(dataType->fixedSize) {
-        memcpy(*dst, src, dataType->memSize * (size_t)noElements);
+        memcpy(*dst, src, dataType->memSize * (size_t)elements);
         return UA_STATUSCODE_GOOD;
     }
 
     uintptr_t ptrs = (uintptr_t)src;
     uintptr_t ptrd = (uintptr_t)*dst;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    for(int i = 0; i < noElements; i++) {
+    for(int i = 0; i < elements; i++) {
         retval |= UA_copy((void*)ptrs, (void*)ptrd, dataType);
         ptrs += dataType->memSize;
         ptrd += dataType->memSize;
     }
 
     if(retval != UA_STATUSCODE_GOOD)
-        UA_Array_delete(*dst, dataType, noElements);
+        UA_Array_delete(*dst, dataType, elements);
 
     return retval;
 }
 
-void UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 noElements) {
+void UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 elements) {
     if(!dataType->fixedSize) {
         uintptr_t ptr = (uintptr_t)p;
-        for(UA_Int32 i = 0; i<noElements; i++) {
+        for(UA_Int32 i = 0; i < elements; i++) {
             UA_deleteMembers((void*)ptr, dataType);
             ptr += dataType->memSize;
         }

Datei-Diff unterdrückt, da er zu groß ist
+ 580 - 494
src/ua_types_encoding_binary.c


+ 28 - 16
src/ua_types_encoding_binary.h

@@ -16,9 +16,6 @@
  * All datatypes have similar functions with a common postfix. DO NOT CALL THESE
  * FUNCTIONS WITH NULL-POINTERS IF IT IS NOT EXPLICITLY PERMITTED.
  *
- * - _calcSize: Returns the size of the (encoded) variable in bytes. This length
- *   is used to allocate the bytestring for later encoding.
- *
  * - _encode: Encodes a variable into a bytestring. If an error occurs
  *   (indicated by the return value), the bytestring may be left in an
  *   inconsistent state.
@@ -32,29 +29,43 @@
  */
 
 #define UA_TYPE_BINARY_ENCODING(TYPE)                                   \
-    size_t TYPE##_calcSizeBinary(TYPE const *p);                        \
     UA_StatusCode TYPE##_encodeBinary(TYPE const *src, UA_ByteString *dst, size_t *UA_RESTRICT offset); \
     UA_StatusCode TYPE##_decodeBinary(UA_ByteString const *src, size_t *UA_RESTRICT offset, TYPE *dst);
 
 UA_TYPE_BINARY_ENCODING(UA_Boolean)
-UA_TYPE_BINARY_ENCODING(UA_SByte)
 UA_TYPE_BINARY_ENCODING(UA_Byte)
-UA_TYPE_BINARY_ENCODING(UA_Int16)
+#define UA_SByte_encodeBinary(src, dst, offset) UA_Byte_encodeBinary((const UA_Byte *)src, dst, offset)
+#define UA_SByte_decodeBinary(src, offset, dst) UA_Byte_decodeBinary(src, offset, (UA_Byte *)dst)
 UA_TYPE_BINARY_ENCODING(UA_UInt16)
-UA_TYPE_BINARY_ENCODING(UA_Int32)
+#define UA_Int16_encodeBinary(src, dst, offset) UA_UInt16_encodeBinary((const UA_UInt16 *)src, dst, offset)
+#define UA_Int16_decodeBinary(src, offset, dst) UA_UInt16_decodeBinary(src, offset, (UA_UInt16 *)dst)
 UA_TYPE_BINARY_ENCODING(UA_UInt32)
-UA_TYPE_BINARY_ENCODING(UA_Int64)
+#define UA_Int32_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
+#define UA_Int32_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
 UA_TYPE_BINARY_ENCODING(UA_UInt64)
-UA_TYPE_BINARY_ENCODING(UA_Float)
-UA_TYPE_BINARY_ENCODING(UA_Double)
+#define UA_Int64_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
+#define UA_Int64_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
+#ifdef UA_MIXED_ENDIAN
+ UA_TYPE_BINARY_ENCODING(UA_Float)
+ UA_TYPE_BINARY_ENCODING(UA_Double)
+#else
+ #define UA_Float_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
+ #define UA_Float_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
+ #define UA_Double_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
+ #define UA_Double_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
+#endif
 UA_TYPE_BINARY_ENCODING(UA_String)
-UA_TYPE_BINARY_ENCODING(UA_DateTime)
+#define UA_DateTime_encodeBinary(src, dst, offset) UA_UInt64_encodeBinary((const UA_UInt64 *)src, dst, offset)
+#define UA_DateTime_decodeBinary(src, offset, dst) UA_UInt64_decodeBinary(src, offset, (UA_UInt64 *)dst)
 UA_TYPE_BINARY_ENCODING(UA_Guid)
-UA_TYPE_BINARY_ENCODING(UA_ByteString)
-UA_TYPE_BINARY_ENCODING(UA_XmlElement)
+#define UA_ByteString_encodeBinary(src, dst, offset) UA_String_encodeBinary((const UA_String *)src, dst, offset)
+#define UA_ByteString_decodeBinary(src, offset, dst) UA_String_decodeBinary(src, offset, (UA_String *)dst)
+#define UA_XmlElement_encodeBinary(src, dst, offset) UA_String_encodeBinary((const UA_String *)src, dst, offset)
+#define UA_XmlElement_decodeBinary(src, offset, dst) UA_String_decodeBinary(src, offset, (UA_String *)dst)
 UA_TYPE_BINARY_ENCODING(UA_NodeId)
 UA_TYPE_BINARY_ENCODING(UA_ExpandedNodeId)
-UA_TYPE_BINARY_ENCODING(UA_StatusCode)
+#define UA_StatusCode_encodeBinary(src, dst, offset) UA_UInt32_encodeBinary((const UA_UInt32 *)src, dst, offset)
+#define UA_StatusCode_decodeBinary(src, offset, dst) UA_UInt32_decodeBinary(src, offset, (UA_UInt32 *)dst)
 UA_TYPE_BINARY_ENCODING(UA_QualifiedName)
 UA_TYPE_BINARY_ENCODING(UA_LocalizedText)
 UA_TYPE_BINARY_ENCODING(UA_ExtensionObject)
@@ -62,11 +73,12 @@ UA_TYPE_BINARY_ENCODING(UA_DataValue)
 UA_TYPE_BINARY_ENCODING(UA_Variant)
 UA_TYPE_BINARY_ENCODING(UA_DiagnosticInfo)
 
-size_t UA_calcSizeBinary(const void *p, const UA_DataType *dataType);
 UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *dataType, UA_ByteString *dst,
                               size_t *UA_RESTRICT offset);
 UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t * UA_RESTRICT offset, void *dst,
                               const UA_DataType *dataType);
-/// @} /* end of group */
+size_t UA_calcSizeBinary(const void *p, const UA_DataType *dataType);
+
+/// @}
 
 #endif /* UA_TYPES_ENCODING_BINARY_H_ */

+ 17 - 48
src/ua_util.h

@@ -1,25 +1,7 @@
 #ifndef UA_UTIL_H_
 #define UA_UTIL_H_
 
-#ifndef UA_AMALGAMATE
-# include "ua_config.h"
-#endif
-
-#ifndef __USE_POSIX
-# define __USE_POSIX
-#endif
-#ifndef _POSIX_SOURCE
-# define _POSIX_SOURCE
-#endif
-#ifndef _POSIX_C_SOURCE
-# define _POSIX_C_SOURCE 199309L
-#endif
-#ifndef _BSD_SOURCE
-# define _BSD_SOURCE
-#endif
-#ifndef _DEFAULT_SOURCE
-# define _DEFAULT_SOURCE
-#endif
+#include "ua_config.h"
 
 /*********************/
 /* Memory Management */
@@ -35,9 +17,9 @@
 
 /* Visual Studio needs __restrict */
 #ifdef _MSC_VER
-    #define UA_RESTRICT __restrict
+# define UA_RESTRICT __restrict
 #else
-    #define UA_RESTRICT restrict
+# define UA_RESTRICT restrict
 #endif
 
 /* Visual Studio does not know fnct/unistd file access results */
@@ -66,34 +48,31 @@
 
 /* Replace the macros with functions for custom allocators if necessary */
 #ifndef UA_free
-    #define UA_free(ptr) free(ptr)
+# define UA_free(ptr) free(ptr)
 #endif
 #ifndef UA_malloc
-    #define UA_malloc(size) malloc(size)
+# define UA_malloc(size) malloc(size)
 #endif
 #ifndef UA_calloc
-    #define UA_calloc(num, size) calloc(num, size)
+# define UA_calloc(num, size) calloc(num, size)
 #endif
 #ifndef UA_realloc
-    #define UA_realloc(ptr, size) realloc(ptr, size)
+# define UA_realloc(ptr, size) realloc(ptr, size)
 #endif
 
 #define UA_memcpy(dst, src, size) memcpy(dst, src, size)
 #define UA_memset(ptr, value, size) memset(ptr, value, size)
 
-#ifdef NO_ALLOCA
-#else
-#ifdef _WIN32
-    # define UA_alloca(SIZE) _alloca(SIZE)
-#else
- #ifdef __GNUC__
-    # define UA_alloca(size) __builtin_alloca (size)
- #else
-    # include <alloca.h>
-    # define UA_alloca(SIZE) alloca(SIZE)
- #endif
+#ifndef NO_ALLOCA
+# ifdef __GNUC__
+#  define UA_alloca(size) __builtin_alloca (size)
+# elif defined(_WIN32)
+#  define UA_alloca(SIZE) _alloca(SIZE)
+# else
+#  include <alloca.h>
+#  define UA_alloca(SIZE) alloca(SIZE)
+# endif
 #endif
-#endif /* NO_ALLOCA */
 
 /********************/
 /* System Libraries */
@@ -112,23 +91,13 @@
 #else
 # include <sys/time.h>
 # define RAND(SEED) (UA_UInt32)rand_r(SEED)
-# ifndef UA_NON_LITTLEENDIAN_ARCHITECTURE
-#  if defined(__linux__) || defined(__APPLE__)
-#   include <endian.h>
-#   if ( __BYTE_ORDER != __LITTLE_ENDIAN )
-#    define UA_NON_LITTLEENDIAN_ARCHITECTURE
-#   endif
-#  endif
-# endif
 #endif
 
 /*************************/
 /* External Dependencies */
 /*************************/
 
-#ifndef UA_AMALGAMATE
-# include "queue.h"
-#endif
+#include "queue.h"
 
 #ifdef UA_MULTITHREADING
 # define _LGPL_SOURCE

+ 4 - 0
tests/CMakeLists.txt

@@ -39,6 +39,10 @@ add_executable(check_services_view check_services_view.c $<TARGET_OBJECTS:open62
 target_link_libraries(check_services_view ${LIBS})
 add_test(services_view ${CMAKE_CURRENT_BINARY_DIR}/check_services_view)
 
+add_executable(check_services_attributes check_services_attributes.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_services_attributes ${LIBS})
+add_test(services_attributes ${CMAKE_CURRENT_BINARY_DIR}/check_services_attributes)
+
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
 add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)

Datei-Diff unterdrückt, da er zu groß ist
+ 1334 - 1333
tests/check_builtin.c


+ 8 - 5
tests/check_memory.c

@@ -51,8 +51,9 @@ START_TEST(encodeShallYieldDecode) {
 	UA_ByteString msg1, msg2;
 	size_t pos = 0;
 	void *obj1 = UA_new(&UA_TYPES[_i]);
-	UA_ByteString_newMembers(&msg1, UA_calcSizeBinary(obj1, &UA_TYPES[_i]));
+	UA_ByteString_newMembers(&msg1, 65000); // fixed buf size
     UA_StatusCode retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
+    msg1.length = pos;
 	if(retval != UA_STATUSCODE_GOOD) {
 		UA_delete(obj1, &UA_TYPES[_i]);
 		UA_ByteString_deleteMembers(&msg1);
@@ -63,13 +64,15 @@ START_TEST(encodeShallYieldDecode) {
 	void *obj2 = UA_new(&UA_TYPES[_i]);
 	pos = 0; retval = UA_decodeBinary(&msg1, &pos, obj2, &UA_TYPES[_i]);
 	ck_assert_msg(retval == UA_STATUSCODE_GOOD, "messages differ idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric);
-	retval = UA_ByteString_newMembers(&msg2, UA_calcSizeBinary(obj2, &UA_TYPES[_i]));
+	retval = UA_ByteString_newMembers(&msg2, 65000);
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	pos = 0; retval = UA_encodeBinary(obj2, &UA_TYPES[_i], &msg2, &pos);
+    msg2.length = pos;
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
 	// then
-	ck_assert_msg(UA_ByteString_equal(&msg1, &msg2) == UA_TRUE, "messages differ idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric);
+	ck_assert_msg(UA_ByteString_equal(&msg1, &msg2) == UA_TRUE, "messages differ idx=%d,nodeid=%i", _i,
+                  UA_TYPES[_i].typeId.identifier.numeric);
 
 	// finally
 	UA_delete(obj1, &UA_TYPES[_i]);
@@ -84,7 +87,7 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
 	UA_ByteString msg1;
 	void *obj1 = UA_new(&UA_TYPES[_i]);
 	size_t pos = 0;
-	UA_ByteString_newMembers(&msg1, UA_calcSizeBinary(obj1, &UA_TYPES[_i]));
+	UA_ByteString_newMembers(&msg1, 65000); // fixed buf size
     UA_StatusCode retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &msg1, &pos);
 	UA_delete(obj1, &UA_TYPES[_i]);
     if(retval != UA_STATUSCODE_GOOD) {
@@ -94,7 +97,7 @@ START_TEST(decodeShallFailWithTruncatedBufferButSurvive) {
 	// when
 	void *obj2 = UA_new(&UA_TYPES[_i]);
 	pos = 0;
-	msg1.length = msg1.length / 2;
+	msg1.length = pos / 2;
 	//fprintf(stderr,"testing %s with half buffer\n",UA_TYPES[_i].name);
 	UA_decodeBinary(&msg1, &pos, obj2, &UA_TYPES[_i]);
 	//then

+ 694 - 0
tests/check_services_attributes.c

@@ -0,0 +1,694 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "check.h"
+#include "server/ua_nodestore.h"
+#include "server/ua_services.h"
+#include "ua_client.h"
+#include "ua_nodeids.h"
+#include "ua_statuscodes.h"
+#include "ua_types.h"
+#include "ua_util.h"
+#include "server/ua_server_internal.h"
+
+
+//#include "server/ua_services_attribute.c"
+
+#ifdef UA_MULTITHREADING
+#include <pthread.h>
+#include <urcu.h>
+#endif
+
+static void copyNames(UA_Node *node, char *name) {
+    node->browseName = UA_QUALIFIEDNAME_ALLOC(0, name);
+    node->displayName = UA_LOCALIZEDTEXT_ALLOC("", name);
+    node->description = UA_LOCALIZEDTEXT_ALLOC("", name);
+}
+
+
+static UA_Server* makeTestSequence(void) {
+	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+	/* VariableNode */
+	UA_Variant *myIntegerVariant = UA_Variant_new();
+	UA_Int32 myInteger = 42;
+	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
+			&UA_TYPES[UA_TYPES_INT32]);
+	const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+	const 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,
+			myIntegerNodeId, parentNodeId, parentReferenceNodeId);
+
+	/* ObjectNode */
+	UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, 50), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE));
+
+	/* ReferenceTypeNode */
+    UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
+    copyNames((UA_Node*)hierarchicalreferences, "Hierarchicalreferences");
+    hierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+    hierarchicalreferences->isAbstract = UA_TRUE;
+    hierarchicalreferences->symmetric  = UA_FALSE;
+    hierarchicalreferences->inverseName = UA_LOCALIZEDTEXT("", "test");
+
+    UA_Server_addNode(server, (UA_Node*)hierarchicalreferences,
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_REFERENCES),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+
+	/* ViewNode */
+    UA_ViewNode *viewtest = UA_ViewNode_new();
+    copyNames((UA_Node*)viewtest, "Viewtest");
+    viewtest->nodeId.identifier.numeric = UA_NS0ID_VIEWNODE;
+
+    UA_Server_addNode(server, (UA_Node*)viewtest,
+                      UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+
+	/* MethodNode */
+    UA_MethodNode *methodtest = UA_MethodNode_new();
+    copyNames((UA_Node*)methodtest, "Methodtest");
+    methodtest->nodeId.identifier.numeric = UA_NS0ID_METHODNODE;
+
+    UA_Server_addNode(server, (UA_Node*)methodtest,
+                      UA_EXPANDEDNODEID_NUMERIC(0, 3),
+                      UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+
+
+	return server;
+}
+
+static UA_VariableNode* makeCompareSequence(void) {
+	UA_VariableNode *node = UA_VariableNode_new();
+	UA_Variant *myIntegerVariant = UA_Variant_new();
+	UA_Int32 myInteger = 42;
+	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
+			&UA_TYPES[UA_TYPES_INT32]);
+	const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+	const 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);
+	node->value.variant=*myIntegerVariant;
+	UA_NodeId_copy(&myIntegerNodeId,&node->nodeId);
+	UA_QualifiedName_copy(&myIntegerName,&node->browseName);
+	UA_String_copy(&myIntegerName.name,&node->displayName.text);
+	UA_ExpandedNodeId parentId;
+	UA_ExpandedNodeId_init(&parentId);
+	UA_NodeId_copy(&parentNodeId,&parentId.nodeId);
+
+	return node;
+}
+
+START_TEST(ReadSingleAttributeValueWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+		ck_assert_int_eq(42, *(UA_Int32* )resp.value.data);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeNodeIdWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_NODEID;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+		UA_NodeId* respval;
+		respval = (UA_NodeId*) resp.value.data;
+		const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+		ck_assert_int_eq(1, respval->namespaceIndex);
+		for (int var = 0; var < respval->identifier.string.length; ++var) {
+			ck_assert_int_eq(myIntegerNodeId.identifier.string.data[var],
+					respval->identifier.string.data[var]);
+		}
+		UA_free(respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeNodeClassWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_NODECLASS;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		//ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODECLASS],resp.value.type);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_BROWSENAME;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		UA_QualifiedName* respval;
+		respval = (UA_QualifiedName*) resp.value.data;
+		const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1,
+				"the answer");
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_QUALIFIEDNAME], resp.value.type);
+		ck_assert_int_eq(1, respval->namespaceIndex);
+		ck_assert_int_eq(myIntegerName.name.length, respval->name.length);
+		for (int var = 0; var < respval->name.length - 1; ++var) {
+			ck_assert_int_eq(myIntegerName.name.data[var],
+					respval->name.data[var]);
+		}
+		UA_free(respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeDisplayNameWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		UA_LocalizedText* respval;
+		respval = (UA_LocalizedText*) resp.value.data;
+		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("locale", "the answer");
+		UA_VariableNode* compNode = makeCompareSequence();
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
+		ck_assert_int_eq(comp.text.length, respval->text.length);
+		for (int var = 0; var < respval->text.length - 1; ++var) {
+			ck_assert_int_eq(comp.text.data[var], respval->text.data[var]);
+		}
+		ck_assert_int_eq(compNode->displayName.locale.length, respval->locale.length);
+		for (int var = 0; var < respval->locale.length - 1; ++var) {
+			ck_assert_int_eq(compNode->displayName.locale.data[var], respval->locale.data[var]);
+		}
+		UA_free(respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DESCRIPTION;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+		UA_LocalizedText* respval;
+		respval = (UA_LocalizedText*) resp.value.data;
+		UA_VariableNode* compNode = makeCompareSequence();
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
+		ck_assert_int_eq(compNode->description.locale.length, respval->locale.length);
+				for (int var = 0; var < respval->locale.length - 1; ++var) {
+					ck_assert_int_eq(compNode->description.locale.data[var], respval->locale.data[var]);
+				}
+		ck_assert_int_eq(compNode->description.text.length, respval->text.length);
+		for (int var = 0; var < respval->text.length - 1; ++var) {
+			ck_assert_int_eq(compNode->description.text.data[var], respval->text.data[var]);
+		}
+		UA_free(respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_WRITEMASK;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+		//UA_UInt32* respval;
+		//respval = (UA_UInt32*) resp.value.data;
+		//UA_VariableNode* compNode = makeCompareSequence();
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+		//ck_assert_int_eq(*(UA_UInt32* )compNode->writeMask,respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ISABSTRACT;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_TRUE);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_SYMMETRIC;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_INVERSENAME;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		/*UA_LocalizedText* respval;
+		respval = (UA_LocalizedText*) resp.value.data;
+		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "test");
+*/
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
+		/*ck_assert_int_eq(comp.text.length, respval->text.length);
+		for (int var = 0; var < respval->text.length - 1; ++var) {
+			ck_assert_int_eq(comp.text.data[var], respval->text.data[var]);
+		}
+		ck_assert_int_eq(comp.locale.length, respval->locale.length);
+		for (int var = 0; var < respval->locale.length - 1; ++var) {
+			ck_assert_int_eq(comp.locale.data[var], respval->locale.data[var]);
+		}
+		UA_free(respval);*/
+	}END_TEST
+
+START_TEST(ReadSingleAttributeContainsNoLoopsWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_VIEWNODE;
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_CONTAINSNOLOOPS;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeEventNotifierWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(1, 50);
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BYTE],resp.value.type);
+		ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		//UA_NodeId* respval;
+		//respval = (UA_NodeId*) resp.value.data;
+		//const UA_VariableNode compNode = makeCompareSequence();
+		//const UA_NodeId comp = compNode;
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeValueRankWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUERANK;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+
+		ck_assert_int_eq(-2, *(UA_Int32* )resp.value.data);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ACCESSLEVEL;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type);
+		ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeUserAccessLevelWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USERACCESSLEVEL;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+		UA_VariableNode* compNode = makeCompareSequence();
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type);
+		ck_assert_int_eq(*(UA_Byte*)resp.value.data, compNode->userAccessLevel);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId =
+				UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		//UA_Double* respval;
+		//respval = (UA_Double*) resp.value.data;
+		//UA_VariableNode* compNode = makeCompareSequence();
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
+		//ck_assert_int_eq(compNode->minimumSamplingInterval,respval);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_HISTORIZING;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+		ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeExecutableWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EXECUTABLE;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+	}END_TEST
+
+START_TEST(ReadSingleAttributeUserExecutableWithoutTimestamp)
+	{
+		UA_Server *server = makeTestSequence();
+
+		UA_DataValue resp;
+		UA_DataValue_init(&resp);
+		UA_ReadRequest rReq;
+		UA_ReadRequest_init(&rReq);
+		rReq.nodesToRead = UA_ReadValueId_new();
+		rReq.nodesToReadSize = 1;
+		rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer");
+		rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
+
+		readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0],
+				&resp);
+
+		ck_assert_int_eq(-1, resp.value.arrayLength);
+		ck_assert_int_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
+	}END_TEST
+
+static Suite * testSuite_services_attributes(void) {
+	Suite *s = suite_create("services_attributes_read");
+
+	TCase *tc_readSingleAttributes = tcase_create("readSingleAttributes");
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeValueWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeNodeIdWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeNodeClassWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeBrowseNameWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeDisplayNameWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeDescriptionWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeWriteMaskWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeUserWriteMaskWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeIsAbstractWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeSymmetricWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeInverseNameWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeContainsNoLoopsWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeEventNotifierWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeDataTypeWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeValueRankWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeArrayDimensionsWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeAccessLevelWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeUserAccessLevelWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeHistorizingWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeExecutableWithoutTimestamp);
+	tcase_add_test(tc_readSingleAttributes,
+			ReadSingleAttributeUserExecutableWithoutTimestamp);
+
+	suite_add_tcase(s, tc_readSingleAttributes);
+	return s;
+}
+
+int main(void) {
+
+	int number_failed = 0;
+	Suite *s;
+	s = testSuite_services_attributes();
+	SRunner *sr = srunner_create(s);
+	srunner_set_log(sr, "test.log");
+	void srunner_set_fork_status(SRunner * sr, enum fork_status CK_NOFORK);
+	srunner_run_all(sr, CK_NORMAL);
+
+	number_failed += srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 0 - 21
tests/check_stack.c

@@ -558,26 +558,6 @@ START_TEST(UA_SecureConversationMessageFooter_copyShallWorkOnInputExample) {
 }
 END_TEST
 
-START_TEST(UA_SecureConversationMessageFooter_calcSizeBinaryShallWorkOnInputExample) {
-	// given
-	UA_SecureConversationMessageFooter src;
-	UA_SecureConversationMessageFooter_init(&src);
-	UA_Byte srcByte[3] = {24, 57, 87};
-	src.padding = srcByte;
-	src.paddingSize = 3;
-	src.signature = 5;
-
-	const UA_SecureConversationMessageFooter srcConst = src;
-
-	UA_Int32 ret;
-
-	// when
-	ret = UA_SecureConversationMessageFooter_calcSizeBinary(&srcConst);
-	// then
-	ck_assert_int_eq(ret, 8);
-}
-END_TEST
-
 START_TEST(UA_SecureConversationMessageFooter_encodeBinaryShallWorkOnInputExample) {
 //	// given
 //	UA_SecureConversationMessageFooter src = {3, (UA_Byte*)"447", 5};;
@@ -639,7 +619,6 @@ Suite *testSuite() {
 	tcase_add_test(tc_transport, UA_SecureConversationMessageHeader_copyShallWorkOnInputExample);
 	tcase_add_test(tc_transport, UA_SequenceHeader_copyShallWorkOnInputExample);
 	tcase_add_test(tc_transport, UA_SecureConversationMessageFooter_copyShallWorkOnInputExample);
-	tcase_add_test(tc_transport, UA_SecureConversationMessageFooter_calcSizeBinaryShallWorkOnInputExample);
 	tcase_add_test(tc_transport, UA_SecureConversationMessageFooter_encodeBinaryShallWorkOnInputExample);
 	tcase_add_test(tc_transport, UA_SecureConversationMessageAbortBody_copyShallWorkOnInputExample);
 	suite_add_tcase(s, tc_transport);

+ 15 - 37
tools/amalgamate.py

@@ -5,37 +5,24 @@ import os.path
 import io
 
 parser = argparse.ArgumentParser()
-parser.add_argument('version', help='version to include')
-parser.add_argument('outfile', help='outfile w/o extension')
+parser.add_argument('version', help='file version')
+parser.add_argument('outfile', help='outfile with extension .c/.h')
 parser.add_argument('inputs', nargs='*', action='store', help='input filenames')
 args = parser.parse_args()
 
 outname = args.outfile.split("/")[-1]
+is_c = False
+if outname[-2:] == ".c":
+    is_c = True
 pos = outname.find(".")
 if pos > 0:
     outname = outname[:pos]
-include_re = re.compile("^#include ([\"<].*[\">]).*$")
+include_re = re.compile("^#include (\".*\").*$")
 guard_re = re.compile("^#(?:(?:ifndef|define) [A-Z_]+_H_|endif /\* [A-Z_]+_H_ \*/)")
 includes = []
 
-is_c = False
-
 print ("Starting amalgamating file "+ args.outfile)
 
-for fname in args.inputs:
-    if("util.h" in fname):
-        is_c = True
-        continue
-    with io.open(fname, encoding="utf8") as infile:
-        print ("Integrating file '" + fname + "'...", end=""),
-        for line in infile:
-            res = include_re.match(line)
-            if res:
-                inc = res.group(1)
-                if not inc in includes and not inc[0] == '"':
-                    includes.append(inc)
-        print ("done."),
-
 file = io.open(args.outfile, 'w')
 file.write(u'''/* THIS IS A SINGLE-FILE DISTRIBUTION CONCATENATED FROM THE OPEN62541 SOURCES 
  * visit http://open62541.org/ for information about this software
@@ -69,30 +56,21 @@ if not is_c:
     for inc in includes:
         file.write(u"#include " + inc + "\n")
 else:
-    file.write(u"#define UA_AMALGAMATE\n")
     file.write(u'''#ifndef UA_DYNAMIC_LINKING
 # define UA_DYNAMIC_LINKING
 #endif\n\n''')
-    for fname in args.inputs:
-        if "ua_config.h" in fname or "ua_util.h" in fname:
-            with io.open(fname, encoding="utf8") as infile:
-                print ("Integrating file '" + fname + "'...", end=""),
-                for line in infile:
-                    file.write(line)
-                print ("done."),
     file.write(u"#include \"" + outname + ".h\"\n")
 
 for fname in args.inputs:
-    if not "util.h" in fname:
-        with io.open(fname, encoding="utf8") as infile:
-            file.write(u"/*********************************** amalgamated original file \"" + fname + u"\" ***********************************/\n")
-            print ("Integrating file '" + fname + "'...", end=""),
-            for line in infile:
-                inc_res = include_re.match(line)
-                guard_res = guard_re.match(line)
-                if not inc_res and not guard_res:
-                    file.write(line)
-            print ("done."),
+    with io.open(fname, encoding="utf8") as infile:
+        file.write(u"\n/*********************************** amalgamated original file \"" + fname + u"\" ***********************************/\n\n")
+        print ("Integrating file '" + fname + "'...", end=""),
+        for line in infile:
+            inc_res = include_re.match(line)
+            guard_res = guard_re.match(line)
+            if not inc_res and not guard_res:
+                file.write(line)
+        print ("done."),
 
 if not is_c:
     file.write(u'''

+ 6 - 8
tools/generate_datatypes.py

@@ -58,10 +58,11 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
                  "NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
                  "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
                  "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse", 
-                 "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken" ];
+                 "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault"]
 
-subscription_types = [ "DeleteMonitoredItemsRequest", "DeleteMonitoredItemsResponse", "NotificationMessage",
-                  "MonitoredItemNotification", "DataChangeNotification", "ModifySubscriptionRequest", "ModifySubscriptionResponse" ];
+subscription_types = ["DeleteMonitoredItemsRequest", "DeleteMonitoredItemsResponse", "NotificationMessage",
+                      "MonitoredItemNotification", "DataChangeNotification", "ModifySubscriptionRequest",
+                      "ModifySubscriptionResponse"]
 
 class TypeDescription(object):
     def __init__(self, name, nodeid, namespaceid):
@@ -186,9 +187,8 @@ class EnumerationType(object):
 #define %s_delete(p) UA_Int32_delete((UA_Int32*)p)
 #define %s_deleteMembers(p) UA_Int32_deleteMembers((UA_Int32*)p)
 #define %s_copy(src, dst) UA_Int32_copy((const UA_Int32*)src, (UA_Int32*)dst)
-#define %s_calcSizeBinary(p) UA_Int32_calcSizeBinary((UA_Int32*)p)
 #define %s_encodeBinary(src, dst, offset) UA_Int32_encodeBinary((UA_Int32*)src, dst, offset)
-#define %s_decodeBinary(src, offset, dst) UA_Int32_decodeBinary(src, offset, (UA_Int32*)dst)''' % tuple(itertools.repeat(self.name, 9))
+#define %s_decodeBinary(src, offset, dst) UA_Int32_decodeBinary(src, offset, (UA_Int32*)dst)''' % tuple(itertools.repeat(self.name, 8))
 
 class OpaqueType(object):
     def __init__(self, name, description = ""):
@@ -220,7 +220,6 @@ class OpaqueType(object):
 #define %s_delete UA_ByteString_delete
 #define %s_deleteMembers UA_ByteString_deleteMembers
 #define %s_copy UA_ByteString_copy
-#define %s_calcSizeBinary UA_ByteString_calcSizeBinary
 #define %s_encodeBinary UA_ByteString_encodeBinary
 #define %s_decodeBinary UA_ByteString_decodeBinary''' % tuple(itertools.repeat(self.name, 8))
 
@@ -327,10 +326,9 @@ class StructType(object):
 #define %s_delete(p) UA_delete(p, %s)
 #define %s_deleteMembers(p) UA_deleteMembers(p, %s)
 #define %s_copy(src, dst) UA_copy(src, dst, %s)
-#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([self.name] + list(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()+"]"], 7))))
 
 def parseTypeDefinitions(xmlDescription, existing_types = OrderedDict()):
     '''Returns an ordered dict that maps names to types. The order is such that