Julius Pfrommer преди 8 години
родител
ревизия
da366aedda
променени са 51 файла, в които са добавени 1281 реда и са изтрити 814 реда
  1. 1 3
      .travis.yml
  2. 3 0
      CMakeLists.txt
  3. 97 0
      FEATURES.md
  4. 5 2
      README.md
  5. 7 0
      doc/CMakeLists.txt
  6. 1 1
      doc/conf.py
  7. 54 15
      doc/index.rst
  8. 0 15
      doc/index_html.rst
  9. 4 4
      doc/internal.rst
  10. 0 67
      doc/introduction.rst
  11. 15 0
      doc/toc.rst
  12. 5 29
      doc/tutorial_client_firstSteps.rst
  13. 10 5
      doc/tutorial_noderelations.rst
  14. 2 2
      doc/tutorial_server_firstSteps.rst
  15. 1 1
      doc/tutorial_server_method.rst
  16. 1 1
      doc/tutorial_server_variables.rst
  17. 6 8
      doc/tutorials.rst
  18. 10 7
      examples/CMakeLists.txt
  19. 15 27
      examples/client_firstSteps.c
  20. 14 3
      examples/server.c
  21. 138 0
      examples/server_inheritance.c
  22. 21 14
      examples/server_udp.c
  23. 4 1
      include/ua_client.h
  24. 7 7
      include/ua_job.h
  25. 34 2
      include/ua_types.h
  26. 85 53
      plugins/ua_network_tcp.c
  27. 45 30
      plugins/ua_network_udp.c
  28. 6 5
      plugins/ua_network_udp.h
  29. 126 97
      src/client/ua_client.c
  30. 81 2
      src/server/ua_nodes.h
  31. 2 0
      src/server/ua_server.c
  32. 85 210
      src/server/ua_server_binary.c
  33. 3 1
      src/server/ua_server_internal.h
  34. 36 1
      src/server/ua_server_worker.c
  35. 4 2
      src/server/ua_services_attribute.c
  36. 9 5
      src/server/ua_services_session.c
  37. 34 32
      src/server/ua_services_subscription.c
  38. 146 78
      src/server/ua_subscription.c
  39. 4 0
      src/server/ua_subscription.h
  40. 120 26
      src/ua_securechannel.c
  41. 7 12
      src/ua_securechannel.h
  42. 0 20
      src/ua_session.c
  43. 0 2
      src/ua_session.h
  44. 3 3
      tests/check_services_subscriptions.c
  45. 2 3
      tests/check_types_builtin.c
  46. 2 1
      tools/pyUANamespace/open62541_XMLPreprocessor.py
  47. 2 2
      tools/pyUANamespace/ua_node_types.py
  48. 19 0
      tools/travis/travis_linux_after_success.sh
  49. 2 14
      tools/travis/travis_push_coverity.sh
  50. 1 0
      tools/travis/travis_push_doc.sh
  51. 2 1
      tools/travis/travis_push_release.sh

+ 1 - 3
.travis.yml

@@ -98,9 +98,7 @@ script:
 - if [ ${TRAVIS_OS_NAME} == "osx" ]; then sh ./tools/travis/travis_osx_script.sh; fi
 - if [ ${TRAVIS_OS_NAME} == "osx" ]; then sh ./tools/travis/travis_osx_script.sh; fi
 
 
 after_success:
 after_success:
-- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "0.2" && ${TRAVIS_PULL_REQUEST} == "false" ) ]]; then sh ./tools/travis/travis_push_doc.sh; fi
-- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" && ${TRAVIS_BRANCH} == "master" && ${TRAVIS_PULL_REQUEST} == "false" ) ]]; then sh ./tools/travis/travis_push_coverity.sh; fi
-- if [[ ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "gcc" ) ]]; then sh ./tools/travis/travis_push_release.sh; fi
+- if [ ${TRAVIS_OS_NAME} == "linux" ]; then sh ./tools/travis/travis_linux_after_success.sh; fi
 
 
 before_deploy:
 before_deploy:
 - rm build -rf && mkdir -p build && cd build
 - rm build -rf && mkdir -p build && cd build

+ 3 - 0
CMakeLists.txt

@@ -251,6 +251,9 @@ endif()
 if(UA_ENABLE_EXTERNAL_NAMESPACES)
 if(UA_ENABLE_EXTERNAL_NAMESPACES)
     list(APPEND exported_headers ${PROJECT_SOURCE_DIR}/include/ua_server_external_ns.h)
     list(APPEND exported_headers ${PROJECT_SOURCE_DIR}/include/ua_server_external_ns.h)
 endif()
 endif()
+if(UA_ENABLE_NONSTANDARD_UDP)
+    list(APPEND exported_headers ${PROJECT_SOURCE_DIR}/plugins/ua_network_udp.h)
+endif()
 
 
 #########################
 #########################
 # Generate source files #
 # Generate source files #

+ 97 - 0
FEATURES.md

@@ -0,0 +1,97 @@
+open62541 Supported Features
+============================
+
+| __**Service**__             |                                 |                      | Comment              |
+|:----------------------------|:--------------------------------|:--------------------:|:---------------------|
+| Discovery Service Set       |                                 |                      |                      |
+|                             | FindServers()                   |  :white_check_mark:  |                      |
+|                             | FindServersOnNetwork()          | :first_quarter_moon: | Branch: feature_mdns |
+|                             | GetEndpoints()                  |  :white_check_mark:  |                      |
+|                             | RegisterServer()                |  :white_check_mark:  |                      |
+|                             | RegisterServer2()               |     :full_moon:      | Branch: master       |
+| Secure Channel Service Set  |                                 |                      |                      |
+|                             | OpenSecureChannel()             |  :white_check_mark:  |                      |
+|                             | CloseSecureChannel()            |  :white_check_mark:  |                      |
+| Session Service Set         |                                 |                      |                      |
+|                             | CreateSession()                 |  :white_check_mark:  |                      |
+|                             | CloseSession()                  |  :white_check_mark:  |                      |
+|                             | ActivateSession()               |  :white_check_mark:  |                      |
+|                             | Cancel()                        |      :new_moon:      |                      |
+| Node Management Service Set |                                 |                      |                      |
+|                             | AddNodes()                      |  :white_check_mark:  |                      |
+|                             | AddReferences()                 |  :white_check_mark:  |                      |
+|                             | DeleteNodes()                   |  :white_check_mark:  |                      |
+|                             | DeleteReferences()              |  :white_check_mark:  |                      |
+| View Service Set            |                                 |                      |                      |
+|                             | Browse()                        |  :white_check_mark:  |                      |
+|                             | BrowseNext()                    |  :white_check_mark:  |                      |
+|                             | TranslateBrowsePathsToNodeIds() |  :white_check_mark:  |                      |
+|                             | RegisterNodes()                 |  :white_check_mark:  |                      |
+|                             | UnregisterNodes()               |  :white_check_mark:  |                      |
+| Query Service Set           |                                 |                      |                      |
+|                             | QueryFirst()                    |      :new_moon:      |                      |
+|                             | QueryNext()                     |      :new_moon:      |                      |
+| Attribute Service Set       |                                 |                      |                      |
+|                             | Read()                          |  :white_check_mark:  |                      |
+|                             | Write()                         |  :white_check_mark:  |                      |
+|                             | HistoryRead()                   |      :new_moon:      |                      |
+|                             | HistoryUpdate()                 |      :new_moon:      |                      |
+| Method Service Set          |                                 |                      |                      |
+|                             | Call()                          |  :white_check_mark:  |                      |
+| MonitoredItems Service Set  |                                 |                      |                      |
+|                             | CreateMonitoredItems()          |  :white_check_mark:  |                      |
+|                             | DeleteMonitoredItems()          |  :white_check_mark:  |                      |
+|                             | ModifyMonitoredItems()          |  :white_check_mark:  |                      |
+|                             | SetMonitoringMode()             |  :white_check_mark:  |                      |
+|                             | SetTriggering()                 |      :new_moon:      |                      |
+| Subscription Service Set    |                                 |                      |                      |
+|                             | CreateSubscription()            |  :white_check_mark:  |                      |
+|                             | ModifySubscription()            |  :white_check_mark:  |                      |
+|                             | SetPublishingMode()             |  :white_check_mark:  |                      |
+|                             | Publish()                       |  :white_check_mark:  |                      |
+|                             | Republish()                     |  :white_check_mark:  |                      |
+|                             | DeleteSubscriptions()           |  :white_check_mark:  |                      |
+|                             | TransferSubscriptions()         |      :new_moon:      |                      |
+
+
+|                                         |                      |                      |
+|:----------------------------------------|:--------------------:|:---------------------|
+| **Transport**                           |                      |                      |
+| UA-TCP UA-SC UA Binary                  |  :white_check_mark:  | OPC.TCP - Binary     |
+| SOAP-HTTP WS-SC UA Binary               |      :new_moon:      | HTTP/HTTPS - Binary  |
+| SOAP-HTTP WS-SC UA XML                  |      :new_moon:      |                      |
+| SOAP-HTTP WS-SC UA XML-UA Binary        |      :new_moon:      |                      |
+| **Encryption**                          |                      |                      |
+| None                                    |  :white_check_mark:  |                      |
+| Basic128Rsa15                           |      :new_moon:      |                      |
+| Basic256                                |      :new_moon:      |                      |
+| Basic256Sha256                          |      :new_moon:      |                      |
+| **Authentication**                      |                      |                      |
+| Anonymous                               |  :white_check_mark:  |                      |
+| User Name Password                      |  :white_check_mark:  |                      |
+| X509 Certificate                        |      :new_moon:      |                      |
+| **Server Facets**                       |                      |                      |
+| Core Server                             |  :white_check_mark:  |                      |
+| Data Access Server                      |  :white_check_mark:  |                      |
+| Embedded Server                         |  :white_check_mark:  |                      |
+| Nano Embedded Device Server             |  :white_check_mark:  |                      |
+| Micro Embedded Device Server            |  :white_check_mark:  |                      |
+| Method Server                           |  :white_check_mark:  |                      |
+| Embedded DataChange Subscription Server |  :white_check_mark:  |                      |
+| Node Management Server                  |  :white_check_mark:  |                      |
+| Standard DataChange Subscription Server | :waxing_gibbous_moon: | Only Deadband Filter missing |
+| Event Subscription Server               |     :new_moon:       |                      |
+| **Client Facets**                       |                      |                      |
+| Base Client Behaviour                   |  :white_check_mark:  |                      |
+| AddressSpace Lookup                     |  :white_check_mark:  |                      |
+| Attribute Read                          |  :white_check_mark:  |                      |
+| DataChange Subscription                 |  :white_check_mark:  |                      |
+| DataAccess                              |  :white_check_mark:  |                      |
+| Discovery                               |  :white_check_mark:  |                      |
+| Event Subscription                      |  :white_check_mark:  |                      |
+| Method call                             |  :white_check_mark:  |                      |
+| Advanced Type                           |  :white_check_mark:  |                      |
+| **Discovery**                           |                      | See Discovery Service Set |
+| Local Disovery Server                   | :first_quarter_moon: | Branch: feature_mdns |
+| Local Discovery Server Multicast Ext.   | :first_quarter_moon: | Branch: feature_mdns |
+| Global Discovery Server                 |      :new_moon:      |                      |

+ 5 - 2
README.md

@@ -12,6 +12,9 @@ open62541 is licensed under the LGPL with a static linking exception. So the **o
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/1864/badge.svg)](https://scan.coverity.com/projects/1864)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/1864/badge.svg)](https://scan.coverity.com/projects/1864)
 
 
 ### Features
 ### Features
+
+For a complete list of features check: [open62541 Features](FEATURES.md)
+
 open62541 implements the OPC UA binary protocol stack as well as a client and server SDK. It currently supports the Micro Embedded Device Server Profile plus some additional features. The final server binaries can be well under 100kb, depending on the size of the information model.
 open62541 implements the OPC UA binary protocol stack as well as a client and server SDK. It currently supports the Micro Embedded Device Server Profile plus some additional features. The final server binaries can be well under 100kb, depending on the size of the information model.
 - Communication Stack
 - Communication Stack
   - OPC UA binary protocol
   - OPC UA binary protocol
@@ -120,8 +123,8 @@ int main(int argc, char *argv[])
     UA_Variant value; /* Variants can hold scalar values and arrays of any type */
     UA_Variant value; /* Variants can hold scalar values and arrays of any type */
     UA_Variant_init(&value);
     UA_Variant_init(&value);
     status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "the.answer"), &value);
     status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "the.answer"), &value);
-    if(status == UA_STATUSCODE_GOOD && UA_Variant_isScalar(&value) &&
-       value.type == &UA_TYPES[UA_TYPES_INT32]) {
+    if(status == UA_STATUSCODE_GOOD &&
+       UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT32])) {
         printf("the value is: %i\n", *(UA_Int32*)value.data);
         printf("the value is: %i\n", *(UA_Int32*)value.data);
     }
     }
 
 

+ 7 - 0
doc/CMakeLists.txt

@@ -23,6 +23,13 @@ configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_server_firstSteps.rst"
 configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_client_firstSteps.rst"
 configure_file("${PROJECT_SOURCE_DIR}/doc/tutorial_client_firstSteps.rst"
                "${DOC_SRC_DIR}/tutorial_client_firstSteps.rst")
                "${DOC_SRC_DIR}/tutorial_client_firstSteps.rst")
 
 
+# Copy example code
+set(EXAMPLES ${PROJECT_SOURCE_DIR}/examples/client_firstSteps.c
+             ${PROJECT_SOURCE_DIR}/examples/server_firstSteps.c
+             ${PROJECT_SOURCE_DIR}/examples/server_variable.c
+             ${PROJECT_SOURCE_DIR}/examples/server_method.c)
+file(COPY ${EXAMPLES} DESTINATION ${DOC_SRC_DIR})
+               
 function(generate_rst in out)
 function(generate_rst in out)
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
   add_custom_command(OUTPUT ${out} DEPENDS ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in}
                      PRE_BUILD COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in} ${out})
                      PRE_BUILD COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2rst.py ${in} ${out})

+ 1 - 1
doc/conf.py

@@ -43,7 +43,7 @@ source_suffix = '.rst'
 #source_encoding = 'utf-8-sig'
 #source_encoding = 'utf-8-sig'
 
 
 # The master toctree document.
 # The master toctree document.
-master_doc = 'index'
+master_doc = 'toc'
 
 
 # General information about the project.
 # General information about the project.
 project = u'open62541'
 project = u'open62541'

Файловите разлики са ограничени, защото са твърде много
+ 54 - 15
doc/index.rst


+ 0 - 15
doc/index_html.rst

@@ -1,15 +0,0 @@
-.. include:: introduction.rst
-
-.. toctree::
-    :hidden:
-
-    self
-    building
-    tutorials
-    types
-    information_modelling
-    services
-    server
-    client
-    constants
-    internal

+ 4 - 4
doc/internal.rst

@@ -1,8 +1,8 @@
 Internals
 Internals
 =========
 =========
 
 
-.. include:: nodestore.rst
+.. toctree::
 
 
-.. include:: connection.rst
-
-.. include:: log.rst
+   nodestore
+   connection
+   log

Файловите разлики са ограничени, защото са твърде много
+ 0 - 67
doc/introduction.rst


+ 15 - 0
doc/toc.rst

@@ -0,0 +1,15 @@
+open62541 Documentation
+#######################
+
+.. toctree::
+
+   index
+   building
+   tutorials
+   types
+   information_modelling
+   services
+   server
+   client
+   constants
+   internal

+ 5 - 29
doc/tutorial_client_firstSteps.rst

@@ -5,41 +5,17 @@ You should already have a basic server from the previous tutorials. open62541
 provides both a server- and clientside API, so creating a client is as easy as
 provides both a server- and clientside API, so creating a client is as easy as
 creating a server. Copy the following into a file `myClient.c`:
 creating a server. Copy the following into a file `myClient.c`:
 
 
-.. code-block:: c
-
-    #include "open62541.h"
-
-    int main(void) {
-      UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
-      UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:16664");
-      if(retval != UA_STATUSCODE_GOOD) {
-          UA_Client_delete(client);
-          return retval;
-      }
-
-      UA_Client_disconnect(client);
-      UA_Client_delete(client);
-      return 0;
-    }
+.. literalinclude:: client_firstSteps.c
+   :language: c
+   :linenos:
+   :lines: 4,5,12,14-
 
 
-Compilation is very much similar to the server example.
+Compilation is similar to the server example.
 
 
 .. code-block:: bash
 .. code-block:: bash
 
 
    $ gcc -std=c99 open6251.c myClient.c -o myClient
    $ gcc -std=c99 open6251.c myClient.c -o myClient
 
 
-Reading a node attibute
-^^^^^^^^^^^^^^^^^^^^^^^
-
-In this example we are going to connect to the server from the second tutorial
-and read the value-attribute of the added variable node.
-
-.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/client_firstSteps.c
-   :language: c
-   :linenos:
-   :lines: 4,5,12,14-
-
-
 Further tasks
 Further tasks
 ^^^^^^^^^^^^^
 ^^^^^^^^^^^^^
 * Try to connect to some other OPC UA server by changing
 * Try to connect to some other OPC UA server by changing

Файловите разлики са ограничени, защото са твърде много
+ 10 - 5
doc/tutorial_noderelations.rst


+ 2 - 2
doc/tutorial_server_firstSteps.rst

@@ -17,10 +17,10 @@ have the ``open62541.c/.h`` files in the current folder.
 
 
 Now create a new C source-file called ``myServer.c`` with the following content:
 Now create a new C source-file called ``myServer.c`` with the following content:
 
 
-.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_firstSteps.c
+.. literalinclude:: server_firstSteps.c
    :language: c
    :language: c
    :linenos:
    :linenos:
-   :lines: 4,12,14-34
+   :lines: 4,12,14-
 
 
 This is all that is needed for a simple OPC UA server. Compile the the server
 This is all that is needed for a simple OPC UA server. Compile the the server
 with GCC using the following command:
 with GCC using the following command:

+ 1 - 1
doc/tutorial_server_method.rst

@@ -16,7 +16,7 @@ The last example presents a way to bind a new method callback to an already
 instantiated method node.
 instantiated method node.
 
 
 
 
-.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_method.c
+.. literalinclude:: server_method.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,5,14,16-
    :lines: 4,5,14,16-

+ 1 - 1
doc/tutorial_server_variables.rst

@@ -10,7 +10,7 @@ connected to a physical process in the background.
 This is the code for a server with a single variable node holding an integer. We
 This is the code for a server with a single variable node holding an integer. We
 will take this example to explain some of the fundamental concepts of open62541.
 will take this example to explain some of the fundamental concepts of open62541.
 
 
-.. literalinclude:: ${PROJECT_SOURCE_DIR}/examples/server_variable.c
+.. literalinclude:: server_variable.c
    :language: c
    :language: c
    :linenos:
    :linenos:
    :lines: 4,13,15-
    :lines: 4,13,15-

+ 6 - 8
doc/tutorials.rst

@@ -1,12 +1,10 @@
 Tutorials
 Tutorials
 =========
 =========
 
 
-.. include:: tutorial_server_firstSteps.rst
+.. toctree::
 
 
-.. include:: tutorial_server_variables.rst
-
-.. include:: tutorial_server_method.rst
-
-.. include:: tutorial_noderelations.rst
-
-.. include:: tutorial_client_firstSteps.rst
+   tutorial_server_firstSteps.rst
+   tutorial_server_variables.rst
+   tutorial_server_method.rst
+   tutorial_noderelations.rst
+   tutorial_client_firstSteps.rst

+ 10 - 7
examples/CMakeLists.txt

@@ -28,7 +28,7 @@ add_executable(server server.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(server ${LIBS})
 target_link_libraries(server ${LIBS})
 
 
 if(UA_ENABLE_NONSTANDARD_UDP)
 if(UA_ENABLE_NONSTANDARD_UDP)
-  add_executable(server_udp server_udp.c $<TARGET_OBJECTS:open62541-object> ${PROJECT_SOURCE_DIR}/plugins/networklayer_udp.c)
+  add_executable(server_udp server_udp.c $<TARGET_OBJECTS:open62541-object> ${PROJECT_SOURCE_DIR}/plugins/ua_network_udp.c)
   target_link_libraries(server_udp ${LIBS})
   target_link_libraries(server_udp ${LIBS})
 endif()
 endif()
 
 
@@ -65,6 +65,15 @@ add_executable(server_readspeed server_readspeed.c $<TARGET_OBJECTS:open62541-ob
 target_include_directories(server_readspeed PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
 target_include_directories(server_readspeed PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
 target_link_libraries(server_readspeed ${LIBS})
 target_link_libraries(server_readspeed ${LIBS})
 
 
+add_executable(server_inheritance server_inheritance.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(server_inheritance ${LIBS})
+
+if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
+  add_executable(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c $<TARGET_OBJECTS:open62541-object>)
+  target_link_libraries(server_nodeset ${LIBS})
+  target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
+endif()
+
 if(UA_ENABLE_METHODCALLS)
 if(UA_ENABLE_METHODCALLS)
   add_executable(server_method server_method.c $<TARGET_OBJECTS:open62541-object>)
   add_executable(server_method server_method.c $<TARGET_OBJECTS:open62541-object>)
   target_link_libraries(server_method ${LIBS})
   target_link_libraries(server_method ${LIBS})
@@ -95,11 +104,5 @@ if(UA_ENABLE_DISCOVERY)
     target_link_libraries(discovery_client_find_servers ${LIBS})
     target_link_libraries(discovery_client_find_servers ${LIBS})
 endif()
 endif()
 
 
-if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
-  add_executable(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c $<TARGET_OBJECTS:open62541-object>)
-  target_link_libraries(server_nodeset ${LIBS})
-  target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
-endif()
-
 add_executable(client_firstSteps client_firstSteps.c $<TARGET_OBJECTS:open62541-object>)
 add_executable(client_firstSteps client_firstSteps.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(client_firstSteps ${LIBS})
 target_link_libraries(client_firstSteps ${LIBS})

+ 15 - 27
examples/client_firstSteps.c

@@ -20,37 +20,25 @@ int main(void) {
         return (int)retval;
         return (int)retval;
     }
     }
 
 
-    //variables to store data
-    UA_DateTime raw_date = 0;
-    UA_String string_date;
+    /* Read the value attribute of the node. UA_Client_readValueAttribute is a
+     * wrapper for the raw read service available as UA_Client_Service_read. */
+    UA_Variant value; /* Variants can hold scalar values and arrays of any type */
+    UA_Variant_init(&value);
 
 
-    /* create a readrequest with one entry */
-    UA_ReadRequest rReq;
-    UA_ReadRequest_init(&rReq);
-    rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]);
-    rReq.nodesToReadSize = 1;
-
-    /* define the node and attribute to be read */
     #define NS0_CURRENT_TIME 2258
     #define NS0_CURRENT_TIME 2258
-    rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(0, NS0_CURRENT_TIME);
-    rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
+    const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, NS0_CURRENT_TIME);
 
 
-    /* call the service and print the result */
-    UA_ReadResponse rResp = UA_Client_Service_read(client, rReq);
-    if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 &&
-       rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) &&
-       rResp.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]) {
-        raw_date = *(UA_DateTime*)rResp.results[0].value.data;
-        printf("raw date is: %" PRId64 "\n", raw_date);
-        string_date = UA_DateTime_toString(raw_date);
+    retval = UA_Client_readValueAttribute(client, nodeId, &value);
+    if(retval == UA_STATUSCODE_GOOD &&
+       UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME])) {
+        UA_DateTime raw_date = *(UA_DateTime*)value.data;
+        UA_String string_date = UA_DateTime_toString(raw_date);
         printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
         printf("string date is: %.*s\n", (int)string_date.length, string_date.data);
+        UA_String_deleteMembers(&string_date);
     }
     }
 
 
-    UA_ReadRequest_deleteMembers(&rReq);
-    UA_ReadResponse_deleteMembers(&rResp);
-    UA_String_deleteMembers(&string_date);
-
-    UA_Client_disconnect(client);
-    UA_Client_delete(client);
-    return (int) UA_STATUSCODE_GOOD;
+    /* Clean up */
+    UA_Variant_deleteMembers(&value);
+    UA_Client_delete(client); /* Disconnects the client internally */
+    return UA_STATUSCODE_GOOD;
 }
 }

+ 14 - 3
examples/server.c

@@ -249,17 +249,23 @@ int main(int argc, char** argv) {
         UA_VariableAttributes attr;
         UA_VariableAttributes attr;
         UA_VariableAttributes_init(&attr);
         UA_VariableAttributes_init(&attr);
         attr.valueRank = -2;
         attr.valueRank = -2;
+        attr.dataType = UA_TYPES[type].typeId;
+#ifndef UA_ENABLE_TYPENAMES
         char name[15];
         char name[15];
 #if defined(_WIN32) && !defined(__MINGW32__)
 #if defined(_WIN32) && !defined(__MINGW32__)
         sprintf_s(name, 15, "%02d", type);
         sprintf_s(name, 15, "%02d", type);
 #else
 #else
         sprintf(name, "%02d", type);
         sprintf(name, "%02d", type);
 #endif
 #endif
-        attr.displayName = UA_LOCALIZEDTEXT("en_US",name);
+        attr.displayName = UA_LOCALIZEDTEXT("en_US", name);
+        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
+#else
+        attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", UA_TYPES[type].typeName);
+        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME_ALLOC(1, UA_TYPES[type].typeName);
+#endif
         attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
         attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
         attr.writeMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
         attr.writeMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
         attr.userWriteMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
         attr.userWriteMask = UA_WRITEMASK_DISPLAYNAME | UA_WRITEMASK_DESCRIPTION;
-        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
 
 
         /* add a scalar node for every built-in type */
         /* add a scalar node for every built-in type */
         void *value = UA_new(&UA_TYPES[type]);
         void *value = UA_new(&UA_TYPES[type]);
@@ -289,10 +295,15 @@ int main(int argc, char** argv) {
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   UA_NODEID_NULL, attr, NULL, NULL);
                                   UA_NODEID_NULL, attr, NULL, NULL);
         UA_Variant_deleteMembers(&attr.value);
         UA_Variant_deleteMembers(&attr.value);
+#ifdef UA_ENABLE_TYPENAMES
+        UA_LocalizedText_deleteMembers(&attr.displayName);
+        UA_QualifiedName_deleteMembers(&qualifiedName);
+#endif
     }
     }
 
 
     /* Hierarchy of depth 10 for CTT testing with forward and inverse references */
     /* Hierarchy of depth 10 for CTT testing with forward and inverse references */
-    /* Enter node "depth 9" in CTT configuration - Project->Settings->Server Test->NodeIds->Paths->Starting Node 1 */
+    /* Enter node "depth 9" in CTT configuration - Project->Settings->Server
+       Test->NodeIds->Paths->Starting Node 1 */
     object_attr.description = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     object_attr.description = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     object_attr.displayName = UA_LOCALIZEDTEXT("en_US","DepthDemo");
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEPTHID),
     UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEPTHID),

+ 138 - 0
examples/server_inheritance.c

@@ -0,0 +1,138 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#include <signal.h>
+
+#ifdef UA_NO_AMALGAMATION
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_network_tcp.h"
+#else
+#include "open62541.h"
+#endif
+
+UA_Boolean running = true;
+static void stopHandler(int sig) {
+    running = false;
+}
+
+int main(void) {
+    signal(SIGINT,  stopHandler);
+    signal(SIGTERM, stopHandler);
+
+    UA_ServerConfig config = UA_ServerConfig_standard;
+    UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
+    config.networkLayers = &nl;
+    config.networkLayersSize = 1;
+    UA_Server *server = UA_Server_new(config);
+
+
+    /* Create a rudimentary objectType
+     *
+     * Type:
+     * + MamalType
+     *  v- Class  = "mamalia"
+     *  v- Species
+     *  o- Abilities
+     *      v- MakeSound
+     *      v- Breathe = True
+     *  + DogType
+     *      v- Species = "Canis"
+     *      v- Name
+     *      o- Abilities
+     *          v- MakeSound = "Wuff"
+     *           v- FetchNewPaper
+     */
+    UA_StatusCode retval;
+    UA_ObjectTypeAttributes otAttr;
+    UA_ObjectTypeAttributes_init(&otAttr);
+
+    otAttr.description = UA_LOCALIZEDTEXT("en_US", "A mamal");
+    otAttr.displayName = UA_LOCALIZEDTEXT("en_US", "MamalType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 10000),
+                                UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "MamalType"), otAttr, NULL, NULL);
+
+    UA_VariableAttributes   vAttr;
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This mamals class");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Class");
+    UA_String classVar = UA_STRING("mamalia");
+    UA_Variant_setScalarCopy(&vAttr.value, &classVar, &UA_TYPES[UA_TYPES_STRING]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10001),
+                              UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "Class"), UA_NODEID_NULL, vAttr, NULL, NULL);
+
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This mamals species");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Species");
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 10002),
+                            UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                            UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NULL, vAttr, NULL, NULL);
+
+
+
+    UA_ObjectTypeAttributes_init(&otAttr);
+    otAttr.description = UA_LOCALIZEDTEXT("en_US", "A dog, subtype of mamal");
+    otAttr.displayName = UA_LOCALIZEDTEXT("en_US", "DogType");
+    UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 20000),
+                                UA_NODEID_NUMERIC(1, 10000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                UA_QUALIFIEDNAME(1, "DogType"), otAttr, NULL, NULL);
+
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This dogs species");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Species");
+    UA_String defaultSpecies = UA_STRING("Canis");
+    UA_Variant_setScalarCopy(&vAttr.value, &defaultSpecies, &UA_TYPES[UA_TYPES_STRING]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20001),
+                            UA_NODEID_NUMERIC(1, 20000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                            UA_QUALIFIEDNAME(1, "Species"), UA_NODEID_NULL, vAttr, NULL, NULL);
+
+    UA_VariableAttributes_init(&vAttr);
+    vAttr.description =  UA_LOCALIZEDTEXT("en_US", "This dogs name");
+    vAttr.displayName =  UA_LOCALIZEDTEXT("en_US", "Name");
+    UA_String defaultName = UA_STRING("unnamed dog");
+    UA_Variant_setScalarCopy(&vAttr.value, &defaultName, &UA_TYPES[UA_TYPES_STRING]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, 20002),
+                              UA_NODEID_NUMERIC(1, 20000), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
+                              UA_QUALIFIEDNAME(1, "Name"), UA_NODEID_NULL, vAttr, NULL, NULL);
+
+
+    /* Instatiate a dog named bello:
+     * (O) Objects
+     *   + O Bello <DogType>
+     *     + Age
+     *     + Name
+     */
+
+    UA_ObjectAttributes oAttr;
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.description = UA_LOCALIZEDTEXT("en_US", "A dog named Bello");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Bello");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "Bello"), UA_NODEID_NUMERIC(1, 20000), oAttr, NULL, NULL);
+
+
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.description = UA_LOCALIZEDTEXT("en_US", "Another dog");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Dog2");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "Dog2"), UA_NODEID_NUMERIC(1, 20000), oAttr, NULL, NULL);
+
+
+    UA_ObjectAttributes_init(&oAttr);
+    oAttr.description = UA_LOCALIZEDTEXT("en_US", "A mamal");
+    oAttr.displayName = UA_LOCALIZEDTEXT("en_US", "Mamal1");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "Mamal1"), UA_NODEID_NUMERIC(1, 10000), oAttr, NULL, NULL);
+
+
+    retval = UA_Server_run(server, &running);
+    UA_Server_delete(server);
+    nl.deleteMembers(&nl);
+    return (int)retval;
+}

+ 21 - 14
examples/server_udp.c

@@ -2,18 +2,21 @@
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  */
  */
-#include <time.h>
 #include <stdio.h>
 #include <stdio.h>
-#include <stdlib.h> 
 #include <signal.h>
 #include <signal.h>
 
 
-// provided by the open62541 lib
-# include "ua_types.h"
-# include "ua_server.h"
+#ifdef UA_NO_AMALGAMATION
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_config_standard.h"
+#include "ua_log_stdout.h"
+#include "ua_network_udp.h"
+#else
+#include "open62541.h"
+#endif
 
 
-// provided by the user, implementations available in the /examples folder
-#include "logger_stdout.h"
-#include "networklayer_udp.h"
+
+UA_Logger logger = UA_Log_Stdout;
 
 
 UA_Boolean running = 1;
 UA_Boolean running = 1;
 
 
@@ -23,11 +26,15 @@ static void stopHandler(int sign) {
 }
 }
 
 
 int main(int argc, char** argv) {
 int main(int argc, char** argv) {
-    signal(SIGINT, stopHandler); /* catches ctrl-c */
+	signal(SIGINT,  stopHandler);
+	signal(SIGTERM, stopHandler);
 
 
-    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    UA_ServerNetworkLayer *nl = ServerNetworkLayerUDP_new(UA_ConnectionConfig_standard, 16664);
-    UA_Server_addNetworkLayer(server, nl);
+	UA_ServerConfig config = UA_ServerConfig_standard;
+	UA_ServerNetworkLayer nl;
+	nl = UA_ServerNetworkLayerUDP(UA_ConnectionConfig_standard, 16664);
+	config.networkLayers = &nl;
+	config.networkLayersSize = 1;
+	UA_Server *server = UA_Server_new(config);
 
 
     // add a variable node to the adresspace
     // add a variable node to the adresspace
     UA_VariableAttributes attr;
     UA_VariableAttributes attr;
@@ -42,9 +49,9 @@ int main(int argc, char** argv) {
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
     UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                               parentReferenceNodeId, myIntegerName,
                               parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, attr, NULL);
+                              UA_NODEID_NULL, attr, NULL, NULL);
 
 
-    UA_StatusCode retval = UA_Server_run(server, 1, &running);
+    UA_StatusCode retval = UA_Server_run(server, &running);
     UA_Server_delete(server);
     UA_Server_delete(server);
 
 
     return (int) retval;
     return (int) retval;

+ 4 - 1
include/ua_client.h

@@ -359,7 +359,10 @@ UA_Client_Service_publish(UA_Client *client, const UA_PublishRequest request) {
 
 
 #endif
 #endif
 
 
-/** .. include:: client_highlevel.rst */
+/**
+ * .. toctree::
+ *
+ *    client_highlevel */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"

+ 7 - 7
include/ua_job.h

@@ -27,15 +27,15 @@ typedef struct UA_Server UA_Server;
 
 
 typedef void (*UA_ServerCallback)(UA_Server *server, void *data);
 typedef void (*UA_ServerCallback)(UA_Server *server, void *data);
 
 
-/** Jobs describe work that is executed once or repeatedly in the server */
+/* Jobs describe work that is executed once or repeatedly in the server */
 typedef struct {
 typedef struct {
     enum {
     enum {
-        UA_JOBTYPE_NOTHING, ///< Guess what?
-        UA_JOBTYPE_DETACHCONNECTION, ///< Detach the connection from the secure channel (but don't delete it)
-        UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER, ///< The binary message is memory managed by the networklayer
-        UA_JOBTYPE_BINARYMESSAGE_ALLOCATED, ///< The binary message was relocated away from the networklayer
-        UA_JOBTYPE_METHODCALL, ///< Call the method as soon as possible
-        UA_JOBTYPE_METHODCALL_DELAYED, ///< Call the method as soon as all previous jobs have finished
+        UA_JOBTYPE_NOTHING,
+        UA_JOBTYPE_DETACHCONNECTION, /* Detach the connection from the secure channel (but don't delete it) */
+        UA_JOBTYPE_BINARYMESSAGE_NETWORKLAYER, /* The binary message is memory managed by the networklayer */
+        UA_JOBTYPE_BINARYMESSAGE_ALLOCATED, /* The binary message was relocated away from the networklayer */
+        UA_JOBTYPE_METHODCALL, /* Call the method as soon as possible */
+        UA_JOBTYPE_METHODCALL_DELAYED, /* Call the method as soon as all previous jobs have finished */
     } type;
     } type;
     union {
     union {
         UA_Connection *closeConnection;
         UA_Connection *closeConnection;

+ 34 - 2
include/ua_types.h

@@ -503,16 +503,46 @@ typedef struct {
     UA_UInt32 *arrayDimensions;   /* The length of each dimension */
     UA_UInt32 *arrayDimensions;   /* The length of each dimension */
 } UA_Variant;
 } UA_Variant;
 
 
+/* Returns true if the variant has no value defined (contains neither an array
+ * nor a scalar value).
+ *
+ * @param v The variant
+ * @return Is the variant empty */
+static UA_INLINE UA_Boolean
+UA_Variant_isEmpty(const UA_Variant *v) {
+    return v->type == NULL;
+}
+
 /* Returns true if the variant contains a scalar value. Note that empty variants
 /* Returns true if the variant contains a scalar value. Note that empty variants
  * contain an array of length -1 (undefined).
  * contain an array of length -1 (undefined).
  *
  *
  * @param v The variant
  * @param v The variant
- * @return Does the variant contain a scalar value. */
+ * @return Does the variant contain a scalar value */
 static UA_INLINE UA_Boolean
 static UA_INLINE UA_Boolean
 UA_Variant_isScalar(const UA_Variant *v) {
 UA_Variant_isScalar(const UA_Variant *v) {
     return (v->arrayLength == 0 && v->data > UA_EMPTY_ARRAY_SENTINEL);
     return (v->arrayLength == 0 && v->data > UA_EMPTY_ARRAY_SENTINEL);
 }
 }
 
 
+/* Returns true if the variant contains a scalar value of the given type.
+ *
+ * @param v The variant
+ * @param type The data type
+ * @return Does the variant contain a scalar value of the given type */
+static UA_INLINE UA_Boolean
+UA_Variant_hasScalarType(const UA_Variant *v, const UA_DataType *type) {
+    return UA_Variant_isScalar(v) && type == v->type;
+}
+
+/* Returns true if the variant contains an array of the given type.
+ *
+ * @param v The variant
+ * @param type The data type
+ * @return Does the variant contain an array of the given type */
+static UA_INLINE UA_Boolean
+UA_Variant_hasArrayType(const UA_Variant *v, const UA_DataType *type) {
+    return (!UA_Variant_isScalar(v)) && type == v->type;
+}
+
 /* Set the variant to a scalar value that already resides in memory. The value
 /* Set the variant to a scalar value that already resides in memory. The value
  * takes on the lifecycle of the variant and is deleted with it.
  * takes on the lifecycle of the variant and is deleted with it.
  *
  *
@@ -835,7 +865,9 @@ UA_Guid UA_EXPORT UA_Guid_random(void);     /* no cryptographic entropy */
  *
  *
  * The following data types were auto-generated from a definition in XML format.
  * The following data types were auto-generated from a definition in XML format.
  *
  *
- * .. include:: types_generated.rst */
+ * .. toctree::
+ *
+ *    types_generated */
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif

+ 85 - 53
plugins/ua_network_tcp.c

@@ -19,10 +19,12 @@
 #include <errno.h>
 #include <errno.h>
 #ifdef _WIN32
 #ifdef _WIN32
 # include <malloc.h>
 # include <malloc.h>
+/* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
 # ifdef SLIST_ENTRY
 # ifdef SLIST_ENTRY
-#  undef SLIST_ENTRY /* Fix redefinition of SLIST_ENTRY on mingw winnt.h */
+#  undef SLIST_ENTRY
 # endif
 # endif
-# define _WINSOCK_DEPRECATED_NO_WARNINGS /* inet_ntoa is deprecated on MSVC but used for compatibility */
+/* inet_ntoa is deprecated on MSVC but used for compatibility */
+# define _WINSOCK_DEPRECATED_NO_WARNINGS
 # include <winsock2.h>
 # include <winsock2.h>
 # include <ws2tcpip.h>
 # include <ws2tcpip.h>
 # define CLOSESOCKET(S) closesocket((SOCKET)S)
 # define CLOSESOCKET(S) closesocket((SOCKET)S)
@@ -54,7 +56,9 @@
 #endif
 #endif
 
 
 /* unsigned int for windows and workaround to a glibc bug */
 /* unsigned int for windows and workaround to a glibc bug */
-#if defined(_WIN32) || defined(__OpenBSD__) || (defined(__GNU_LIBRARY__) && (__GNU_LIBRARY__ <= 6) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16))
+#if defined(_WIN32) || defined(__OpenBSD__) || \
+    (defined(__GNU_LIBRARY__) && (__GNU_LIBRARY__ <= 6) && \
+     (__GLIBC__ <= 2) && (__GLIBC_MINOR__ < 16))
 # define UA_fd_set(fd, fds) FD_SET((unsigned int)fd, fds)
 # define UA_fd_set(fd, fds) FD_SET((unsigned int)fd, fds)
 # define UA_fd_isset(fd, fds) FD_ISSET((unsigned int)fd, fds)
 # define UA_fd_isset(fd, fds) FD_ISSET((unsigned int)fd, fds)
 #else
 #else
@@ -86,7 +90,8 @@ socket_write(UA_Connection *connection, UA_ByteString *buf) {
         /* If the OS throws EMSGSIZE, force a smaller packet size:
         /* If the OS throws EMSGSIZE, force a smaller packet size:
          * size_t bytes_to_send = buf->length - nWritten >  1024 ? 1024 : buf->length - nWritten; */
          * size_t bytes_to_send = buf->length - nWritten >  1024 ? 1024 : buf->length - nWritten; */
             size_t bytes_to_send = buf->length - nWritten;
             size_t bytes_to_send = buf->length - nWritten;
-            n = send((SOCKET)connection->sockfd, (const char*)buf->data + nWritten, WIN32_INT bytes_to_send, 0);
+            n = send((SOCKET)connection->sockfd, (const char*)buf->data + nWritten,
+                     WIN32_INT bytes_to_send, 0);
 #ifdef _WIN32
 #ifdef _WIN32
             if(n < 0 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK)
             if(n < 0 && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK)
 #else
 #else
@@ -120,12 +125,15 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
 # ifdef __APPLE__
 # ifdef __APPLE__
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
 # else
 # else
-        struct timeval tmptv = {(long int)(timeout_usec / 1000000), (long int)(timeout_usec % 1000000)};
+        struct timeval tmptv = {(long int)(timeout_usec / 1000000),
+                                (long int)(timeout_usec % 1000000)};
 # endif
 # endif
-        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tmptv, sizeof(struct timeval));
+        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                             (const char *)&tmptv, sizeof(struct timeval));
 #else
 #else
         DWORD timeout_dw = timeout;
         DWORD timeout_dw = timeout;
-        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_dw, sizeof(DWORD));
+        int ret = setsockopt(connection->sockfd, SOL_SOCKET, SO_RCVTIMEO,
+                             (const char*)&timeout_dw, sizeof(DWORD));
 #endif
 #endif
         if(0 != ret) {
         if(0 != ret) {
             UA_ByteString_deleteMembers(response);
             UA_ByteString_deleteMembers(response);
@@ -144,7 +152,8 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
     #ifdef __APPLE__
     #ifdef __APPLE__
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
         struct timeval tmptv = {(long int)(timeout_usec / 1000000), timeout_usec % 1000000};
     #else
     #else
-        struct timeval tmptv = {(long int)(timeout_usec / 1000000), (long int)(timeout_usec % 1000000)};
+        struct timeval tmptv = {(long int)(timeout_usec / 1000000),
+                                (long int)(timeout_usec % 1000000)};
     #endif
     #endif
         UA_Int32 retval;
         UA_Int32 retval;
 
 
@@ -152,15 +161,18 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
         UA_fd_set(connection->sockfd, &fdset);
         UA_fd_set(connection->sockfd, &fdset);
         retval = select(connection->sockfd+1, &fdset, NULL, NULL, &tmptv);
         retval = select(connection->sockfd+1, &fdset, NULL, NULL, &tmptv);
         if(retval && UA_fd_isset(connection->sockfd, &fdset)) {
         if(retval && UA_fd_isset(connection->sockfd, &fdset)) {
-            ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
+            ret = recv(connection->sockfd, (char*)response->data,
+                       connection->localConf.recvBufferSize, 0);
         } else {
         } else {
             ret = 0;
             ret = 0;
         }
         }
     } else {
     } else {
-        ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
+        ret = recv(connection->sockfd, (char*)response->data,
+                   connection->localConf.recvBufferSize, 0);
     }
     }
 #else
 #else
-    ssize_t ret = recv(connection->sockfd, (char*)response->data, connection->localConf.recvBufferSize, 0);
+    ssize_t ret = recv(connection->sockfd, (char*)response->data,
+                       connection->localConf.recvBufferSize, 0);
 #endif
 #endif
 
 
     if(ret == 0) {
     if(ret == 0) {
@@ -172,9 +184,11 @@ socket_recv(UA_Connection *connection, UA_ByteString *response, UA_UInt32 timeou
         UA_ByteString_deleteMembers(response);
         UA_ByteString_deleteMembers(response);
 #ifdef _WIN32
 #ifdef _WIN32
         const int last_error = WSAGetLastError();
         const int last_error = WSAGetLastError();
-        #define TEST_RETRY (last_error == WSAEINTR || (timeout > 0) ? 0 : (last_error == WSAEWOULDBLOCK))
+        #define TEST_RETRY (last_error == WSAEINTR || (timeout > 0) ? \
+                            0 : (last_error == WSAEWOULDBLOCK))
 #else
 #else
-        #define TEST_RETRY (errno == EINTR || (timeout > 0) ? 0 : (errno == EAGAIN || errno == EWOULDBLOCK))
+        #define TEST_RETRY (errno == EINTR || (timeout > 0) ? \
+                            0 : (errno == EAGAIN || errno == EWOULDBLOCK))
 #endif
 #endif
         if (TEST_RETRY)
         if (TEST_RETRY)
             return UA_STATUSCODE_GOOD; /* retry */
             return UA_STATUSCODE_GOOD; /* retry */
@@ -210,34 +224,37 @@ static void FreeConnectionCallback(UA_Server *server, void *ptr) {
 /***************************/
 /***************************/
 
 
 /**
 /**
- * For the multithreaded mode, assume a single thread that periodically "gets work" from the network
- * layer. In addition, several worker threads are asynchronously calling into the callbacks of the
- * UA_Connection that holds a single connection.
+ * For the multithreaded mode, assume a single thread that periodically "gets
+ * work" from the network layer. In addition, several worker threads are
+ * asynchronously calling into the callbacks of the UA_Connection that holds a
+ * single connection.
  *
  *
- * Creating a connection: When "GetWork" encounters a new connection, it creates a UA_Connection
- * with the socket information. This is added to the mappings array that links sockets to
- * UA_Connection structs.
+ * Creating a connection: When "GetJobs" encounters a new connection, it creates
+ * a UA_Connection with the socket information. This is added to the mappings
+ * array that links sockets to UA_Connection structs.
  *
  *
- * Reading data: In "GetWork", we listen on the sockets in the mappings array. If data arrives (or
- * the connection closes), a WorkItem is created that carries the work and a pointer to the
- * connection.
+ * Reading data: In "GetJobs", we listen on the sockets in the mappings array.
+ * If data arrives (or the connection closes), a WorkItem is created that
+ * carries the work and a pointer to the connection.
  *
  *
- * Closing a connection: Closing can happen in two ways. Either it is triggered by the server in an
- * asynchronous callback. Or the connection is close by the client and this is detected in
- * "GetWork". The server needs to do some internal cleanups (close attached securechannels, etc.).
- * So even when a closed connection is detected in "GetWork", we trigger the server to close the
- * connection (with a WorkItem) and continue from the callback.
+ * Closing a connection: Closing can happen in two ways. Either it is triggered
+ * by the server in an asynchronous callback. Or the connection is close by the
+ * client and this is detected in "GetJobs". The server needs to do some
+ * internal cleanups (close attached securechannels, etc.). So even when a
+ * closed connection is detected in "GetJobs", we trigger the server to close
+ * the connection (with a WorkItem) and continue from the callback.
  *
  *
- * - Server calls close-callback: We close the socket, set the connection-state to closed and add
- *   the connection to a linked list from which it is deleted later. The connection cannot be freed
- *   right away since other threads might still be using it.
+ * - Server calls close-callback: We close the socket, set the connection-state
+ *   to closed and add the connection to a linked list from which it is deleted
+ *   later. The connection cannot be freed right away since other threads might
+ *   still be using it.
  *
  *
- * - GetWork: We remove the connection from the mappings array. In the non-multithreaded case, the
- *   connection is freed. For multithreading, we return a workitem that is delayed, i.e. that is
- *   called only after all workitems created before are finished in all threads. This workitems
- *   contains a callback that goes through the linked list of connections to be freed.
- *
- */
+ * - GetJobs: We remove the connection from the mappings array. In the
+ *   non-multithreaded case, the connection is freed. For multithreading, we
+ *   return a workitem that is delayed, i.e. that is called only after all
+ *   workitems created before are finished in all threads. This workitems
+ *   contains a callback that goes through the linked list of connections to be
+ *   freed. */
 
 
 #define MAXBACKLOG 100
 #define MAXBACKLOG 100
 
 
@@ -300,7 +317,8 @@ ServerNetworkLayerTCP_closeConnection(UA_Connection *connection) {
 #if UA_LOGLEVEL <= 300
 #if UA_LOGLEVEL <= 300
    //cppcheck-suppress unreadVariable
    //cppcheck-suppress unreadVariable
     ServerNetworkLayerTCP *layer = connection->handle;
     ServerNetworkLayerTCP *layer = connection->handle;
-    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | Force closing the connection",
+    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
+                "Connection %i | Force closing the connection",
                 connection->sockfd);
                 connection->sockfd);
 #endif
 #endif
     /* only "shutdown" here. this triggers the select, where the socket is
     /* only "shutdown" here. this triggers the select, where the socket is
@@ -319,11 +337,13 @@ ServerNetworkLayerTCP_add(ServerNetworkLayerTCP *layer, UA_Int32 newsockfd) {
     socklen_t addrlen = sizeof(struct sockaddr_in);
     socklen_t addrlen = sizeof(struct sockaddr_in);
     int res = getpeername(newsockfd, (struct sockaddr*)&addr, &addrlen);
     int res = getpeername(newsockfd, (struct sockaddr*)&addr, &addrlen);
     if(res == 0) {
     if(res == 0) {
-        UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP from %s:%d",
+        UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
+                    "Connection %i | New connection over TCP from %s:%d",
             newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
             newsockfd, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
     } else {
     } else {
-        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Connection %i | New connection over TCP, getpeername failed with errno %i",
-                       newsockfd, errno);
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
+                       "Connection %i | New connection over TCP, "
+                       "getpeername failed with errno %i", newsockfd, errno);
     }
     }
     UA_Connection_init(c);
     UA_Connection_init(c);
     c->sockfd = newsockfd;
     c->sockfd = newsockfd;
@@ -359,7 +379,8 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_Logger logger) {
     if(gethostname(hostname, 255) == 0) {
     if(gethostname(hostname, 255) == 0) {
         char discoveryUrl[256];
         char discoveryUrl[256];
 #ifndef _MSC_VER
 #ifndef _MSC_VER
-        du.length = (size_t)snprintf(discoveryUrl, 255, "opc.tcp://%s:%d", hostname, layer->port);
+        du.length = (size_t)snprintf(discoveryUrl, 255, "opc.tcp://%s:%d",
+                                     hostname, layer->port);
 #else
 #else
         du.length = (size_t)_snprintf_s(discoveryUrl, 255, _TRUNCATE,
         du.length = (size_t)_snprintf_s(discoveryUrl, 255, _TRUNCATE,
                                         "opc.tcp://%s:%d", hostname, layer->port);
                                         "opc.tcp://%s:%d", hostname, layer->port);
@@ -376,37 +397,44 @@ ServerNetworkLayerTCP_start(UA_ServerNetworkLayer *nl, UA_Logger logger) {
     if(newsock < 0)
     if(newsock < 0)
 #endif
 #endif
     {
     {
-        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening the server socket");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
+                       "Error opening the server socket");
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
     /* Set socket options */
     /* Set socket options */
     int optval = 1;
     int optval = 1;
-    if(setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1 ||
+    if(setsockopt(newsock, SOL_SOCKET, SO_REUSEADDR,
+                  (const char *)&optval, sizeof(optval)) == -1 ||
        socket_set_nonblocking(newsock) != UA_STATUSCODE_GOOD) {
        socket_set_nonblocking(newsock) != UA_STATUSCODE_GOOD) {
-        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during setting of server socket options");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
+                       "Error during setting of server socket options");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
     /* Bind socket to address */
     /* Bind socket to address */
-    const struct sockaddr_in serv_addr = {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
-                                          .sin_port = htons(layer->port), .sin_zero = {0}};
+    const struct sockaddr_in serv_addr = {
+        .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY,
+        .sin_port = htons(layer->port), .sin_zero = {0}};
     if(bind(newsock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
     if(bind(newsock, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
-        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error during binding of the server socket");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
+                       "Error during binding of the server socket");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
     /* Start listening */
     /* Start listening */
     if(listen(newsock, MAXBACKLOG) < 0) {
     if(listen(newsock, MAXBACKLOG) < 0) {
-        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error listening on server socket");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK,
+                       "Error listening on server socket");
         CLOSESOCKET(newsock);
         CLOSESOCKET(newsock);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
     layer->serversockfd = (UA_Int32)newsock; /* cast on win32 */
     layer->serversockfd = (UA_Int32)newsock; /* cast on win32 */
-    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK, "TCP network layer listening on %.*s",
+    UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
+                "TCP network layer listening on %.*s",
                 nl->discoveryUrl.length, nl->discoveryUrl.data);
                 nl->discoveryUrl.length, nl->discoveryUrl.data);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
@@ -435,14 +463,16 @@ ServerNetworkLayerTCP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt1
 #endif
 #endif
         {
         {
             socket_set_nonblocking(newsockfd);
             socket_set_nonblocking(newsockfd);
-            /* Send messages directly and do wait to merge packets (disable Nagle's algorithm) */
+            /* Send messages directly and do wait to merge packets (disable
+               Nagle's algorithm) */
             int i = 1;
             int i = 1;
             setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
             setsockopt(newsockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&i, sizeof(i));
             ServerNetworkLayerTCP_add(layer, (UA_Int32)newsockfd);
             ServerNetworkLayerTCP_add(layer, (UA_Int32)newsockfd);
         }
         }
     }
     }
 
 
-    /* alloc enough space for a cleanup-connection and free-connection job per resulted socket */
+    /* alloc enough space for a cleanup-connection and free-connection job per
+       resulted socket */
     if(resultsize == 0)
     if(resultsize == 0)
         return 0;
         return 0;
     UA_Job *js = malloc(sizeof(UA_Job) * (size_t)resultsize * 2);
     UA_Job *js = malloc(sizeof(UA_Job) * (size_t)resultsize * 2);
@@ -493,7 +523,8 @@ static size_t
 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
 ServerNetworkLayerTCP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
     ServerNetworkLayerTCP *layer = nl->handle;
     ServerNetworkLayerTCP *layer = nl->handle;
     UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
     UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
-                "Shutting down the TCP network layer with %d open connection(s)", layer->mappingsSize);
+                "Shutting down the TCP network layer with %d open connection(s)",
+                layer->mappingsSize);
     shutdown((SOCKET)layer->serversockfd,2);
     shutdown((SOCKET)layer->serversockfd,2);
     CLOSESOCKET(layer->serversockfd);
     CLOSESOCKET(layer->serversockfd);
     UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
     UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
@@ -581,7 +612,8 @@ ClientNetworkLayerClose(UA_Connection *connection) {
 
 
 /* we have no networklayer. instead, attach the reusable buffer to the handle */
 /* we have no networklayer. instead, attach the reusable buffer to the handle */
 UA_Connection
 UA_Connection
-UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl, UA_Logger logger) {
+UA_ClientConnectionTCP(UA_ConnectionConfig localConf, const char *endpointUrl,
+                       UA_Logger logger) {
 #ifdef _WIN32
 #ifdef _WIN32
     WORD wVersionRequested;
     WORD wVersionRequested;
     WSADATA wsaData;
     WSADATA wsaData;

+ 45 - 30
plugins/ua_network_udp.c

@@ -1,7 +1,7 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 
-#include "networklayer_udp.h"
+#include "ua_network_udp.h"
 #include <stdlib.h> // malloc, free
 #include <stdlib.h> // malloc, free
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h> // memset
 #include <string.h> // memset
@@ -45,7 +45,7 @@
 static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
 static UA_StatusCode GetMallocedBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) {
     if(length > connection->remoteConf.recvBufferSize)
     if(length > connection->remoteConf.recvBufferSize)
         return UA_STATUSCODE_BADCOMMUNICATIONERROR;
         return UA_STATUSCODE_BADCOMMUNICATIONERROR;
-    return UA_ByteString_newMembers(buf, connection->remoteConf.recvBufferSize);
+    return UA_ByteString_allocBuffer(buf, connection->remoteConf.recvBufferSize);
 }
 }
 
 
 static void ReleaseMallocedBuffer(UA_Connection *connection, UA_ByteString *buf) {
 static void ReleaseMallocedBuffer(UA_Connection *connection, UA_ByteString *buf) {
@@ -65,18 +65,19 @@ typedef struct {
 } UDPConnection;
 } UDPConnection;
 
 
 typedef struct {
 typedef struct {
-    UA_ServerNetworkLayer layer;
     UA_ConnectionConfig conf;
     UA_ConnectionConfig conf;
+	UA_UInt16 port;
     fd_set fdset;
     fd_set fdset;
     UA_Int32 serversockfd;
     UA_Int32 serversockfd;
-    UA_UInt32 port;
+
+	UA_Logger logger; // Set during start
 } ServerNetworkLayerUDP;
 } ServerNetworkLayerUDP;
 
 
 /** Accesses only the sockfd in the handle. Can be run from parallel threads. */
 /** Accesses only the sockfd in the handle. Can be run from parallel threads. */
 static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) {
 static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) {
     UDPConnection *udpc = (UDPConnection*)connection;
     UDPConnection *udpc = (UDPConnection*)connection;
     ServerNetworkLayerUDP *layer = (ServerNetworkLayerUDP*)connection->handle;
     ServerNetworkLayerUDP *layer = (ServerNetworkLayerUDP*)connection->handle;
-    size_t nWritten = 0;
+    long nWritten = 0;
     struct sockaddr_in *sin = NULL;
     struct sockaddr_in *sin = NULL;
 
 
     if (udpc->from.sa_family == AF_INET) {
     if (udpc->from.sa_family == AF_INET) {
@@ -93,11 +94,11 @@ static UA_StatusCode sendUDP(UA_Connection *connection, UA_ByteString *buf) {
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
 
 
-    while (nWritten < (size_t)buf->length) {
-        UA_Int32 n = sendto(layer->serversockfd, buf->data, buf->length, 0,
+    while (nWritten < (long)buf->length) {
+        long n = sendto(layer->serversockfd, buf->data, buf->length, 0,
                             (struct sockaddr*)sin, sizeof(struct sockaddr_in));
                             (struct sockaddr*)sin, sizeof(struct sockaddr_in));
         if(n == -1L) {
         if(n == -1L) {
-            UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "UDP send error %i", errno);
+            UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "UDP send error %i", errno);
             UA_ByteString_deleteMembers(buf);
             UA_ByteString_deleteMembers(buf);
             return UA_STATUSCODE_BADINTERNALERROR;
             return UA_STATUSCODE_BADINTERNALERROR;
         }
         }
@@ -123,11 +124,12 @@ static void closeConnectionUDP(UA_Connection *handle) {
     free(handle);
     free(handle);
 }
 }
 
 
-static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, UA_Logger logger) {
-    layer->layer.logger = logger;
+static UA_StatusCode ServerNetworkLayerUDP_start(UA_ServerNetworkLayer *nl, UA_Logger logger) {
+	ServerNetworkLayerUDP *layer = nl->handle;
+    layer->logger = logger;
     layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0);
     layer->serversockfd = socket(PF_INET, SOCK_DGRAM, 0);
     if(layer->serversockfd < 0) {
     if(layer->serversockfd < 0) {
-        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Error opening socket");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Error opening socket");
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
     const struct sockaddr_in serv_addr =
     const struct sockaddr_in serv_addr =
@@ -136,46 +138,47 @@ static UA_StatusCode ServerNetworkLayerUDP_start(ServerNetworkLayerUDP *layer, U
     int optval = 1;
     int optval = 1;
     if(setsockopt(layer->serversockfd, SOL_SOCKET,
     if(setsockopt(layer->serversockfd, SOL_SOCKET,
                   SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
                   SO_REUSEADDR, (const char *)&optval, sizeof(optval)) == -1) {
-        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Could not setsockopt");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not setsockopt");
         CLOSESOCKET(layer->serversockfd);
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
     if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
     if(bind(layer->serversockfd, (const struct sockaddr *)&serv_addr,
             sizeof(serv_addr)) < 0) {
             sizeof(serv_addr)) < 0) {
-        UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Could not bind the socket");
+        UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Could not bind the socket");
         CLOSESOCKET(layer->serversockfd);
         CLOSESOCKET(layer->serversockfd);
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     }
     }
     socket_set_nonblocking(layer->serversockfd);
     socket_set_nonblocking(layer->serversockfd);
-    UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "Listening for UDP connections on %s:%d",
+    UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "Listening for UDP connections on %s:%d",
                    inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
                    inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port));
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
 
 
-static size_t ServerNetworkLayerUDP_getJobs(ServerNetworkLayerUDP *layer, UA_Job **jobs, UA_UInt16 timeout) {
+static size_t ServerNetworkLayerUDP_getJobs(UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout) {
+	ServerNetworkLayerUDP *layer = nl->handle;
     UA_Job *items = NULL;
     UA_Job *items = NULL;
     setFDSet(layer);
     setFDSet(layer);
     struct timeval tmptv = {0, timeout};
     struct timeval tmptv = {0, timeout};
-    UA_Int32 resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv);
+    int resultsize = select(layer->serversockfd+1, &layer->fdset, NULL, NULL, &tmptv);
     if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) {
     if(resultsize <= 0 || !FD_ISSET(layer->serversockfd, &layer->fdset)) {
         *jobs = items;
         *jobs = items;
         return 0;
         return 0;
     }
     }
-    items = malloc(sizeof(UA_Job)*resultsize);
+    items = malloc(sizeof(UA_Job)*(unsigned long)resultsize);
     // read from established sockets
     // read from established sockets
-    UA_Int32 j = 0;
-    UA_ByteString buf = {-1, NULL};
+	size_t j = 0;
+    UA_ByteString buf = {0, NULL};
     if(!buf.data) {
     if(!buf.data) {
         buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
         buf.data = malloc(sizeof(UA_Byte) * layer->conf.recvBufferSize);
         if(!buf.data)
         if(!buf.data)
-            UA_LOG_WARNING(layer->layer.logger, UA_LOGCATEGORY_NETWORK, "malloc failed");
+            UA_LOG_WARNING(layer->logger, UA_LOGCATEGORY_NETWORK, "malloc failed");
     }
     }
     struct sockaddr sender;
     struct sockaddr sender;
     socklen_t sendsize = sizeof(sender);
     socklen_t sendsize = sizeof(sender);
     bzero(&sender, sizeof(sender));
     bzero(&sender, sizeof(sender));
     ssize_t rec_result = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
     ssize_t rec_result = recvfrom(layer->serversockfd, buf.data, layer->conf.recvBufferSize, 0, &sender, &sendsize);
     if (rec_result > 0) {
     if (rec_result > 0) {
-        buf.length = rec_result;
+        buf.length = (size_t)rec_result;
         UDPConnection *c = malloc(sizeof(UDPConnection));
         UDPConnection *c = malloc(sizeof(UDPConnection));
         if(!c){
         if(!c){
                 free(items);
                 free(items);
@@ -209,25 +212,37 @@ static size_t ServerNetworkLayerUDP_getJobs(ServerNetworkLayerUDP *layer, UA_Job
     return j;
     return j;
 }
 }
 
 
-static UA_Int32 ServerNetworkLayerUDP_stop(ServerNetworkLayerUDP * layer, UA_Job **jobs) {
+static size_t ServerNetworkLayerUDP_stop(UA_ServerNetworkLayer *nl, UA_Job **jobs) {
+	ServerNetworkLayerUDP *layer = nl->handle;
+	UA_LOG_INFO(layer->logger, UA_LOGCATEGORY_NETWORK,
+				"Shutting down the UDP network layer");
     CLOSESOCKET(layer->serversockfd);
     CLOSESOCKET(layer->serversockfd);
     return 0;
     return 0;
 }
 }
 
 
-static void ServerNetworkLayerUDP_deleteMembers(ServerNetworkLayerUDP *layer) {
+static void ServerNetworkLayerUDP_deleteMembers(UA_ServerNetworkLayer *nl) {
+	ServerNetworkLayerUDP *layer = nl->handle;
+	free(layer);
+	UA_String_deleteMembers(&nl->discoveryUrl);
 }
 }
 
 
-UA_ServerNetworkLayer * ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port) {
+UA_ServerNetworkLayer
+UA_ServerNetworkLayerUDP(UA_ConnectionConfig conf, UA_UInt16 port) {
+	UA_ServerNetworkLayer nl;
+	memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
+
     ServerNetworkLayerUDP *layer = malloc(sizeof(ServerNetworkLayerUDP));
     ServerNetworkLayerUDP *layer = malloc(sizeof(ServerNetworkLayerUDP));
     if(!layer)
     if(!layer)
-        return NULL;
+        return nl;
     memset(layer, 0, sizeof(ServerNetworkLayerUDP));
     memset(layer, 0, sizeof(ServerNetworkLayerUDP));
 
 
     layer->conf = conf;
     layer->conf = conf;
     layer->port = port;
     layer->port = port;
-    layer->layer.start = (UA_StatusCode(*)(UA_ServerNetworkLayer*,UA_Logger))ServerNetworkLayerUDP_start;
-    layer->layer.getJobs = (size_t(*)(UA_ServerNetworkLayer*,UA_Job**,UA_UInt16))ServerNetworkLayerUDP_getJobs;
-    layer->layer.stop = (size_t(*)(UA_ServerNetworkLayer*, UA_Job**))ServerNetworkLayerUDP_stop;
-    layer->layer.deleteMembers = (void(*)(UA_ServerNetworkLayer*))ServerNetworkLayerUDP_deleteMembers;
-    return &layer->layer;
+
+	nl.handle = layer;
+	nl.start = ServerNetworkLayerUDP_start;
+	nl.getJobs = ServerNetworkLayerUDP_getJobs;
+	nl.stop = ServerNetworkLayerUDP_stop;
+	nl.deleteMembers = ServerNetworkLayerUDP_deleteMembers;
+	return nl;
 }
 }

+ 6 - 5
plugins/ua_network_udp.h

@@ -1,8 +1,8 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
  * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
 
 
-#ifndef UA_NETWORKLAYER_UDP_H_
-#define UA_NETWORKLAYER_UDP_H_
+#ifndef UA_NETWORK_UDP_H_
+#define UA_NETWORK_UDP_H_
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
@@ -12,13 +12,14 @@ extern "C" {
 #include "ua_client.h"
 #include "ua_client.h"
 
 
 /* Create the UDP networklayer and listen to the specified port */
 /* Create the UDP networklayer and listen to the specified port */
-UA_ServerNetworkLayer UA_EXPORT * ServerNetworkLayerUDP_new(UA_ConnectionConfig conf, UA_UInt32 port);
+UA_ServerNetworkLayer UA_EXPORT
+UA_ServerNetworkLayerUDP(UA_ConnectionConfig conf, UA_UInt16 port);
 
 
 UA_Connection UA_EXPORT
 UA_Connection UA_EXPORT
-ClientNetworkLayerUDP_connect(UA_ConnectionConfig conf, char endpointUrl[], UA_Logger logger);
+UA_ClientConnectionUDP(UA_ConnectionConfig conf, const char *endpointUrl, UA_Logger logger);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 } // extern "C"
 } // extern "C"
 #endif
 #endif
 
 
-#endif /* UA_NETWORKLAYER_UDP_H_ */
+#endif /* UA_NETWORK_UDP_H_ */

+ 126 - 97
src/client/ua_client.c

@@ -104,7 +104,8 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
 
 
     /* Get a buffer */
     /* Get a buffer */
     UA_ByteString message;
     UA_ByteString message;
-    UA_StatusCode retval = client->connection->getSendBuffer(client->connection, UA_MINMESSAGESIZE, &message);
+    UA_StatusCode retval =
+        client->connection->getSendBuffer(client->connection, UA_MINMESSAGESIZE, &message);
     if(retval != UA_STATUSCODE_GOOD)
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
         return retval;
 
 
@@ -173,9 +174,9 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
         conn->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
         conn->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
         conn->remoteConf.protocolVersion = ackMessage.protocolVersion;
         conn->remoteConf.protocolVersion = ackMessage.protocolVersion;
         conn->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
         conn->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
+        conn->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
         if(conn->remoteConf.recvBufferSize < conn->localConf.sendBufferSize)
         if(conn->remoteConf.recvBufferSize < conn->localConf.sendBufferSize)
             conn->localConf.sendBufferSize = conn->remoteConf.recvBufferSize;
             conn->localConf.sendBufferSize = conn->remoteConf.recvBufferSize;
-        conn->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
         if(conn->remoteConf.sendBufferSize < conn->localConf.recvBufferSize)
         if(conn->remoteConf.sendBufferSize < conn->localConf.recvBufferSize)
             conn->localConf.recvBufferSize = conn->remoteConf.sendBufferSize;
             conn->localConf.recvBufferSize = conn->remoteConf.sendBufferSize;
         conn->state = UA_CONNECTION_ESTABLISHED;
         conn->state = UA_CONNECTION_ESTABLISHED;
@@ -187,7 +188,8 @@ static UA_StatusCode HelAckHandshake(UA_Client *client) {
     return retval;
     return retval;
 }
 }
 
 
-static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
+static UA_StatusCode
+SecureChannelHandshake(UA_Client *client, UA_Boolean renew) {
     /* Check if sc is still valid */
     /* Check if sc is still valid */
     if(renew && client->scRenewAt - UA_DateTime_now() > 0)
     if(renew && client->scRenewAt - UA_DateTime_now() > 0)
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
@@ -197,7 +199,8 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
         return UA_STATUSCODE_BADSERVERNOTCONNECTED;
 
 
     UA_SecureConversationMessageHeader messageHeader;
     UA_SecureConversationMessageHeader messageHeader;
-    messageHeader.messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
+    messageHeader.messageHeader.messageTypeAndChunkType =
+        UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
     if(renew)
     if(renew)
         messageHeader.secureChannelId = client->channel->securityToken.channelId;
         messageHeader.secureChannelId = client->channel->securityToken.channelId;
     else
     else
@@ -209,10 +212,12 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
 
 
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
     UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
-    asymHeader.securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
+    asymHeader.securityPolicyUri =
+        UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
 
 
     /* id of opensecurechannelrequest */
     /* id of opensecurechannelrequest */
-    UA_NodeId requestType = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId);
+    UA_NodeId requestType =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST].binaryEncodingId);
 
 
     UA_OpenSecureChannelRequest opnSecRq;
     UA_OpenSecureChannelRequest opnSecRq;
     UA_OpenSecureChannelRequest_init(&opnSecRq);
     UA_OpenSecureChannelRequest_init(&opnSecRq);
@@ -221,10 +226,12 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
     opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
     if(renew) {
     if(renew) {
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to renew the SecureChannel");
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to renew the SecureChannel");
     } else {
     } else {
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
         opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "Requesting to open a SecureChannel");
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to open a SecureChannel");
     }
     }
 
 
     UA_ByteString_copy(&client->channel->clientNonce, &opnSecRq.clientNonce);
     UA_ByteString_copy(&client->channel->clientNonce, &opnSecRq.clientNonce);
@@ -277,7 +284,8 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
     UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &asymHeader);
     UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
     UA_NodeId_decodeBinary(&reply, &offset, &requestType);
     UA_NodeId_decodeBinary(&reply, &offset, &requestType);
-    UA_NodeId expectedRequest = UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
+    UA_NodeId expectedRequest =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
     if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
     if(!UA_NodeId_equal(&requestType, &expectedRequest)) {
         UA_ByteString_deleteMembers(&reply);
         UA_ByteString_deleteMembers(&reply);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -288,7 +296,6 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
     }
     }
 
 
     UA_OpenSecureChannelResponse response;
     UA_OpenSecureChannelResponse response;
-    UA_OpenSecureChannelResponse_init(&response);
     retval = UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
     retval = UA_OpenSecureChannelResponse_decodeBinary(&reply, &offset, &response);
     if(!realloced)
     if(!realloced)
         c->releaseRecvBuffer(c, &reply);
         c->releaseRecvBuffer(c, &reply);
@@ -323,12 +330,15 @@ static UA_StatusCode SecureChannelHandshake(UA_Client *client, UA_Boolean renew)
         UA_ByteString_copy(&response.serverNonce, &client->channel->serverNonce);
         UA_ByteString_copy(&response.serverNonce, &client->channel->serverNonce);
 
 
         if(renew)
         if(renew)
-            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel renewed");
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "SecureChannel renewed");
         else
         else
-            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel opened");
+            UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                         "SecureChannel opened");
     } else {
     } else {
-        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL, "SecureChannel could "
-                     "not be opened / renewed with statuscode %i", retval);
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "SecureChannel could not be opened / "
+                     "renewed with statuscode %i", retval);
     }
     }
     UA_OpenSecureChannelResponse_deleteMembers(&response);
     UA_OpenSecureChannelResponse_deleteMembers(&response);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -368,12 +378,14 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
 
 
     if(response.responseHeader.serviceResult) {
     if(response.responseHeader.serviceResult) {
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "ActivateSession failed with statuscode 0x%08x", response.responseHeader.serviceResult);
+                     "ActivateSession failed with statuscode 0x%08x",
+                     response.responseHeader.serviceResult);
     }
     }
 
 
+    UA_StatusCode retval = response.responseHeader.serviceResult;
     UA_ActivateSessionRequest_deleteMembers(&request);
     UA_ActivateSessionRequest_deleteMembers(&request);
     UA_ActivateSessionResponse_deleteMembers(&response);
     UA_ActivateSessionResponse_deleteMembers(&response);
-    return response.responseHeader.serviceResult; // not deleted
+    return retval;
 }
 }
 
 
 /**
 /**
@@ -381,12 +393,14 @@ static UA_StatusCode ActivateSession(UA_Client *client) {
  * Memory is allocated for endpointDescription array
  * Memory is allocated for endpointDescription array
  */
  */
 static UA_StatusCode
 static UA_StatusCode
-GetEndpoints(UA_Client *client, size_t* endpointDescriptionsSize, UA_EndpointDescription** endpointDescriptions) {
+GetEndpoints(UA_Client *client, size_t* endpointDescriptionsSize,
+             UA_EndpointDescription** endpointDescriptions) {
     UA_GetEndpointsRequest request;
     UA_GetEndpointsRequest request;
     UA_GetEndpointsRequest_init(&request);
     UA_GetEndpointsRequest_init(&request);
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timestamp = UA_DateTime_now();
     request.requestHeader.timeoutHint = 10000;
     request.requestHeader.timeoutHint = 10000;
-    request.endpointUrl = client->endpointUrl; // assume the endpointurl outlives the service call
+    // assume the endpointurl outlives the service call
+    request.endpointUrl = client->endpointUrl; 
 
 
     UA_GetEndpointsResponse response;
     UA_GetEndpointsResponse response;
     UA_GetEndpointsResponse_init(&response);
     UA_GetEndpointsResponse_init(&response);
@@ -394,16 +408,16 @@ GetEndpoints(UA_Client *client, size_t* endpointDescriptionsSize, UA_EndpointDes
                         &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
                         &response, &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE]);
 
 
     if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
     if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        UA_StatusCode retval = response.responseHeader.serviceResult;
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
         UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                     "GetEndpointRequest failed with statuscode 0x%08x", response.responseHeader.serviceResult);
+                     "GetEndpointRequest failed with statuscode 0x%08x", retval);
         UA_GetEndpointsResponse_deleteMembers(&response);
         UA_GetEndpointsResponse_deleteMembers(&response);
-        return response.responseHeader.serviceResult;
+        return retval;
     }
     }
-
+    *endpointDescriptions = response.endpoints;
     *endpointDescriptionsSize = response.endpointsSize;
     *endpointDescriptionsSize = response.endpointsSize;
-    *endpointDescriptions = UA_Array_new(response.endpointsSize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
-    for(size_t i=0;i<response.endpointsSize;i++)
-        UA_EndpointDescription_copy(&response.endpoints[i], &(*endpointDescriptions)[i]);
+    response.endpoints = NULL;
+    response.endpointsSize = 0;
     UA_GetEndpointsResponse_deleteMembers(&response);
     UA_GetEndpointsResponse_deleteMembers(&response);
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
@@ -425,7 +439,8 @@ static UA_StatusCode EndpointsHandshake(UA_Client *client) {
         UA_EndpointDescription* endpoint = &endpointArray[i];
         UA_EndpointDescription* endpoint = &endpointArray[i];
         /* look out for binary transport endpoints */
         /* look out for binary transport endpoints */
         //NODE: Siemens returns empty ProfileUrl, we will accept it as binary
         //NODE: Siemens returns empty ProfileUrl, we will accept it as binary
-        if(endpoint->transportProfileUri.length!=0 && !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
+        if(endpoint->transportProfileUri.length!=0 &&
+           !UA_String_equal(&endpoint->transportProfileUri, &binaryTransport))
             continue;
             continue;
         /* look out for an endpoint without security */
         /* look out for an endpoint without security */
         if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone))
         if(!UA_String_equal(&endpoint->securityPolicyUri, &securityNone))
@@ -481,9 +496,10 @@ static UA_StatusCode SessionHandshake(UA_Client *client) {
 
 
     UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
     UA_NodeId_copy(&response.authenticationToken, &client->authenticationToken);
 
 
+    UA_StatusCode retval = response.responseHeader.serviceResult;
     UA_CreateSessionRequest_deleteMembers(&request);
     UA_CreateSessionRequest_deleteMembers(&request);
     UA_CreateSessionResponse_deleteMembers(&response);
     UA_CreateSessionResponse_deleteMembers(&response);
-    return response.responseHeader.serviceResult; // not deleted
+    return retval;
 }
 }
 
 
 static UA_StatusCode CloseSession(UA_Client *client) {
 static UA_StatusCode CloseSession(UA_Client *client) {
@@ -497,9 +513,10 @@ static UA_StatusCode CloseSession(UA_Client *client) {
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
     __UA_Client_Service(client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
                         &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
                         &response, &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE]);
 
 
+    UA_StatusCode retval = response.responseHeader.serviceResult;
     UA_CloseSessionRequest_deleteMembers(&request);
     UA_CloseSessionRequest_deleteMembers(&request);
     UA_CloseSessionResponse_deleteMembers(&response);
     UA_CloseSessionResponse_deleteMembers(&response);
-    return response.responseHeader.serviceResult; // not deleted
+    return retval;
 }
 }
 
 
 static UA_StatusCode CloseSecureChannel(UA_Client *client) {
 static UA_StatusCode CloseSecureChannel(UA_Client *client) {
@@ -611,7 +628,9 @@ UA_Client_connect(UA_Client *client, const char *endpointUrl) {
     }
     }
 
 
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    *client->connection = client->config.connectionFunc(UA_ConnectionConfig_standard, endpointUrl, client->config.logger);
+    *client->connection =
+        client->config.connectionFunc(UA_ConnectionConfig_standard,
+                                      endpointUrl, client->config.logger);
     if(client->connection->state != UA_CONNECTION_OPENING) {
     if(client->connection->state != UA_CONNECTION_OPENING) {
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
         goto cleanup;
         goto cleanup;
@@ -671,24 +690,84 @@ UA_StatusCode UA_Client_manuallyRenewSecureChannel(UA_Client *client) {
 /* Raw Services */
 /* Raw Services */
 /****************/
 /****************/
 
 
+struct ResponseDescription {
+    UA_Client *client;
+    UA_Boolean processed;
+    UA_UInt32 requestId;
+    void *response;
+    const UA_DataType *responseType;
+};
+
+static void
+processServiceResponse(struct ResponseDescription *rd, UA_SecureChannel *channel,
+                       UA_MessageType messageType, UA_UInt32 requestId, UA_ByteString *message) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    UA_ResponseHeader *respHeader = (UA_ResponseHeader*)rd->response;
+    rd->processed = true;
+
+    /* Check that the request id matches */
+    /* Todo: we need to demux async responses since a publish responses may come at any time */
+    if(requestId != rd->requestId) {
+        UA_LOG_ERROR(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Reply answers the wrong requestId. Async services are not yet implemented.");
+        retval = UA_STATUSCODE_BADINTERNALERROR;
+        goto finish;
+    }
+
+    /* Check that the response type matches */
+    const UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, rd->responseType->binaryEncodingId);
+    const UA_NodeId serviceFaultNodeId =
+        UA_NODEID_NUMERIC(0, UA_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId);
+    size_t offset = 0;
+    UA_NodeId responseId;
+    retval = UA_NodeId_decodeBinary(message, &offset, &responseId);
+    if(retval != UA_STATUSCODE_GOOD)
+        goto finish;
+    if(!UA_NodeId_equal(&responseId, &expectedNodeId)) {
+        if(UA_NodeId_equal(&responseId, &serviceFaultNodeId)) {
+            /* Take the statuscode from the servicefault */
+            retval = UA_decodeBinary(message, &offset, rd->response, &UA_TYPES[UA_TYPES_SERVICEFAULT]);
+        } else {
+            UA_LOG_ERROR(rd->client->config.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);
+            UA_NodeId_deleteMembers(&responseId);
+            retval = UA_STATUSCODE_BADINTERNALERROR;
+        }
+        goto finish;
+    }
+
+    /* Decode the response */
+    retval = UA_decodeBinary(message, &offset, rd->response, rd->responseType);
+
+ finish:
+    if(retval == UA_STATUSCODE_GOOD) {
+        UA_LOG_DEBUG(rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
+                     "Received a response of type %i", responseId.identifier.numeric);
+    } else {
+        if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
+            retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
+        UA_LOG_INFO(rd->client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response");
+        respHeader->serviceResult = retval;
+    }
+}
+
 void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType,
 void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *requestType,
                          void *response, const UA_DataType *responseType) {
                          void *response, const UA_DataType *responseType) {
-    /* Requests always begin witih a RequestHeader, therefore we can cast. */
-    UA_RequestHeader *request = (void*)(uintptr_t)r;
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    UA_init(response, responseType);
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
     UA_ResponseHeader *respHeader = (UA_ResponseHeader*)response;
 
 
-    /* make sure we have a valid session */
-    retval = UA_Client_manuallyRenewSecureChannel(client);
+    /* Make sure we have a valid session */
+    UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         respHeader->serviceResult = retval;
         respHeader->serviceResult = retval;
         client->state = UA_CLIENTSTATE_ERRORED;
         client->state = UA_CLIENTSTATE_ERRORED;
         return;
         return;
     }
     }
 
 
-    /* handling request parameters */
+    /* Handling request parameters */
     //here const *r is 'violated'
     //here const *r is 'violated'
+    UA_RequestHeader *request = (void*)(uintptr_t)r;
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
     UA_NodeId_copy(&client->authenticationToken, &request->authenticationToken);
     request->timestamp = UA_DateTime_now();
     request->timestamp = UA_DateTime_now();
     request->requestHandle = ++client->requestHandle;
     request->requestHandle = ++client->requestHandle;
@@ -704,11 +783,15 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
         else
         else
             respHeader->serviceResult = retval;
             respHeader->serviceResult = retval;
         client->state = UA_CLIENTSTATE_ERRORED;
         client->state = UA_CLIENTSTATE_ERRORED;
-        return;
+        goto finish;
     }
     }
 
 
+    /* Requests always begin witih a RequestHeader, therefore we can cast. */
+    UA_init(response, responseType);
+    struct ResponseDescription rd = (struct ResponseDescription){
+        client, false, requestId, response, responseType};
+
     /* Retrieve the response */
     /* Retrieve the response */
-    // Todo: push this into the generic securechannel implementation for client and server
     UA_ByteString reply;
     UA_ByteString reply;
     UA_ByteString_init(&reply);
     UA_ByteString_init(&reply);
     UA_Boolean realloced = false;
     UA_Boolean realloced = false;
@@ -718,68 +801,14 @@ void __UA_Client_Service(UA_Client *client, const void *r, const UA_DataType *re
         if(retval != UA_STATUSCODE_GOOD) {
         if(retval != UA_STATUSCODE_GOOD) {
             respHeader->serviceResult = retval;
             respHeader->serviceResult = retval;
             client->state = UA_CLIENTSTATE_ERRORED;
             client->state = UA_CLIENTSTATE_ERRORED;
-            //free token
-            UA_NodeId_deleteMembers(&request->authenticationToken);
-            return;
+            goto finish;
         }
         }
-    } while(!reply.data);
-
-    size_t offset = 0;
-    UA_SecureConversationMessageHeader msgHeader;
-    retval |= UA_SecureConversationMessageHeader_decodeBinary(&reply, &offset, &msgHeader);
-    UA_SymmetricAlgorithmSecurityHeader symHeader;
-    retval |= UA_SymmetricAlgorithmSecurityHeader_decodeBinary(&reply, &offset, &symHeader);
-    UA_SequenceHeader seqHeader;
-    retval |= UA_SequenceHeader_decodeBinary(&reply, &offset, &seqHeader);
-    UA_NodeId responseId;
-    retval |= UA_NodeId_decodeBinary(&reply, &offset, &responseId);
-    UA_NodeId expectedNodeId = UA_NODEID_NUMERIC(0, responseType->binaryEncodingId);
-
-    if(retval != UA_STATUSCODE_GOOD)
-        goto finish;
-
-    /* Does the sequence number match? */
-    retval = UA_SecureChannel_processSequenceNumber(seqHeader.sequenceNumber, client->channel);
-    if (retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO_CHANNEL(client->config.logger, client->channel,
-                            "The sequence number was not increased by one. Got %i, expected %i",
-                            seqHeader.sequenceNumber, client->channel->receiveSequenceNumber + 1);
-        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_TYPES[UA_TYPES_SERVICEFAULT].binaryEncodingId) {
-            UA_LOG_ERROR(client->config.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);
-    if(retval == UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED)
-        retval = UA_STATUSCODE_BADRESPONSETOOLARGE;
+        if(!reply.data)
+            break;
+        UA_SecureChannel_processChunks(client->channel, &reply,
+                                       (UA_ProcessMessageCallback*)processServiceResponse, &rd);
+    } while(!rd.processed);
 
 
  finish:
  finish:
-    UA_SymmetricAlgorithmSecurityHeader_deleteMembers(&symHeader);
-    if(!realloced)
-        client->connection->releaseRecvBuffer(client->connection, &reply);
-    else
-        UA_ByteString_deleteMembers(&reply);
-
-    if(retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT, "Error receiving the response");
-        client->state = UA_CLIENTSTATE_FAULTED;
-        respHeader->serviceResult = retval;
-    } else {
-      client->state = UA_CLIENTSTATE_CONNECTED;
-    }
-    //free token
     UA_NodeId_deleteMembers(&request->authenticationToken);
     UA_NodeId_deleteMembers(&request->authenticationToken);
-    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_CLIENT,
-                 "Received a response of type %i", responseId.identifier.numeric);
 }
 }

+ 81 - 2
src/server/ua_nodes.h

@@ -243,8 +243,87 @@ typedef struct {
  * ReferenceTypeNode
  * ReferenceTypeNode
  * -----------------
  * -----------------
  *
  *
- * The OPC UA standard defines a set of ReferenceTypes as a mandatory part of
- * OPC UA information models. */
+ * Each reference between two nodes is typed with a ReferenceType that gives
+ * meaning to the relation. The OPC UA standard defines a set of ReferenceTypes
+ * as a mandatory part of OPC UA information models.
+ *
+ * - Abstract ReferenceTypes cannot be used in actual references and are only
+ *   used to structure the ReferenceTypes hierarchy
+ * - Symmetric references have the same meaning from the perspective of the
+ *   source and target node
+ *
+ * The figure below shows the hierarchy of the standard ReferenceTypes (arrows
+ * indicate a ``hasSubType`` relation). Please refer to Part 3 of the OPC UA
+ * specification for the full semantics of each ReferenceType. The ReferenceType
+ * hierarchy can be extended with user-defined ReferenceTypes. Many Companion
+ * Specifications for OPC UA define new ReferenceTypes to be used in their
+ * domain of interest.
+ *
+ * .. graphviz::
+ *
+ *    digraph tree {
+ *
+ *    node [height=0, shape=box, fillcolor="#E5E5E5", concentrate=true]
+ *
+ *    references [label="References\n(Abstract, Symmetric)"]
+ *    hierarchical_references [label="HierarchicalReferences\n(Abstract)"]
+ *    references -> hierarchical_references
+ *
+ *    nonhierarchical_references [label="NonHierarchicalReferences\n(Abstract, Symmetric)"]
+ *    references -> nonhierarchical_references
+ *
+ *    haschild [label="HasChild\n(Abstract)"]
+ *    hierarchical_references -> haschild
+ *
+ *    aggregates [label="Aggregates\n(Abstract)"]
+ *    haschild -> aggregates
+ *
+ *    organizes [label="Organizes"]
+ *    hierarchical_references -> organizes
+ *
+ *    hascomponent [label="HasComponent"]
+ *    aggregates -> hascomponent
+ *
+ *    hasorderedcomponent [label="HasOrderedComponent"]
+ *    hascomponent -> hasorderedcomponent
+ *
+ *    hasproperty [label="HasProperty"]
+ *    aggregates -> hasproperty
+ *
+ *    hassubtype [label="HasSubtype"]
+ *    haschild -> hassubtype
+ *
+ *    hasmodellingrule [label="HasModellingRule"]
+ *    nonhierarchical_references -> hasmodellingrule
+ *
+ *    hastypedefinition [label="HasTypeDefinition"]
+ *    nonhierarchical_references -> hastypedefinition
+ *
+ *    hasencoding [label="HasEncoding"]
+ *    nonhierarchical_references -> hasencoding
+ *
+ *    hasdescription [label="HasDescription"]
+ *    nonhierarchical_references -> hasdescription
+ *
+ *    haseventsource [label="HasEventSource"]
+ *    hierarchical_references -> haseventsource
+ *
+ *    hasnotifier [label="HasNotifier"]
+ *    hierarchical_references -> hasnotifier
+ *
+ *    generatesevent [label="GeneratesEvent"]
+ *    nonhierarchical_references -> generatesevent
+ *
+ *    alwaysgeneratesevent [label="AlwaysGeneratesEvent"]
+ *    generatesevent -> alwaysgeneratesevent
+ *
+ *    {rank=same hierarchical_references nonhierarchical_references}
+ *    {rank=same generatesevent haseventsource hasmodellingrule
+ *               hasencoding hassubtype}
+ *    {rank=same alwaysgeneratesevent hasproperty}
+ *
+ *    }
+ */
 typedef struct {
 typedef struct {
     UA_NODE_BASEATTRIBUTES
     UA_NODE_BASEATTRIBUTES
     UA_Boolean isAbstract;
     UA_Boolean isAbstract;

+ 2 - 0
src/server/ua_server.c

@@ -468,6 +468,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     rcu_init();
     rcu_init();
     cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
     cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
     cds_lfs_init(&server->mainLoopJobs);
     cds_lfs_init(&server->mainLoopJobs);
+#else
+    SLIST_INIT(&server->delayedCallbacks);
 #endif
 #endif
 
 
     /* uncomment for non-reproducible server runs */
     /* uncomment for non-reproducible server runs */

+ 85 - 210
src/server/ua_server_binary.c

@@ -35,34 +35,6 @@ sendError(UA_SecureChannel *channel, const UA_ByteString *msg,
     UA_ResponseHeader_deleteMembers(responseHeader);
     UA_ResponseHeader_deleteMembers(responseHeader);
 }
 }
 
 
-/* Returns a complete decoded request (without securechannel headers + padding)
-   or UA_BYTESTRING_NULL */
-static UA_ByteString
-processChunk(UA_SecureChannel *channel, UA_Server *server,
-             const UA_TcpMessageHeader *messageHeader, UA_UInt32 requestId,
-             const UA_ByteString *msg, size_t offset, size_t chunksize,
-             UA_Boolean *deleteRequest) {
-    UA_ByteString bytes = UA_BYTESTRING_NULL;
-    switch(messageHeader->messageTypeAndChunkType & 0xff000000) {
-    case UA_CHUNKTYPE_INTERMEDIATE:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Chunk message");
-        UA_SecureChannel_appendChunk(channel, requestId, msg, offset, chunksize);
-        break;
-    case UA_CHUNKTYPE_FINAL:
-        UA_LOG_TRACE_CHANNEL(server->config.logger, channel, "Final chunk message");
-        bytes = UA_SecureChannel_finalizeChunk(channel, requestId, msg, offset,
-                                               chunksize, deleteRequest);
-        break;
-    case UA_CHUNKTYPE_ABORT:
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Chunk aborted");
-        UA_SecureChannel_removeChunk(channel, requestId);
-        break;
-    default:
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Unknown chunk type");
-    }
-    return bytes;
-}
-
 static void
 static void
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
 getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
                    const UA_DataType **responseType, UA_Service *service,
                    const UA_DataType **responseType, UA_Service *service,
@@ -336,35 +308,31 @@ static void processHEL(UA_Connection *connection, const UA_ByteString *msg, size
 
 
 /* OPN -> Open up/renew the securechannel */
 /* OPN -> Open up/renew the securechannel */
 static void
 static void
-processOPN(UA_Connection *connection, UA_Server *server,
-           const UA_ByteString *msg, size_t *offset) {
-    if(connection->state != UA_CONNECTION_ESTABLISHED) {
-        connection->close(connection);
-        return;
-    }
-
-    UA_UInt32 channelId;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-
+processOPN(UA_Server *server, UA_Connection *connection,
+           UA_UInt32 channelId, const UA_ByteString *msg) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    /* Called before HEL */
+    if(connection->state != UA_CONNECTION_ESTABLISHED)
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
     /* Opening up a channel with a channelid already set */
     /* Opening up a channel with a channelid already set */
     if(!connection->channel && channelId != 0)
     if(!connection->channel && channelId != 0)
-        retval |= UA_STATUSCODE_BADREQUESTTYPEINVALID;
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
     /* Renew a channel with the wrong channelid */
     /* Renew a channel with the wrong channelid */
     if(connection->channel && channelId != connection->channel->securityToken.channelId)
     if(connection->channel && channelId != connection->channel->securityToken.channelId)
-        retval |= UA_STATUSCODE_BADREQUESTTYPEINVALID;
+        retval = UA_STATUSCODE_BADCOMMUNICATIONERROR;
 
 
     /* Decode the request */
     /* Decode the request */
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_AsymmetricAlgorithmSecurityHeader asymHeader;
     UA_SequenceHeader seqHeader;
     UA_SequenceHeader seqHeader;
     UA_NodeId requestType;
     UA_NodeId requestType;
     UA_OpenSecureChannelRequest r;
     UA_OpenSecureChannelRequest r;
+    size_t offset = 0;
+    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, &offset, &asymHeader);
+    retval |= UA_SequenceHeader_decodeBinary(msg, &offset, &seqHeader);
+    retval |= UA_NodeId_decodeBinary(msg, &offset, &requestType);
+    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, &offset, &r);
 
 
-    retval |= UA_AsymmetricAlgorithmSecurityHeader_decodeBinary(msg, offset, &asymHeader);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &seqHeader);
-    retval |= UA_NodeId_decodeBinary(msg, offset, &requestType);
-    retval |= UA_OpenSecureChannelRequest_decodeBinary(msg, offset, &r);
-
-    /* Could not decode or wrong service type */
+    /* Error occured */
     if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != 446) {
     if(retval != UA_STATUSCODE_GOOD || requestType.identifier.numeric != 446) {
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_NodeId_deleteMembers(&requestType);
         UA_NodeId_deleteMembers(&requestType);
@@ -379,9 +347,8 @@ processOPN(UA_Connection *connection, UA_Server *server,
     Service_OpenSecureChannel(server, connection, &r, &p);
     Service_OpenSecureChannel(server, connection, &r, &p);
     UA_OpenSecureChannelRequest_deleteMembers(&r);
     UA_OpenSecureChannelRequest_deleteMembers(&r);
 
 
-    UA_SecureChannel *channel = connection->channel;
-
     /* Opening the channel failed */
     /* Opening the channel failed */
+    UA_SecureChannel *channel = connection->channel;
     if(!channel) {
     if(!channel) {
         UA_OpenSecureChannelResponse_deleteMembers(&p);
         UA_OpenSecureChannelResponse_deleteMembers(&p);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
         UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&asymHeader);
@@ -440,8 +407,8 @@ processOPN(UA_Connection *connection, UA_Server *server,
 }
 }
 
 
 static void
 static void
-processRequest(UA_SecureChannel *channel, UA_Server *server,
-               UA_UInt32 requestId, const UA_ByteString *msg) {
+processMSG(UA_Server *server, UA_SecureChannel *channel,
+           UA_UInt32 requestId, const UA_ByteString *msg) {
     /* At 0, the nodeid starts... */
     /* At 0, the nodeid starts... */
     size_t ppos = 0;
     size_t ppos = 0;
     size_t *offset = &ppos;
     size_t *offset = &ppos;
@@ -598,193 +565,101 @@ processRequest(UA_SecureChannel *channel, UA_Server *server,
         UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Could not send the message over "
         UA_LOG_INFO_CHANNEL(server->config.logger, channel, "Could not send the message over "
                              "the SecureChannel with error code 0x%08x", retval);
                              "the SecureChannel with error code 0x%08x", retval);
 
 
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-    /* See if we need to return publish requests without a subscription */
-    if(session && requestType == &UA_TYPES[UA_TYPES_DELETESUBSCRIPTIONSREQUEST])
-        UA_Session_answerPublishRequestsWithoutSubscription(session);
-#endif
-
     /* Clean up */
     /* Clean up */
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(request, requestType);
     UA_deleteMembers(response, responseType);
     UA_deleteMembers(response, responseType);
 }
 }
 
 
-/* MSG -> Normal request */
+/* Takes decoded messages starting at the nodeid of the content type. Only OPN
+ * messages start at the asymmetricalgorithmsecurityheader and are not
+ * decoded. */
 static void
 static void
-processMSG(UA_Connection *connection, UA_Server *server,
-           const UA_TcpMessageHeader *messageHeader,
-           const UA_ByteString *msg, size_t *offset) {
-    /* Decode the header */
-    UA_UInt32 channelId = 0;
-    UA_UInt32 tokenId = 0;
-    UA_SequenceHeader sequenceHeader;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-    retval |= UA_UInt32_decodeBinary(msg, offset, &tokenId);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &sequenceHeader);
-    if(retval != UA_STATUSCODE_GOOD)
-        return;
-
-    /* Get the SecureChannel */
-    UA_SecureChannel *channel = connection->channel;
-    UA_SecureChannel anonymousChannel; /* use if no channel specified */
-    if(!channel) {
-        UA_SecureChannel_init(&anonymousChannel);
-        anonymousChannel.connection = connection;
-        channel = &anonymousChannel;
-    }
-
-    /* Is the channel attached to connection? */
-    if(channelId != channel->securityToken.channelId) {
-        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                    "Connection %i | Received MSG with the channel id %i not bound to the connection",
-                    connection->sockfd, channelId);
-        Service_CloseSecureChannel(server, channel);
-        connection->close(connection);
-        return;
-    }
-
-    /* Does the sequence number match? */
-    retval = UA_SecureChannel_processSequenceNumber(sequenceHeader.sequenceNumber, channel);
-    if (retval != UA_STATUSCODE_GOOD){
-        UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                            "The sequence number was not increased by one. Got %i, expected %i",
-                            sequenceHeader.sequenceNumber, channel->receiveSequenceNumber + 1);
-        sendError(channel, msg, *offset, &UA_TYPES[UA_TYPES_SERVICEFAULT],
-                  sequenceHeader.requestId, UA_STATUSCODE_BADSECURITYCHECKSFAILED);
+UA_Server_processSecureChannelMessage(UA_Server *server, UA_SecureChannel *channel,
+                                      UA_MessageType messagetype, UA_UInt32 requestId,
+                                      const UA_ByteString *message) {
+    switch(messagetype) {
+    case UA_MESSAGETYPE_HEL:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Cannot process a HEL on an open channel");
+        break;
+    case UA_MESSAGETYPE_OPN:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process an OPN on an open channel");
+        processOPN(server, channel->connection, channel->securityToken.channelId, message);
+        break;
+    case UA_MESSAGETYPE_MSG:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process a MSG", connection->sockfd);
+        processMSG(server, channel, requestId, message);
+        break;
+    case UA_MESSAGETYPE_CLO:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Process a CLO", connection->sockfd);
         Service_CloseSecureChannel(server, channel);
         Service_CloseSecureChannel(server, channel);
-        connection->close(connection);
-        return;
-    }
-
-    /* Does the token match? */
-    if(tokenId != channel->securityToken.tokenId) {
-        if(tokenId != channel->nextSecurityToken.tokenId) {
-            UA_LOG_INFO_CHANNEL(server->config.logger, channel,
-                                "Request with a wrong security token. Closing the SecureChannel.");
-            Service_CloseSecureChannel(server, channel);
-            connection->close(connection);
-            return;
-        }
-        UA_SecureChannel_revolveTokens(channel);
-    }
-
-    /* Process chunk to get complete request */
-    UA_Boolean deleteRequest = false;
-    UA_ByteString request = processChunk(channel, server, messageHeader, sequenceHeader.requestId,
-                                         msg, *offset, messageHeader->messageSize - 24, &deleteRequest);
-    *offset += (messageHeader->messageSize - 24);
-    if(request.length > 0) {
-        /* Process the request */
-        processRequest(channel, server, sequenceHeader.requestId, &request);
-        if(deleteRequest)
-            UA_ByteString_deleteMembers(&request);
+        break;
+    default:
+        UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                             "Unknown message type");
     }
     }
-
-    /* Clean up a possible anonymous channel */
-    if(channel == &anonymousChannel)
-        UA_SecureChannel_deleteMembersCleanup(channel);
 }
 }
 
 
-/* CLO -> Close the secure channel */
-static void
-processCLO(UA_Connection *connection, UA_Server *server,
-           const UA_ByteString *msg, size_t *offset) {
-    UA_UInt32 channelId;
-    UA_UInt32 tokenId = 0;
-    UA_SequenceHeader sequenceHeader;
-    UA_StatusCode retval = UA_UInt32_decodeBinary(msg, offset, &channelId);
-    retval |= UA_UInt32_decodeBinary(msg, offset, &tokenId);
-    retval |= UA_SequenceHeader_decodeBinary(msg, offset, &sequenceHeader);
-    if(retval != UA_STATUSCODE_GOOD)
-        return;
-
-    UA_SecureChannel *channel = connection->channel;
-    if(!channel || channel->securityToken.channelId != channelId ||
-       channel->securityToken.tokenId != tokenId)
-        return;
-
-    if(sequenceHeader.sequenceNumber != channel->receiveSequenceNumber + 1)
-        return;
-
-    Service_CloseSecureChannel(server, connection->channel);
-}
-
-/* Process binary message received from Connection dose not modify UA_ByteString
- * you have to free it youself. use of connection->getSendBuffer() and
- * connection->send() to answer Message */
+/* Takes the raw message from the network layer */
 void
 void
 UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
 UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection,
-                               const UA_ByteString *msg) {
-    size_t offset= 0;
-    UA_TcpMessageHeader tcpMessageHeader;
-    do {
-        /* Decode the message header */
-        UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(msg, &offset, &tcpMessageHeader);
+                               const UA_ByteString *message) {
+    UA_SecureChannel *channel = connection->channel;
+    if(channel) {
+        /* Assemble chunks in the securechannel and process complete messages */
+        UA_StatusCode retval = 
+            UA_SecureChannel_processChunks(channel, message,
+                 (UA_ProcessMessageCallback*)UA_Server_processSecureChannelMessage, server);
+        if(retval != UA_STATUSCODE_GOOD)
+            UA_LOG_TRACE_CHANNEL(server->config.logger, channel,
+                                 "Procesing chunkgs resulted in error code 0x%08x", retval);
+    } else {
+        /* Process messages without a channel and no chunking */
+        size_t offset = 0;
+        UA_TcpMessageHeader tcpMessageHeader;
+        UA_StatusCode retval = UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
         if(retval != UA_STATUSCODE_GOOD) {
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "Decoding of message header failed on Connection %i",
-                        connection->sockfd);
-            connection->close(connection);
-            break;
-        }
-        if(tcpMessageHeader.messageSize < 16) {
-            UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                        "The message is suspiciously small on Connection %i",
-                        connection->sockfd);
             connection->close(connection);
             connection->close(connection);
-            break;
+            return;
         }
         }
 
 
-        /* Set the expected position after processing the chunk */
-        size_t targetpos = offset - 8 + tcpMessageHeader.messageSize;
-
-        /* Process the message */
+        /* Dispatch according to the message type */
         switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
         switch(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) {
         case UA_MESSAGETYPE_HEL:
         case UA_MESSAGETYPE_HEL:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a HEL", connection->sockfd);
-            processHEL(connection, msg, &offset);
+                         "Connection %i | Process HEL message", connection->sockfd);
+            processHEL(connection, message, &offset);
             break;
             break;
-
-        case UA_MESSAGETYPE_OPN:
+        case UA_MESSAGETYPE_OPN: {
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a OPN", connection->sockfd);
-            processOPN(connection, server, msg, &offset);
-            break;
-
-        case UA_MESSAGETYPE_MSG:
-#ifndef UA_ENABLE_NONSTANDARD_STATELESS
-            if(connection->state != UA_CONNECTION_ESTABLISHED) {
-                UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                             "Connection %i | Received a MSG, but the connection " \
-                             "is not established", connection->sockfd);
+                         "Connection %i | Process OPN message", connection->sockfd);
+            UA_UInt32 channelId = 0;
+            retval = UA_UInt32_decodeBinary(message, &offset, &channelId);
+            if(retval != UA_STATUSCODE_GOOD)
                 connection->close(connection);
                 connection->close(connection);
-                return;
-            }
-#endif
+            UA_ByteString offsetMessage = (UA_ByteString){
+                .data = message->data + 12, .length = message->length - 12};
+            processOPN(server, connection, channelId, &offsetMessage);
+            break; }
+        case UA_MESSAGETYPE_MSG:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a MSG", connection->sockfd);
-            processMSG(connection, server, &tcpMessageHeader, msg, &offset);
+                         "Connection %i | Processing a MSG message not possible "
+                         "without a SecureChannel", connection->sockfd);
+            connection->close(connection);
             break;
             break;
-
         case UA_MESSAGETYPE_CLO:
         case UA_MESSAGETYPE_CLO:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Process a CLO", connection->sockfd);
-            processCLO(connection, server, msg, &offset);
-            return;
-
+                         "Connection %i | Processing a CLO message not possible "
+                         "without a SecureChannel", connection->sockfd);
+            connection->close(connection);
+            break;
         default:
         default:
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
             UA_LOG_TRACE(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Unknown chunk type", connection->sockfd);
-        }
-
-        /* Loop to process the next message in the stream */
-        if(offset != targetpos) {
-            UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_NETWORK,
-                         "Connection %i | Message was not entirely processed. " \
-                         "Skip from position %i to position %i; message length is %i",
-                         connection->sockfd, offset, targetpos, msg->length);
-            offset = targetpos;
+                         "Connection %i | Unknown message type", connection->sockfd);
+            connection->close(connection);
         }
         }
-    } while(msg->length > offset);
+    }
 }
 }

+ 3 - 1
src/server/ua_server_internal.h

@@ -76,7 +76,9 @@ struct UA_Server {
     /* Jobs with a repetition interval */
     /* Jobs with a repetition interval */
     LIST_HEAD(RepeatedJobsList, RepeatedJob) repeatedJobs;
     LIST_HEAD(RepeatedJobsList, RepeatedJob) repeatedJobs;
 
 
-#ifdef UA_ENABLE_MULTITHREADING
+#ifndef UA_ENABLE_MULTITHREADING
+    SLIST_HEAD(DelayedJobsList, UA_DelayedJob) delayedCallbacks;
+#else
     /* Dispatch queue head for the worker threads (the tail should not be in the same cache line) */
     /* Dispatch queue head for the worker threads (the tail should not be in the same cache line) */
     struct cds_wfcq_head dispatchQueue_head;
     struct cds_wfcq_head dispatchQueue_head;
     UA_Worker *workers; /* there are nThread workers in a running server */
     UA_Worker *workers; /* there are nThread workers in a running server */

+ 36 - 1
src/server/ua_server_worker.c

@@ -307,7 +307,36 @@ void UA_Server_deleteAllRepeatedJobs(UA_Server *server) {
 /* Delayed Jobs */
 /* Delayed Jobs */
 /****************/
 /****************/
 
 
-#ifdef UA_ENABLE_MULTITHREADING
+#ifndef UA_ENABLE_MULTITHREADING
+
+typedef struct UA_DelayedJob {
+    SLIST_ENTRY(UA_DelayedJob) next;
+    UA_Job job;
+} UA_DelayedJob;
+
+UA_StatusCode
+UA_Server_delayedCallback(UA_Server *server, UA_ServerCallback callback, void *data) {
+    UA_DelayedJob *dj = UA_malloc(sizeof(UA_DelayedJob));
+    if(!dj)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    dj->job.type = UA_JOBTYPE_METHODCALL;
+    dj->job.job.methodCall.data = data;
+    dj->job.job.methodCall.method = callback;
+    SLIST_INSERT_HEAD(&server->delayedCallbacks, dj, next);
+    return UA_STATUSCODE_GOOD;
+}
+
+static void
+processDelayedCallbacks(UA_Server *server) {
+    UA_DelayedJob *dj, *dj_tmp;
+    SLIST_FOREACH_SAFE(dj, &server->delayedCallbacks, next, dj_tmp) {
+        SLIST_REMOVE(&server->delayedCallbacks, dj, UA_DelayedJob, next);
+        processJob(server, &dj->job);
+        UA_free(dj);
+    }
+}
+
+#else
 
 
 #define DELAYEDJOBSSIZE 100 // Collect delayed jobs until we have DELAYEDWORKSIZE items
 #define DELAYEDJOBSSIZE 100 // Collect delayed jobs until we have DELAYEDWORKSIZE items
 
 
@@ -574,6 +603,10 @@ UA_UInt16 UA_Server_run_iterate(UA_Server *server, UA_Boolean waitInternal) {
         }
         }
     }
     }
 
 
+#ifndef UA_ENABLE_MULTITHREADING
+    processDelayedCallbacks(server);
+#endif
+
     now = UA_DateTime_nowMonotonic();
     now = UA_DateTime_nowMonotonic();
     timeout = 0;
     timeout = 0;
     if(nextRepeated > now)
     if(nextRepeated > now)
@@ -607,6 +640,8 @@ UA_StatusCode UA_Server_run_shutdown(UA_Server *server) {
     emptyDispatchQueue(server);
     emptyDispatchQueue(server);
     UA_ASSERT_RCU_UNLOCKED();
     UA_ASSERT_RCU_UNLOCKED();
     rcu_barrier(); // wait for all scheduled call_rcu work to complete
     rcu_barrier(); // wait for all scheduled call_rcu work to complete
+#else
+    processDelayedCallbacks(server);
 #endif
 #endif
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }

+ 4 - 2
src/server/ua_services_attribute.c

@@ -1,6 +1,8 @@
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
 #include "ua_services.h"
 #include "ua_services.h"
-
+#ifdef UA_ENABLE_NONSTANDARD_STATELESS
+#include "ua_types_encoding_binary.h"
+#endif
 /******************/
 /******************/
 /* Read Attribute */
 /* Read Attribute */
 /******************/
 /******************/
@@ -520,7 +522,7 @@ UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableD
            dimensions are checked later */
            dimensions are checked later */
         if(variableDataType == &UA_TYPES[UA_TYPES_BYTE] &&
         if(variableDataType == &UA_TYPES[UA_TYPES_BYTE] &&
            valueDataType == &UA_TYPES[UA_TYPES_BYTESTRING] &&
            valueDataType == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-           !range && !UA_Variant_isScalar(value)) {
+           !range && UA_Variant_isScalar(value)) {
             UA_ByteString *str = (UA_ByteString*)value->data;
             UA_ByteString *str = (UA_ByteString*)value->data;
             editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
             editableValue->type = &UA_TYPES[UA_TYPES_BYTE];
             editableValue->arrayLength = str->length;
             editableValue->arrayLength = str->length;

+ 9 - 5
src/server/ua_services_session.c

@@ -4,7 +4,8 @@
 #include "ua_types_generated_encoding_binary.h"
 #include "ua_types_generated_encoding_binary.h"
 
 
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
 void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
-                           const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response) {
+                           const UA_CreateSessionRequest *request,
+                           UA_CreateSessionResponse *response) {
     if(channel->securityToken.channelId == 0) {
     if(channel->securityToken.channelId == 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID;
         response->responseHeader.serviceResult = UA_STATUSCODE_BADSECURECHANNELIDINVALID;
         return;
         return;
@@ -37,7 +38,8 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
     response->authenticationToken = newSession->authenticationToken;
     response->authenticationToken = newSession->authenticationToken;
     response->responseHeader.serviceResult = UA_String_copy(&request->sessionName, &newSession->sessionName);
     response->responseHeader.serviceResult = UA_String_copy(&request->sessionName, &newSession->sessionName);
     if(server->endpointDescriptionsSize > 0)
     if(server->endpointDescriptionsSize > 0)
-        response->responseHeader.serviceResult |= UA_ByteString_copy(&server->endpointDescriptions->serverCertificate,
+        response->responseHeader.serviceResult |=
+            UA_ByteString_copy(&server->endpointDescriptions->serverCertificate,
                                &response->serverCertificate);
                                &response->serverCertificate);
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
     if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
         UA_SessionManager_removeSession(&server->sessionManager, &newSession->authenticationToken);
         UA_SessionManager_removeSession(&server->sessionManager, &newSession->authenticationToken);
@@ -48,8 +50,9 @@ void Service_CreateSession(UA_Server *server, UA_SecureChannel *channel,
 }
 }
 
 
 void
 void
-Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel, UA_Session *session,
-                        const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response) {
+Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
+                        UA_Session *session, const UA_ActivateSessionRequest *request,
+                        UA_ActivateSessionResponse *response) {
     if(session->validTill < UA_DateTime_nowMonotonic()) {
     if(session->validTill < UA_DateTime_nowMonotonic()) {
         UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: SecureChannel %i wants "
         UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: SecureChannel %i wants "
                             "to activate, but the session has timed out", channel->securityToken.channelId);
                             "to activate, but the session has timed out", channel->securityToken.channelId);
@@ -115,7 +118,8 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel, UA_Session
             }
             }
         }
         }
         if(!match) {
         if(!match) {
-            UA_LOG_INFO_SESSION(server->config.logger, session, "ActivateSession: Did not find matching username/password");
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "ActivateSession: Did not find matching username/password");
             response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
             response->responseHeader.serviceResult = UA_STATUSCODE_BADUSERACCESSDENIED;
             return;
             return;
         }
         }

+ 34 - 32
src/server/ua_services_subscription.c

@@ -66,10 +66,10 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
 
 
     /* Set the subscription parameters */
     /* Set the subscription parameters */
     newSubscription->publishingEnabled = request->publishingEnabled;
     newSubscription->publishingEnabled = request->publishingEnabled;
-    newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount;
     setSubscriptionSettings(server, newSubscription, request->requestedPublishingInterval,
     setSubscriptionSettings(server, newSubscription, request->requestedPublishingInterval,
                             request->requestedLifetimeCount, request->requestedMaxKeepAliveCount,
                             request->requestedLifetimeCount, request->requestedMaxKeepAliveCount,
                             request->maxNotificationsPerPublish, request->priority);
                             request->maxNotificationsPerPublish, request->priority);
+    newSubscription->currentKeepAliveCount = newSubscription->maxKeepAliveCount; /* set settings first */
 
 
     /* Prepare the response */
     /* Prepare the response */
     response->subscriptionId = newSubscription->subscriptionID;
     response->subscriptionId = newSubscription->subscriptionID;
@@ -134,10 +134,6 @@ Service_SetPublishingMode(UA_Server *server, UA_Session *session,
         if(sub->publishingEnabled != request->publishingEnabled) {
         if(sub->publishingEnabled != request->publishingEnabled) {
             sub->publishingEnabled = request->publishingEnabled;
             sub->publishingEnabled = request->publishingEnabled;
             sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
             sub->currentLifetimeCount = 0; /* Reset the subscription lifetime */
-            if(sub->publishingEnabled)
-                Subscription_registerPublishJob(server, sub);
-            else
-                Subscription_unregisterPublishJob(server, sub);
         }
         }
     }
     }
 }
 }
@@ -402,28 +398,17 @@ void
 Service_Publish(UA_Server *server, UA_Session *session,
 Service_Publish(UA_Server *server, UA_Session *session,
                 const UA_PublishRequest *request, UA_UInt32 requestId) {
                 const UA_PublishRequest *request, UA_UInt32 requestId) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing PublishRequest");
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing PublishRequest");
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     /* Return an error if the session has no subscription */
     /* Return an error if the session has no subscription */
     if(LIST_EMPTY(&session->serverSubscriptions)) {
     if(LIST_EMPTY(&session->serverSubscriptions)) {
-        UA_PublishResponse response;
-        UA_PublishResponse_init(&response);
-        response.responseHeader.requestHandle = request->requestHeader.requestHandle;
-        response.responseHeader.timestamp = UA_DateTime_now();
-        response.responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
-        UA_SecureChannel_sendBinaryMessage(session->channel, requestId, &response,
-                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-        return;
+        retval = UA_STATUSCODE_BADNOSUBSCRIPTION;
+        goto send_error;
     }
     }
 
 
     UA_PublishResponseEntry *entry = UA_malloc(sizeof(UA_PublishResponseEntry));
     UA_PublishResponseEntry *entry = UA_malloc(sizeof(UA_PublishResponseEntry));
     if(!entry) {
     if(!entry) {
-        UA_PublishResponse response;
-        UA_PublishResponse_init(&response);
-        response.responseHeader.requestHandle = request->requestHeader.requestHandle;
-        response.responseHeader.timestamp = UA_DateTime_now();
-        response.responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-        UA_SecureChannel_sendBinaryMessage(session->channel, requestId, &response,
-                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-        return;
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto send_error;
     }
     }
     entry->requestId = requestId;
     entry->requestId = requestId;
 
 
@@ -435,14 +420,9 @@ Service_Publish(UA_Server *server, UA_Session *session,
         response->results = UA_Array_new(request->subscriptionAcknowledgementsSize,
         response->results = UA_Array_new(request->subscriptionAcknowledgementsSize,
                                          &UA_TYPES[UA_TYPES_STATUSCODE]);
                                          &UA_TYPES[UA_TYPES_STATUSCODE]);
         if(!response->results) {
         if(!response->results) {
-            /* Respond immediately with the error code */
-            response->responseHeader.timestamp = UA_DateTime_now();
-            response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
-            UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
-                                               &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-            UA_PublishResponse_deleteMembers(response);
             UA_free(entry);
             UA_free(entry);
-            return;
+            retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            goto send_error;
         }
         }
         response->resultsSize = request->subscriptionAcknowledgementsSize;
         response->resultsSize = request->subscriptionAcknowledgementsSize;
     }
     }
@@ -450,15 +430,15 @@ Service_Publish(UA_Server *server, UA_Session *session,
     /* Delete Acknowledged Subscription Messages */
     /* Delete Acknowledged Subscription Messages */
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
     for(size_t i = 0; i < request->subscriptionAcknowledgementsSize; i++) {
         UA_SubscriptionAcknowledgement *ack = &request->subscriptionAcknowledgements[i];
         UA_SubscriptionAcknowledgement *ack = &request->subscriptionAcknowledgements[i];
-        /* Get the subscription */
         UA_Subscription *sub = UA_Session_getSubscriptionByID(session, ack->subscriptionId);
         UA_Subscription *sub = UA_Session_getSubscriptionByID(session, ack->subscriptionId);
         if(!sub) {
         if(!sub) {
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             response->results[i] = UA_STATUSCODE_BADSUBSCRIPTIONIDINVALID;
             UA_LOG_DEBUG_SESSION(server->config.logger, session,
             UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                         "Cannot process acknowledgements subscription %u", ack->subscriptionId);
+                                 "Cannot process acknowledgements subscription %u",
+                                 ack->subscriptionId);
             continue;
             continue;
         }
         }
-        /* Remove the acked transmission for the retransmission queue */
+        /* Remove the acked transmission from the retransmission queue */
         response->results[i] = UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
         response->results[i] = UA_STATUSCODE_BADSEQUENCENUMBERUNKNOWN;
         UA_NotificationMessageEntry *pre, *pre_tmp;
         UA_NotificationMessageEntry *pre, *pre_tmp;
         LIST_FOREACH_SAFE(pre, &sub->retransmissionQueue, listEntry, pre_tmp) {
         LIST_FOREACH_SAFE(pre, &sub->retransmissionQueue, listEntry, pre_tmp) {
@@ -485,9 +465,20 @@ Service_Publish(UA_Server *server, UA_Session *session,
                                  "Response on a late subscription", immediate->subscriptionID,
                                  "Response on a late subscription", immediate->subscriptionID,
                                  session->authenticationToken.identifier.numeric);
                                  session->authenticationToken.identifier.numeric);
             UA_Subscription_publishCallback(server, immediate);
             UA_Subscription_publishCallback(server, immediate);
-            return;
+            break;
         }
         }
     }
     }
+    return;
+
+    UA_PublishResponse err_response;
+ send_error:
+    UA_PublishResponse_init(&err_response);
+    err_response.responseHeader.requestHandle = request->requestHeader.requestHandle;
+    err_response.responseHeader.timestamp = UA_DateTime_now();
+    err_response.responseHeader.serviceResult = retval;
+    UA_assert(err_response.responseHeader.requestHandle != 0);
+    UA_SecureChannel_sendBinaryMessage(session->channel, requestId, &err_response,
+                                       &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 }
 }
 
 
 void
 void
@@ -520,6 +511,17 @@ Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
                                  response->results[i]);
                                  response->results[i]);
         }
         }
     }
     }
+
+    /* Send dangling publish responses in a delayed job if the last subscription
+       was removed */
+    if(LIST_FIRST(&session->serverSubscriptions))
+        return;
+    UA_NodeId *sessionToken = UA_NodeId_new();
+    if(!sessionToken)
+        return;
+    UA_NodeId_copy(&session->authenticationToken, sessionToken);
+    UA_Server_delayedCallback(server, (UA_ServerCallback)UA_Subscription_answerPublishRequestsNoSubscription,
+                              sessionToken);
 }
 }
 
 
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,
 void Service_DeleteMonitoredItems(UA_Server *server, UA_Session *session,

+ 146 - 78
src/server/ua_subscription.c

@@ -269,32 +269,91 @@ UA_Subscription_deleteMonitoredItem(UA_Server *server, UA_Subscription *sub,
     return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
     return UA_STATUSCODE_BADMONITOREDITEMIDINVALID;
 }
 }
 
 
-void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
-    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
-                         "Publish Callback", sub->subscriptionID);
-
-    /* Count the available notifications */
+static size_t
+countQueuedNotifications(UA_Subscription *sub, UA_Boolean *moreNotifications) {
     size_t notifications = 0;
     size_t notifications = 0;
-    UA_Boolean moreNotifications = false;
     if(sub->publishingEnabled) {
     if(sub->publishingEnabled) {
         UA_MonitoredItem *mon;
         UA_MonitoredItem *mon;
         LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
         LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
             MonitoredItem_queuedValue *qv;
             MonitoredItem_queuedValue *qv;
             TAILQ_FOREACH(qv, &mon->queue, listEntry) {
             TAILQ_FOREACH(qv, &mon->queue, listEntry) {
                 if(notifications >= sub->notificationsPerPublish) {
                 if(notifications >= sub->notificationsPerPublish) {
-                    moreNotifications = true;
+                    *moreNotifications = true;
                     break;
                     break;
                 }
                 }
                 notifications++;
                 notifications++;
             }
             }
         }
         }
     }
     }
+    return notifications;
+}
+
+static UA_StatusCode
+prepareNotificationMessage(UA_Subscription *sub, UA_NotificationMessage *message,
+                           size_t notifications) {
+    /* Array of ExtensionObject to hold different kinds of notifications
+       (currently only DataChangeNotifications) */
+    message->notificationData = UA_Array_new(1, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+    if(!message->notificationData)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    message->notificationDataSize = 1;
+
+    /* Allocate Notification */
+    UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
+    if(!dcn)
+        goto cleanup;
+    UA_ExtensionObject *data = message->notificationData;
+    data->encoding = UA_EXTENSIONOBJECT_DECODED;
+    data->content.decoded.data = dcn;
+    data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
+
+    /* Allocate array of notifications */
+    dcn->monitoredItems =
+        UA_Array_new(notifications, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
+    if(!dcn->monitoredItems)
+        goto cleanup;
+    dcn->monitoredItemsSize = notifications;
+
+    /* Move notifications into the response .. the point of no return */
+    size_t l = 0;
+    UA_MonitoredItem *mon;
+    LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
+        MonitoredItem_queuedValue *qv, *qv_tmp;
+        TAILQ_FOREACH_SAFE(qv, &mon->queue, listEntry, qv_tmp) {
+            if(l >= notifications)
+                return UA_STATUSCODE_GOOD;
+            UA_MonitoredItemNotification *min = &dcn->monitoredItems[l];
+            min->clientHandle = qv->clientHandle;
+            min->value = qv->value;
+            TAILQ_REMOVE(&mon->queue, qv, listEntry);
+            UA_free(qv);
+            mon->currentQueueSize--;
+            l++;
+        }
+    }
+    return UA_STATUSCODE_GOOD;
+    
+ cleanup:
+    UA_NotificationMessage_deleteMembers(message);
+    return UA_STATUSCODE_BADOUTOFMEMORY;
+}
+
+void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
+    UA_LOG_DEBUG_SESSION(server->config.logger, sub->session, "Subscription %u | "
+                         "Publish Callback", sub->subscriptionID);
+
+    /* Count the available notifications */
+    UA_Boolean moreNotifications = false;
+    size_t notifications = countQueuedNotifications(sub, &moreNotifications);
 
 
     /* Return if nothing to do */
     /* Return if nothing to do */
     if(notifications == 0) {
     if(notifications == 0) {
         sub->currentKeepAliveCount++;
         sub->currentKeepAliveCount++;
         if(sub->currentKeepAliveCount < sub->maxKeepAliveCount)
         if(sub->currentKeepAliveCount < sub->maxKeepAliveCount)
             return;
             return;
+        UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
+                             "Sending a keepalive on subscription %u",
+                             sub->subscriptionID)
     }
     }
 
 
     /* Check if the securechannel is valid */
     /* Check if the securechannel is valid */
@@ -304,6 +363,8 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
 
 
     /* Dequeue a response */
     /* Dequeue a response */
     UA_PublishResponseEntry *pre = SIMPLEQ_FIRST(&sub->session->responseQueue);
     UA_PublishResponseEntry *pre = SIMPLEQ_FIRST(&sub->session->responseQueue);
+
+    /* Cannot publish without a response */
     if(!pre) {
     if(!pre) {
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
         UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                              "Cannot send a publish response on subscription %u, "
                              "Cannot send a publish response on subscription %u, "
@@ -321,20 +382,39 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         return;
         return;
     }
     }
 
 
-    SIMPLEQ_REMOVE_HEAD(&sub->session->responseQueue, listEntry);
     UA_PublishResponse *response = &pre->response;
     UA_PublishResponse *response = &pre->response;
-    UA_UInt32 requestId = pre->requestId;
+    UA_NotificationMessage *message = &response->notificationMessage;
+    UA_NotificationMessageEntry *retransmission = NULL;
+    if(notifications > 0) {
+        /* Allocate the retransmission entry */
+        retransmission = malloc(sizeof(UA_NotificationMessageEntry));
+        if(!retransmission) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | Could not allocate memory "
+                                   "for retransmission", sub->subscriptionID);
+            return;
+        }
+        /* Prepare the response */
+        UA_StatusCode retval =
+            prepareNotificationMessage(sub, message, notifications);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
+                                   "Subscription %u | Could not prepare the "
+                                   "notification message", sub->subscriptionID);
+            UA_free(retransmission);
+            return;
+        }
+    }
 
 
-    /* We have a request. Reset state to normal. */
-    sub->state = UA_SUBSCRIPTIONSTATE_NORMAL;
-    sub->currentKeepAliveCount = 0;
-    sub->currentLifetimeCount = 0;
+    /* <-- The point of no return --> */
+
+    /* Remove the response from the response queue */
+    SIMPLEQ_REMOVE_HEAD(&sub->session->responseQueue, listEntry);
 
 
-    /* Prepare the response */
+    /* Set up the response */
     response->responseHeader.timestamp = UA_DateTime_now();
     response->responseHeader.timestamp = UA_DateTime_now();
     response->subscriptionId = sub->subscriptionID;
     response->subscriptionId = sub->subscriptionID;
     response->moreNotifications = moreNotifications;
     response->moreNotifications = moreNotifications;
-    UA_NotificationMessage *message = &response->notificationMessage;
     message->publishTime = response->responseHeader.timestamp;
     message->publishTime = response->responseHeader.timestamp;
     if(notifications == 0) {
     if(notifications == 0) {
         /* Send sequence number for the next notification */
         /* Send sequence number for the next notification */
@@ -343,66 +423,24 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
         /* Increase the sequence number */
         /* Increase the sequence number */
         message->sequenceNumber = ++sub->sequenceNumber;
         message->sequenceNumber = ++sub->sequenceNumber;
 
 
-        /* Collect the notification messages */
-        message->notificationData = UA_ExtensionObject_new();
-        message->notificationDataSize = 1;
-        UA_ExtensionObject *data = message->notificationData;
-        UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
-        dcn->monitoredItems =
-            UA_Array_new(notifications, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
-        dcn->monitoredItemsSize = notifications;
-        size_t l = 0;
-        UA_MonitoredItem *mon;
-        LIST_FOREACH(mon, &sub->MonitoredItems, listEntry) {
-            MonitoredItem_queuedValue *qv, *qv_tmp;
-            size_t mon_l = 0;
-            TAILQ_FOREACH_SAFE(qv, &mon->queue, listEntry, qv_tmp) {
-                if(notifications <= l)
-                    break;
-                UA_MonitoredItemNotification *min = &dcn->monitoredItems[l];
-                min->clientHandle = qv->clientHandle;
-                min->value = qv->value;
-                TAILQ_REMOVE(&mon->queue, qv, listEntry);
-                UA_free(qv);
-                mon->currentQueueSize--;
-                mon_l++;
-            }
-            UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
-                                 "Subscription %u | MonitoredItem %u | "
-                                 "Adding %u notifications to the publish response. "
-                                 "%u notifications remain in the queue",
-                                 sub->subscriptionID, mon->itemId, mon_l,
-                                 mon->currentQueueSize);
-            l += mon_l;
-        }
-        data->encoding = UA_EXTENSIONOBJECT_DECODED;
-        data->content.decoded.data = dcn;
-        data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
-
-        /* Put the notification message into the retransmission queue */
-        UA_NotificationMessageEntry *retransmission =
-            malloc(sizeof(UA_NotificationMessageEntry));
-        if(retransmission) {
-            UA_NotificationMessage_copy(&response->notificationMessage,
-                                        &retransmission->message);
-            LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
-        } else {
-            UA_LOG_WARNING_SESSION(server->config.logger, sub->session,
-                                   "Subscription %u | Could not allocate memory "
-                                   "for retransmission", sub->subscriptionID);
-        }
+        /* Put the notification message into the retransmission queue. This needs to
+         * be done here, so that the message itself is included in the available
+         * sequence numbers for acknowledgement. */
+        retransmission->message = response->notificationMessage;
+        LIST_INSERT_HEAD(&sub->retransmissionQueue, retransmission, listEntry);
     }
     }
 
 
     /* Get the available sequence numbers from the retransmission queue */
     /* Get the available sequence numbers from the retransmission queue */
-    size_t available = 0, i = 0;
+    size_t available = 0;
     UA_NotificationMessageEntry *nme;
     UA_NotificationMessageEntry *nme;
     LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry)
     LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry)
         available++;
         available++;
-    //cppcheck-suppress knownConditionTrueFalse
+    // cppcheck-suppress knownConditionTrueFalse
     if(available > 0) {
     if(available > 0) {
         response->availableSequenceNumbers = UA_alloca(available * sizeof(UA_UInt32));
         response->availableSequenceNumbers = UA_alloca(available * sizeof(UA_UInt32));
         response->availableSequenceNumbersSize = available;
         response->availableSequenceNumbersSize = available;
     }
     }
+    size_t i = 0;
     LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry) {
     LIST_FOREACH(nme, &sub->retransmissionQueue, listEntry) {
         response->availableSequenceNumbers[i] = nme->message.sequenceNumber;
         response->availableSequenceNumbers[i] = nme->message.sequenceNumber;
         i++;
         i++;
@@ -412,43 +450,73 @@ void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub) {
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
     UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
                          "Subscription %u | Sending out a publish response with %u "
                          "Subscription %u | Sending out a publish response with %u "
                          "notifications", sub->subscriptionID, (UA_UInt32)notifications);
                          "notifications", sub->subscriptionID, (UA_UInt32)notifications);
-    UA_SecureChannel_sendBinaryMessage(sub->session->channel, requestId, response,
+    UA_SecureChannel_sendBinaryMessage(sub->session->channel, pre->requestId, response,
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
                                        &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
 
 
-    /* Remove the queued request */
-    response->availableSequenceNumbers = NULL; /* stack-allocated */
-    response->availableSequenceNumbersSize = 0;
-    UA_PublishResponse_deleteMembers(&pre->response);
-    UA_free(pre);
+    /* Reset subscription state to normal. */
+    sub->state = UA_SUBSCRIPTIONSTATE_NORMAL;
+    sub->currentKeepAliveCount = 0;
+    sub->currentLifetimeCount = 0;
+    
+    /* Free the response */
+    UA_Array_delete(response->results, response->resultsSize,
+                    &UA_TYPES[UA_TYPES_UINT32]);
+    UA_free(pre); /* no need for UA_PublishResponse_deleteMembers */
 
 
     /* Repeat if there are more notifications to send */
     /* Repeat if there are more notifications to send */
     if(moreNotifications)
     if(moreNotifications)
         UA_Subscription_publishCallback(server, sub);
         UA_Subscription_publishCallback(server, sub);
 }
 }
 
 
-UA_StatusCode Subscription_registerPublishJob(UA_Server *server, UA_Subscription *sub) {
+UA_StatusCode
+Subscription_registerPublishJob(UA_Server *server, UA_Subscription *sub) {
     if(sub->publishJobIsRegistered)
     if(sub->publishJobIsRegistered)
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
-    if(!sub->publishingEnabled)
-        return UA_STATUSCODE_GOOD;
 
 
     UA_Job job;
     UA_Job job;
     job.type = UA_JOBTYPE_METHODCALL;
     job.type = UA_JOBTYPE_METHODCALL;
     job.job.methodCall.method = (UA_ServerCallback)UA_Subscription_publishCallback;
     job.job.methodCall.method = (UA_ServerCallback)UA_Subscription_publishCallback;
     job.job.methodCall.data = sub;
     job.job.methodCall.data = sub;
-    UA_StatusCode retval = UA_Server_addRepeatedJob(server, job,
-                                                    (UA_UInt32)sub->publishingInterval,
-                                                    &sub->publishJobGuid);
+    UA_StatusCode retval =
+        UA_Server_addRepeatedJob(server, job, (UA_UInt32)sub->publishingInterval,
+                                 &sub->publishJobGuid);
     if(retval == UA_STATUSCODE_GOOD)
     if(retval == UA_STATUSCODE_GOOD)
         sub->publishJobIsRegistered = true;
         sub->publishJobIsRegistered = true;
     return retval;
     return retval;
 }
 }
 
 
-UA_StatusCode Subscription_unregisterPublishJob(UA_Server *server, UA_Subscription *sub) {
+UA_StatusCode
+Subscription_unregisterPublishJob(UA_Server *server, UA_Subscription *sub) {
     if(!sub->publishJobIsRegistered)
     if(!sub->publishJobIsRegistered)
         return UA_STATUSCODE_GOOD;
         return UA_STATUSCODE_GOOD;
     sub->publishJobIsRegistered = false;
     sub->publishJobIsRegistered = false;
     return UA_Server_removeRepeatedJob(server, sub->publishJobGuid);
     return UA_Server_removeRepeatedJob(server, sub->publishJobGuid);
 }
 }
 
 
+/* When the session has publish requests stored but the last subscription is
+   deleted... Send out empty responses */
+void
+UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server, UA_NodeId *sessionToken) {
+    /* Get session */
+    UA_Session *session = UA_SessionManager_getSession(&server->sessionManager, sessionToken);
+    UA_NodeId_delete(sessionToken);
+
+    /* No session or there are remaining subscriptions */
+    if(!session || LIST_FIRST(&session->serverSubscriptions))
+        return;
+
+    /* Send a response for every queued request */
+    UA_PublishResponseEntry *pre;
+    while((pre = SIMPLEQ_FIRST(&session->responseQueue))) {
+        SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
+        UA_PublishResponse *response = &pre->response;
+        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
+        response->responseHeader.timestamp = UA_DateTime_now();
+        UA_SecureChannel_sendBinaryMessage(session->channel, pre->requestId, response,
+                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
+        UA_PublishResponse_deleteMembers(response);
+        UA_free(pre);
+    }
+}
+
 #endif /* UA_ENABLE_SUBSCRIPTIONS */
 #endif /* UA_ENABLE_SUBSCRIPTIONS */

+ 4 - 0
src/server/ua_subscription.h

@@ -118,4 +118,8 @@ UA_Subscription_getMonitoredItem(UA_Subscription *sub, UA_UInt32 monitoredItemID
 
 
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
 void UA_Subscription_publishCallback(UA_Server *server, UA_Subscription *sub);
 
 
+void
+UA_Subscription_answerPublishRequestsNoSubscription(UA_Server *server,
+                                                    UA_NodeId *sessionToken);
+
 #endif /* UA_SUBSCRIPTION_H_ */
 #endif /* UA_SUBSCRIPTION_H_ */

+ 120 - 26
src/ua_securechannel.c

@@ -129,6 +129,10 @@ void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel) {
     UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
     UA_ChannelSecurityToken_init(&channel->nextSecurityToken);
 }
 }
 
 
+/***********************/
+/* Send Binary Message */
+/***********************/
+
 static UA_StatusCode
 static UA_StatusCode
 UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset) {
 UA_SecureChannel_sendChunk(UA_ChunkInfo *ci, UA_ByteString *dst, size_t offset) {
     UA_SecureChannel *channel = ci->channel;
     UA_SecureChannel *channel = ci->channel;
@@ -253,9 +257,27 @@ UA_SecureChannel_sendBinaryMessage(UA_SecureChannel *channel, UA_UInt32 requestI
     return UA_SecureChannel_sendChunk(&ci, &message, messagePos);
     return UA_SecureChannel_sendChunk(&ci, &message, messagePos);
 }
 }
 
 
+/***************************/
+/* Process Received Chunks */
+/***************************/
+
+static void
+UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId) {
+    struct ChunkEntry *ch;
+    LIST_FOREACH(ch, &channel->chunks, pointers) {
+        if(ch->requestId == requestId) {
+            UA_ByteString_deleteMembers(&ch->bytes);
+            LIST_REMOVE(ch, pointers);
+            UA_free(ch);
+            return;
+        }
+    }
+}
+
 /* assume that chunklength fits */
 /* assume that chunklength fits */
-static void appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
-                        size_t offset, size_t chunklength) {
+static void
+appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
+            size_t offset, size_t chunklength) {
     UA_Byte* new_bytes = UA_realloc(ch->bytes.data, ch->bytes.length + chunklength);
     UA_Byte* new_bytes = UA_realloc(ch->bytes.data, ch->bytes.length + chunklength);
     if(!new_bytes) {
     if(!new_bytes) {
         UA_ByteString_deleteMembers(&ch->bytes);
         UA_ByteString_deleteMembers(&ch->bytes);
@@ -266,11 +288,14 @@ static void appendChunk(struct ChunkEntry *ch, const UA_ByteString *msg,
     ch->bytes.length += chunklength;
     ch->bytes.length += chunklength;
 }
 }
 
 
-void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                  const UA_ByteString *msg, size_t offset, size_t chunklength) {
+static void
+UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
+                             const UA_ByteString *msg, size_t offset,
+                             size_t chunklength) {
     /* Check if the chunk fits into the message */
     /* Check if the chunk fits into the message */
     if(msg->length - offset < chunklength) {
     if(msg->length - offset < chunklength) {
-        UA_SecureChannel_removeChunk(channel, requestId); /* can't process all chunks for that request */
+        /* can't process all chunks for that request */
+        UA_SecureChannel_removeChunk(channel, requestId);
         return;
         return;
     }
     }
 
 
@@ -294,11 +319,13 @@ void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId
     appendChunk(ch, msg, offset, chunklength);
     appendChunk(ch, msg, offset, chunklength);
 }
 }
 
 
-UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                             const UA_ByteString *msg, size_t offset, size_t chunklength,
-                                             UA_Boolean *deleteChunk) {
+static UA_ByteString
+UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
+                               const UA_ByteString *msg, size_t offset,
+                               size_t chunklength, UA_Boolean *deleteChunk) {
     if(msg->length - offset < chunklength) {
     if(msg->length - offset < chunklength) {
-        UA_SecureChannel_removeChunk(channel, requestId); /* can't process all chunks for that request */
+        /* can't process all chunks for that request */
+        UA_SecureChannel_removeChunk(channel, requestId);
         return UA_BYTESTRING_NULL;
         return UA_BYTESTRING_NULL;
     }
     }
 
 
@@ -323,27 +350,94 @@ UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt3
     return bytes;
     return bytes;
 }
 }
 
 
-void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId) {
-            UA_ByteString_deleteMembers(&ch->bytes);
-            LIST_REMOVE(ch, pointers);
-            UA_free(ch);
-            return;
-        }
-    }
-}
-
-UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel){
-/* Does the sequence number match? */
+static UA_StatusCode
+UA_SecureChannel_processSequenceNumber(UA_SecureChannel *channel, UA_UInt32 SequenceNumber) {
+    /* Does the sequence number match? */
     if(SequenceNumber != channel->receiveSequenceNumber + 1) {
     if(SequenceNumber != channel->receiveSequenceNumber + 1) {
-        if(channel->receiveSequenceNumber + 1 > 4294966271 && SequenceNumber < 1024) {
+        if(channel->receiveSequenceNumber + 1 > 4294966271 && SequenceNumber < 1024)
             channel->receiveSequenceNumber = SequenceNumber - 1; /* Roll over */
             channel->receiveSequenceNumber = SequenceNumber - 1; /* Roll over */
-        } else {
+        else
             return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
             return UA_STATUSCODE_BADSECURITYCHECKSFAILED;
-        }
     }
     }
     channel->receiveSequenceNumber++;
     channel->receiveSequenceNumber++;
     return UA_STATUSCODE_GOOD;
     return UA_STATUSCODE_GOOD;
 }
 }
+
+UA_StatusCode
+UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *chunks,
+                               UA_ProcessMessageCallback callback, void *application) {
+    size_t offset= 0;
+    do {
+        size_t initial_offset = offset;
+        
+        /* Decode header */
+        UA_SecureConversationMessageHeader header;
+        UA_StatusCode retval = UA_SecureConversationMessageHeader_decodeBinary(chunks, &offset, &header);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+
+        /* Is the channel attached to connection? */
+        if(header.secureChannelId != channel->securityToken.channelId) {
+            //Service_CloseSecureChannel(server, channel);
+            //connection->close(connection);
+            return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+        }
+
+        /* Use requestId = 0 with OPN as argument for the callback */
+        UA_SequenceHeader sequenceHeader;
+        UA_SequenceHeader_init(&sequenceHeader);
+
+        if((header.messageHeader.messageTypeAndChunkType & 0x00ffffff) != UA_MESSAGETYPE_OPN) {
+            /* Check the symmetric security header (not for OPN) */
+            UA_UInt32 tokenId = 0;
+            retval |= UA_UInt32_decodeBinary(chunks, &offset, &tokenId);
+            retval |= UA_SequenceHeader_decodeBinary(chunks, &offset, &sequenceHeader);
+            if(retval != UA_STATUSCODE_GOOD)
+                return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+
+            /* Does the token match? */
+            if(tokenId != channel->securityToken.tokenId) {
+                if(tokenId != channel->nextSecurityToken.tokenId)
+                    return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+                UA_SecureChannel_revolveTokens(channel);
+            }
+
+            /* Does the sequence number match? */
+            retval = UA_SecureChannel_processSequenceNumber(channel, sequenceHeader.sequenceNumber);
+            if(retval != UA_STATUSCODE_GOOD)
+                return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+        }
+
+        /* Process chunk */
+        size_t processed_header = offset - initial_offset;
+        switch(header.messageHeader.messageTypeAndChunkType & 0xff000000) {
+        case UA_CHUNKTYPE_INTERMEDIATE:
+            UA_SecureChannel_appendChunk(channel, sequenceHeader.requestId, chunks, offset,
+                                         header.messageHeader.messageSize - processed_header);
+            break;
+        case UA_CHUNKTYPE_FINAL: {
+            UA_Boolean deleteMessage = false;
+            UA_ByteString message =
+                UA_SecureChannel_finalizeChunk(channel, sequenceHeader.requestId, chunks, offset,
+                                               header.messageHeader.messageSize - processed_header,
+                                               &deleteMessage);
+            if(message.length > 0) {
+                callback(application, channel, header.messageHeader.messageTypeAndChunkType & 0x00ffffff,
+                         sequenceHeader.requestId, &message);
+                if(deleteMessage)
+                    UA_ByteString_deleteMembers(&message);
+            }
+            break; }
+        case UA_CHUNKTYPE_ABORT:
+            UA_SecureChannel_removeChunk(channel, sequenceHeader.requestId);
+            break;
+        default:
+            return UA_STATUSCODE_BADDECODINGERROR;
+        }
+
+        /* Jump to the end of the chunk */
+        offset += (header.messageHeader.messageSize - processed_header);
+    } while(chunks->length > offset);
+
+    return UA_STATUSCODE_GOOD;
+}

+ 7 - 12
src/ua_securechannel.h

@@ -64,20 +64,15 @@ void UA_SecureChannel_revolveTokens(UA_SecureChannel *channel);
 /**
 /**
  * Chunking
  * Chunking
  * -------- */
  * -------- */
-/* Offset is initially set to the beginning of the chunk content. chunklength is
-   the length of the decoded chunk content (minus header, padding, etc.) */
-void UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                  const UA_ByteString *msg, size_t offset, size_t chunklength);
+typedef void
+(UA_ProcessMessageCallback)(void *application, UA_SecureChannel *channel,
+                             UA_MessageType messageType, UA_UInt32 requestId,
+                             const UA_ByteString *message);
 
 
-/* deleteChunk indicates if the returned bytestring was copied off the network
-   buffer (and needs to be freed) or points into the msg */
-UA_ByteString UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
-                                             const UA_ByteString *msg, size_t offset, size_t chunklength,
-                                             UA_Boolean *deleteChunk);
+UA_StatusCode
+UA_SecureChannel_processChunks(UA_SecureChannel *channel, const UA_ByteString *chunks,
+                               UA_ProcessMessageCallback callback, void *application);
 
 
-void UA_SecureChannel_removeChunk(UA_SecureChannel *channel, UA_UInt32 requestId);
-
-UA_StatusCode UA_SecureChannel_processSequenceNumber (UA_UInt32 SequenceNumber, UA_SecureChannel *channel);
 /**
 /**
  * Log Helper
  * Log Helper
  * ---------- */
  * ---------- */

+ 0 - 20
src/ua_session.c

@@ -106,24 +106,4 @@ UA_UInt32 UA_Session_getUniqueSubscriptionID(UA_Session *session) {
     return ++(session->lastSubscriptionID);
     return ++(session->lastSubscriptionID);
 }
 }
 
 
-void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session) {
-    /* Are there remaining subscriptions? */
-    if(LIST_FIRST(&session->serverSubscriptions))
-        return;
-
-    /* Send a response for every queued request */
-    UA_PublishResponseEntry *pre;
-    while((pre = SIMPLEQ_FIRST(&session->responseQueue))) {
-        SIMPLEQ_REMOVE_HEAD(&session->responseQueue, listEntry);
-        UA_PublishResponse *response = &pre->response;
-        UA_UInt32 requestId = pre->requestId;
-        response->responseHeader.serviceResult = UA_STATUSCODE_BADNOSUBSCRIPTION;
-        response->responseHeader.timestamp = UA_DateTime_now();
-        UA_SecureChannel_sendBinaryMessage(session->channel, requestId, response,
-                                           &UA_TYPES[UA_TYPES_PUBLISHRESPONSE]);
-        UA_PublishResponse_deleteMembers(response);
-        UA_free(pre);
-    }
-}
-
 #endif
 #endif

+ 0 - 2
src/ua_session.h

@@ -69,8 +69,6 @@ UA_Session_deleteSubscription(UA_Server *server, UA_Session *session,
 
 
 UA_UInt32
 UA_UInt32
 UA_Session_getUniqueSubscriptionID(UA_Session *session);
 UA_Session_getUniqueSubscriptionID(UA_Session *session);
-
-void UA_Session_answerPublishRequestsWithoutSubscription(UA_Session *session);
 #endif
 #endif
 
 
 /**
 /**

+ 3 - 3
tests/check_services_subscriptions.c

@@ -78,15 +78,15 @@ START_TEST(Server_publishCallback) {
     /* Sleep until the publishing interval times out */
     /* Sleep until the publishing interval times out */
     usleep((useconds_t)(publishingInterval * 1000) + 1000);
     usleep((useconds_t)(publishingInterval * 1000) + 1000);
 
 
-
+    /* Keepalive is set to max initially */
     UA_Subscription *sub;
     UA_Subscription *sub;
     LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
     LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
-        ck_assert_uint_eq(sub->currentKeepAliveCount, 0);
+        ck_assert_uint_eq(sub->currentKeepAliveCount, sub->maxKeepAliveCount);
 
 
     UA_Server_run_iterate(server, false);
     UA_Server_run_iterate(server, false);
 
 
     LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
     LIST_FOREACH(sub, &adminSession.serverSubscriptions, listEntry)
-        ck_assert_uint_eq(sub->currentKeepAliveCount, 1);
+        ck_assert_uint_eq(sub->currentKeepAliveCount, sub->maxKeepAliveCount+1);
 
 
     /* Remove the subscriptions */
     /* Remove the subscriptions */
     UA_DeleteSubscriptionsRequest del_request;
     UA_DeleteSubscriptionsRequest del_request;

+ 2 - 3
tests/check_types_builtin.c

@@ -168,7 +168,7 @@ START_TEST(UA_UInt32_decodeShallNotRespectSign) {
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(pos, 8);
     ck_assert_int_eq(pos, 8);
     ck_assert_uint_eq(val_ff_ff, (UA_UInt32)( (0x01LL << 32 ) - 1 ));
     ck_assert_uint_eq(val_ff_ff, (UA_UInt32)( (0x01LL << 32 ) - 1 ));
-    ck_assert_uint_eq(val_00_80, (UA_UInt32)(0x01 << 31));
+    ck_assert_uint_eq(val_00_80, (UA_UInt32)(0x01) << 31);
 }
 }
 END_TEST
 END_TEST
 
 
@@ -192,8 +192,7 @@ END_TEST
 START_TEST(UA_Int64_decodeShallRespectSign) {
 START_TEST(UA_Int64_decodeShallRespectSign) {
     // given
     // given
     UA_ByteString rawMessage;
     UA_ByteString rawMessage;
-    UA_Int64 expectedVal = 0xFF;
-    expectedVal = expectedVal << 56;
+    UA_UInt64 expectedVal = (UA_UInt64)0xFF << 56;
     UA_Byte  mem[8]      = { 00, 00, 00, 00, 0x00, 0x00, 0x00, 0xFF };
     UA_Byte  mem[8]      = { 00, 00, 00, 00, 0x00, 0x00, 0x00, 0xFF };
     rawMessage.data   = mem;
     rawMessage.data   = mem;
     rawMessage.length = 8;
     rawMessage.length = 8;

+ 2 - 1
tools/pyUANamespace/open62541_XMLPreprocessor.py

@@ -22,6 +22,7 @@ import xml.dom.minidom as dom
 import os
 import os
 import string
 import string
 from collections import Counter
 from collections import Counter
+import re
 
 
 from ua_namespace import opcua_node_id_t
 from ua_namespace import opcua_node_id_t
 
 
@@ -118,7 +119,7 @@ class preProcessDocument:
       if "xmlns:" in key:  # Any key: we will be removing these qualifiers from Values later
       if "xmlns:" in key:  # Any key: we will be removing these qualifiers from Values later
         self.namespaceQualifiers.append(key.replace("xmlns:",""))
         self.namespaceQualifiers.append(key.replace("xmlns:",""))
       if "xmlns:s" in key: # get a numeric nsId and modelname/uri
       if "xmlns:s" in key: # get a numeric nsId and modelname/uri
-        self.namespaceOrder.append((int(key.replace("xmlns:s","")), ns[0].getAttribute(key)))
+        self.namespaceOrder.append((int(key.replace("xmlns:s","")), re.sub("[A-Za-z0-9-_\.]+\.[xXsSdD]{3}$","",ns[0].getAttribute(key))))
 
 
     # Get all nodeIds contained in this XML
     # Get all nodeIds contained in this XML
     for nd in ns[0].childNodes:
     for nd in ns[0].childNodes:

+ 2 - 2
tools/pyUANamespace/ua_node_types.py

@@ -928,8 +928,8 @@ class opcua_node_variable_t(opcua_node_t):
     self.__dataType__            = None
     self.__dataType__            = None
     self.__valueRank__           = -1
     self.__valueRank__           = -1
     self.__arrayDimensions__     = []
     self.__arrayDimensions__     = []
-    self.__accessLevel__         = 0
-    self.__userAccessLevel__     = 0
+    self.__accessLevel__         = 3
+    self.__userAccessLevel__     = 3
     self.__minimumSamplingInterval__ = 0.0
     self.__minimumSamplingInterval__ = 0.0
     self.__historizing__         = False
     self.__historizing__         = False
     self.__xmlValueDef__         = None
     self.__xmlValueDef__         = None

+ 19 - 0
tools/travis/travis_linux_after_success.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+set -ev
+
+if [ $ANALYZE = "true" ]; then
+    echo "=== Skipping after_success scripts in ANALYZE mode ==="
+else
+    echo "=== Executing after_success scripts ==="
+    if [ "$CC" = "gcc" ] && [ "${TRAVIS_REPO_SLUG}" = "open62541/open62541" ]; then
+        if [ ${TRAVIS_BRANCH} = "0.2" ]; then
+            sh ./tools/travis/travis_push_doc.sh
+        fi
+        if [ ${TRAVIS_BRANCH} = "0.2" ]; then
+            sh ./tools/travis/travis_push_coverity.sh
+        fi
+        sh ./tools/travis/travis_push_release.sh;
+    else
+        echo "  Skipping push scripts since not gcc and/or ${TRAVIS_REPO_SLUG} is not the main repo"
+    fi
+fi

+ 2 - 14
tools/travis/travis_push_coverity.sh

@@ -1,24 +1,12 @@
 #!/bin/bash
 #!/bin/bash
+set -ev
 
 
 # This script is run by travis-ci and pushes the first commit
 # This script is run by travis-ci and pushes the first commit
 # of the day to the coverity scan service
 # of the day to the coverity scan service
 
 
-git fetch origin coverity_scan
 COMMITS=`git log --oneline --since=today.midnight | wc -l`
 COMMITS=`git log --oneline --since=today.midnight | wc -l`
 if [ "$COMMITS" -le 1 ]; then
 if [ "$COMMITS" -le 1 ]; then
-    #first commit a day - push changes to branch coverity_scan
-    git clone -b coverity_scan https://$GITAUTH@github.com/acplt/open62541
-    cd open62541
-    git fetch origin
-    git merge origin/master
-    git config --global user.email "open62541-travis-ci@users.noreply.github.com"
-    git config --global user.name "Open62541 travis-ci"
-    git config --global push.default simple
-    git add *
-    git commit -am "push to coverity scan by travis-ci"
-    git push https://$GITAUTH@github.com/acplt/open62541
-    cd ..
-    rm -rf open62541
+    git push https://$GITAUTH@github.com/open62541/open62541 0.2:coverity_scan
 else
 else
     echo "Not the first commit of the day - no push to coverity required"
     echo "Not the first commit of the day - no push to coverity required"
 fi
 fi

+ 1 - 0
tools/travis/travis_push_doc.sh

@@ -1,4 +1,5 @@
 #!/bin/bash
 #!/bin/bash
+set -ev
 
 
 git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-www
 git clone --depth=5 -b gh-pages https://$GITAUTH@github.com/open62541/open62541-www
 cd open62541-www
 cd open62541-www

+ 2 - 1
tools/travis/travis_push_release.sh

@@ -1,4 +1,5 @@
 #!/bin/bash
 #!/bin/bash
+set -ev
 
 
 TAGSTOSAVE=50
 TAGSTOSAVE=50
 TAG="$(git rev-parse --short=10 HEAD)"
 TAG="$(git rev-parse --short=10 HEAD)"
@@ -33,7 +34,7 @@ if [ ! -e "$TAG.zip" ]; then
 
 
     LINETOSTART=$((TAGSTOSAVE+1))
     LINETOSTART=$((TAGSTOSAVE+1))
     #remove obsolete zips
     #remove obsolete zips
-    tail -n +"$LINETOSTART" raw.txt | xargs git rm
+    tail -n +"$LINETOSTART" raw.txt | xargs git rm --ignore-unmatch
 
 
     #remove obsolete zips from list
     #remove obsolete zips from list
     head "-$TAGSTOSAVE" raw.txt > temp && mv temp raw.txt
     head "-$TAGSTOSAVE" raw.txt > temp && mv temp raw.txt